From 0c0300e277164fe3539482b5ff9cb78d0ab6e232 Mon Sep 17 00:00:00 2001 From: youzhi92 Date: Tue, 2 Sep 2025 09:53:54 +0800 Subject: [PATCH] sync OpenHarmony_feature_release_20250728 branch to master Signed-off-by: youzhi92 Change-Id: I5e0c83162fab150cbee1ba1a1ef42e9d372a88b4 --- arkui-plugins/BUILD.gn | 21 +- .../collectors/memo-collectors/factory.ts | 218 + .../memo-collectors/function-collector.ts | 226 + .../memo-collectors/memo-visitor.ts | 26 + .../collectors/memo-collectors/utils.ts | 913 +++ arkui-plugins/common/arkts-utils.ts | 109 +- arkui-plugins/common/debug.ts | 5 +- arkui-plugins/common/declaration-collector.ts | 86 + arkui-plugins/common/import-collector.ts | 105 + arkui-plugins/common/log-collector.ts | 75 + arkui-plugins/common/plugin-context.ts | 45 +- arkui-plugins/common/predefines.ts | 410 +- arkui-plugins/common/program-visitor.ts | 198 +- arkui-plugins/common/safe-types.ts | 45 + .../interop-plugins/decl_transformer.ts | 4 +- arkui-plugins/interop-plugins/index.ts | 1 - arkui-plugins/jest-test.config.js | 5 +- .../memo-plugins/function-transformer.ts | 34 +- arkui-plugins/memo-plugins/index.ts | 38 +- .../memo-plugins/internal-transformer.ts | 2 +- .../memo-plugins/memo-cache-factory.ts | 430 ++ arkui-plugins/memo-plugins/memo-factory.ts | 41 +- .../memo-plugins/parameter-transformer.ts | 5 +- .../memo-plugins/signature-transformer.ts | 3 +- arkui-plugins/memo-plugins/utils.ts | 89 +- arkui-plugins/npm_preinstall.sh | 30 - arkui-plugins/package-lock.json | 5590 ----------------- arkui-plugins/package.json | 4 +- .../test/demo/interop/builder_interop.ets | 114 + .../test/demo/interop/link_link_interop.ets | 118 + .../demo/interop/provide_consume_interop.ets | 106 + ...ink-example.ets => state_link_interop.ets} | 6 +- .../test/demo/interop/state_prop_interop.ets | 93 + .../demo/localtest/build_config_template.json | 66 +- .../entry/{ => src/main/ets/pages}/index.ets | 0 .../entry/{ => src/main/ets/pages}/new.ets | 74 +- .../localtest/entry/src/main/module.json5 | 37 + .../resources/base/profile/main_pages.json | 6 + .../animation/animatable-extend-basic.ets | 32 + .../condition-scope/block-in-switch-case.ets | 66 + .../condition-scope/else-if-in-content.ets | 38 + .../if-break-in-nested-content.ets | 26 + .../condition-scope/if-else-in-content.ets | 40 + .../if-in-switch-in-content.ets | 41 + .../non-builder-within-builder.ets} | 34 +- .../switch-case-in-content.ets | 41 + .../switch-in-if-in-content.ets | 32 + .../condition-scope/with-builder.ets | 76 + .../mock/builder-lambda/simple-component.ets | 4 +- .../builder-lambda/style-with-receiver.ets | 6 +- .../demo/mock/common-utils/annotation.ets | 32 + .../mock/component/declare-component.ets} | 18 +- .../test/demo/mock/component/for-each.ets | 55 + .../builder-param/optional-builder-param.ets | 52 + .../computed/computed-in-observedv2-class.ets | 28 + .../computed/computed-in-struct.ets | 46 + .../computed/computed-no-return-type.ets | 55 + .../decorators/computed/static-computed.ets | 65 + .../custom-dialog/base-custom-dialog.ets | 98 + .../custom-dialog/builder-dialog-options.ets | 44 + .../custom-dialog/controller-in-build.ets | 46 + .../custom-dialog/controller-in-method.ets | 47 + .../custom-dialog/declare-custom-dialog.ets | 44 + .../extends-dialog-controller.ets | 64 + .../mock/decorators/decorator-no-type.ets | 98 + .../decorators/event/event-initialize.ets | 52 + .../decorators/local/local-basic-type.ets | 29 + .../decorators/local/local-complex-type.ets | 48 + .../mock/decorators/local/static-local.ets | 28 + .../localstoragelink-complex-type.ets | 43 + .../localstoragelink-primitive-type.ets | 27 + .../localstorageprop-ref-complex-type.ets | 41 + .../localstorageprop-ref-primitive-type.ets | 28 + .../monitor/enum-monitor-params.ets} | 50 +- .../monitor/monitor-before-state-variable.ets | 45 + .../monitor/monitor-in-observedv2-class.ets | 67 + .../decorators/monitor/monitor-in-struct.ets | 40 + .../decorators/monitor/monitor-params.ets} | 103 +- .../objectlink/objectlink-basic.ets | 28 +- .../objectlink/objectlink-observed.ets | 21 +- .../observed-track/observed-jsonrename.ets | 37 + .../observed-jsonstringifyignore.ets | 37 + .../observed-track/observed-only.ets | 15 + .../observed-track-class-property.ets | 15 + .../observed-track-complex-type.ets | 15 + .../observed-track/observed-track-extends.ets | 15 + .../observed-track-implements.ets | 15 + .../observed-track/observed-track.ets | 20 + .../decorators/observed-track/track-only.ets | 15 + .../base-observedv2-trace.ets | 33 + .../observedv2-serialization.ets | 43 + .../observedv2-trace-extends.ets | 32 + .../observedv2-trace-implements.ets | 34 + .../observedv2-trace-types.ets | 51 + .../static-observedv2-trace.ets | 23 + .../trace-with-constructor.ets | 26 + .../mock/decorators/once/once-basic-type.ets | 29 + .../decorators/once/once-complex-type.ets | 48 + .../demo/mock/decorators/once/once-only.ets | 25 + .../decorators/once/once-with-require.ets | 56 + .../decorators/param/param-basic-type.ets | 29 + .../decorators/param/param-complex-type.ets | 48 + .../decorators/param/param-with-require.ets | 75 + .../prop-ref/prop-ref-basic-type.ets | 29 + .../prop-ref/prop-ref-complex-type.ets | 49 + .../prop-ref-without-initialization.ets | 30 + .../decorators/prop-ref/state-to-prop-ref.ets | 54 + .../consume-basic-type.ets | 29 + .../consume-complex-type.ets | 50 + .../provide-to-consume.ets | 44 + .../consumer-basic-type.ets | 29 + .../consumer-complex-type.ets} | 62 +- .../provider-basic-type.ets | 29 + .../provider-complex-type.ets | 47 + .../provider-to-consumer.ets | 72 + .../mock/decorators/require/basic-require.ets | 40 + .../decorators/resource/resource-in-build.ets | 32 +- .../resource/resource-in-property.ets | 2 +- .../decorators/reusable/reusable-basic.ets | 19 +- .../decorators/reusable/reusable-complex.ets | 15 + .../storagelink/storagelink-appstorage.ets | 16 +- .../storagelink/storagelink-complex-type.ets | 16 +- .../storagelink-primitive-type.ets | 15 + .../storageprop-ref-complex-type.ets | 41 + .../storageprop-ref-primitive-type.ets | 28 + .../storageprop/storageprop-appstorage.ets | 15 + .../storageprop/storageprop-complex-type.ets | 16 +- .../storageprop-primitive-type.ets | 15 + .../mock/decorators/watch/watch-basic.ets | 33 +- .../double-dollar/double-dollar-griditem.ets | 37 + .../double-dollar/double-dollar-toggle.ets} | 50 +- .../mock/entry/entry-only.ets} | 9 +- .../storage-use-shared-storage-false.ets | 25 + .../storage-use-shared-storage-true.ets | 25 + .../demo/mock/entry/localstorage/storage.ets | 26 + .../localstorage/use-shared-storage-false.ets | 25 + .../localstorage/use-shared-storage-true.ets | 25 + .../route-name/route-name-storage-shared.ets | 25 + .../mock/entry/route-name/route-name.ets} | 11 +- .../test/demo/mock/imports/import-struct.ets | 28 + .../test/demo/mock/imports/kit-import.ets | 34 + .../demo/mock/imports/utils/simple-struct.ets | 23 + .../mock/memo/functions/argument-call.ets | 2 +- .../functions/complex-memo-intrinsic.ets} | 85 +- .../mock/memo/functions/declare-and-call.ets | 21 +- .../mock/memo/functions/inner-functions.ets | 2 +- .../mock/memo/functions/internal-memo-arg.ets | 101 + .../memo/functions/non-void-return-type.ets | 2 +- .../mock/memo/functions/type-reference.ets | 2 +- .../mock/memo/functions/void-return-type.ets | 2 +- .../demo/mock/memo/lambdas/argument-call.ets | 2 +- .../memo/lambdas/function-with-receiver.ets | 2 +- .../mock/memo/lambdas/trailing-lambdas.ets | 2 +- .../demo/mock/memo/lambdas/void-lambda.ets | 2 +- .../demo/mock/memo/lambdas/with-receiver.ets | 13 +- .../demo/mock/memo/methods/argument-call.ets | 2 +- .../test/demo/mock/memo/methods/callable.ets | 2 +- .../mock/memo/methods/declare-and-call.ets | 2 +- .../demo/mock/memo/methods/internal-calls.ets | 2 +- .../mock/memo/methods/non-void-method.ets | 9 +- .../demo/mock/memo/methods/void-method.ets | 2 +- .../memo/properties/class-constructor.ets | 2 +- .../mock/memo/properties/class-properties.ets | 2 +- .../demo/mock/memo/properties/implements.ets | 2 +- .../demo/mock/memo/properties/interfaces.ets | 2 +- .../test/demo/mock/resource/ResourceTable.txt | 13 + .../test/demo/mock/resource/rawfile/mock.txt | 1 + .../mock/wrap-builder/builder-in-generic.ets} | 42 +- .../mock/wrap-builder/init-with-builder.ets | 33 + .../wrap-builder/wrap-builder-in-generic.ets | 50 + .../mock/wrap-builder/wrap-builder-in-ui.ets | 44 + .../wrap-builder/wrap-builder-with-lambda.ets | 45 + .../demo/mock/xcomponent/xcomponent-basic.ets | 19 - .../local/@ohos.arkui.component.text.d.ets | 132 - .../@ohos.arkui.stateManagement.runtime.d.ets | 62 - .../@ohos.arkui.stateManagement.storage.d.ets | 119 - arkui-plugins/test/localtest_config.js | 82 +- arkui-plugins/test/package.json | 9 +- arkui-plugins/test/test.log | 372 -- .../test/ut/common/annotation.test.ts | 206 +- .../argument-call.test.ts | 20 +- .../complex-memo-intrinsic.test.ts | 155 + .../declare-and-call.test.ts | 59 +- .../inner-functions.test.ts | 26 +- .../internal-memo-arg.test.ts | 186 + .../non-void-return-type.test.ts | 26 +- .../type-reference.test.ts | 33 +- .../void-return-type.test.ts | 14 +- .../lambda-literals/argument-call.test.ts | 44 +- .../function-with-receiver.test.ts | 22 +- .../lambda-literals/trailing-lambdas.test.ts | 78 +- .../lambda-literals/void-lambda.test.ts | 12 +- .../lambda-literals/with-receiver.test.ts | 78 +- .../method-declarations/argument-call.test.ts | 24 +- .../method-declarations/callable.test.ts | 26 +- .../declare-and-call.test.ts | 18 +- .../internal-calls.test.ts | 42 +- .../non-void-method.test.ts | 51 +- .../method-declarations/void-method.test.ts | 26 +- .../class-constructor.test.ts | 24 +- .../class-properties.test.ts | 22 +- .../property-declarations/interfaces.test.ts | 30 +- .../animatable-extend-basic.test.ts} | 117 +- .../animation/animation-basic.test.ts | 50 +- .../block-in-switch-case.test.ts | 505 ++ .../else-if-in-content.test.ts | 278 + .../if-break-in-nested-content.test.ts | 228 + .../if-else-in-content.test.ts | 261 + .../if-in-switch-in-content.test.ts | 299 + .../non-builder-within-builder.test.ts | 82 + .../switch-case-in-content.test.ts | 244 + .../switch-in-if-in-content.test.ts | 207 + .../condition-scope/with-builder.test.ts | 498 ++ .../custom-component-call.test.ts | 140 +- .../builder-lambda/simple-component.test.ts | 26 +- .../style-with-receiver.test.ts | 50 +- .../component/declare-component.test.ts | 144 + .../ut/ui-plugins/component/for-each.test.ts | 156 + .../builder-param-passing.test.ts | 236 +- .../init-with-local-builder.test.ts | 150 +- .../optional-builder-param.test.ts | 372 ++ .../decorators/builder/global-builder.test.ts | 43 +- .../decorators/builder/local-builder.test.ts | 47 +- .../computed-in-observedv2-class.test.ts | 141 + .../computed/computed-in-struct.test.ts | 165 + .../computed/computed-no-return-type.test.ts | 241 + .../computed/static-computed.test.ts | 277 + .../custom-dialog/base-custom-dialog.test.ts | 286 + .../builder-dialog-options.test.ts | 206 + .../custom-dialog/controller-in-build.test.ts | 191 + .../controller-in-method.test.ts | 175 + .../declare-custom-dialog.test.ts | 180 + .../extends-dialog-controller.test.ts | 183 + .../decorators/decorator-no-type.test.ts | 872 +++ .../decorators/event/event-initialize.test.ts | 281 + .../decorators/link/link-basic-type.test.ts | 130 +- .../decorators/link/link-complex-type.test.ts | 273 +- .../link/link-to-link-prop-state.test.ts | 156 +- .../decorators/link/state-to-link.test.ts | 129 +- .../decorators/local/local-basic-type.test.ts | 170 + .../local/local-complex-type.test.ts | 349 + .../decorators/local/static-local.test.ts | 140 + .../localstoragelink-complex-type.test.ts | 306 + .../localstoragelink-primitive-type.test.ts | 162 + .../localstorageprop-ref-complex-type.test.ts | 279 + ...ocalstorageprop-ref-primitive-type.test.ts | 170 + .../monitor/enum-monitor-params.test.ts | 294 + .../monitor-before-state-variable.test.ts | 189 + .../monitor-in-observedv2-class.test.ts | 277 + .../monitor/monitor-in-struct.test.ts | 176 + .../decorators/monitor/monitor-params.test.ts | 357 ++ .../objectlink/objectlink-basic.test.ts | 253 + .../objectlink/objectlink-observed.test.ts | 310 + .../observed-jsonrename.test.ts | 206 + .../observed-jsonstringifyignore.test.ts | 206 + .../observed-track/observed-only.test.ts | 77 +- .../observed-track-class-property.test.ts | 125 +- .../observed-track-complex-type.test.ts | 634 +- .../observed-track-extends.test.ts | 105 +- .../observed-track-implements.test.ts | 81 +- .../observed-track/observed-track.test.ts | 90 +- .../observed-track/track-only.test.ts | 67 +- .../base-observedv2-trace.test.ts | 182 + .../observed-serialization.test.ts | 277 + .../observedv2-trace-extends.test.ts | 166 + .../observedv2-trace-implements.test.ts | 159 + .../observedv2-trace-types.test.ts | 380 ++ .../static-observedv2-trace.test.ts | 123 + .../trace-with-constructor.test.ts | 136 + .../decorators/once/once-basic-type.test.ts | 177 + .../decorators/once/once-complex-type.test.ts | 378 ++ .../decorators/once/once-only.test.ts | 116 + .../decorators/once/once-with-require.test.ts | 327 + .../decorators/param/param-basic-type.test.ts | 176 + .../param/param-complex-type.test.ts | 381 ++ .../param/param-with-require.test.ts | 347 + .../prop-ref/prop-ref-basic-type.test.ts | 198 + .../prop-ref/prop-ref-complex-type.test.ts | 427 ++ .../prop-ref-without-initialization.test.ts | 285 + .../prop-ref/state-to-propref.test.ts | 207 + .../decorators/prop/prop-basic-type.test.ts | 124 +- .../decorators/prop/prop-complex-type.test.ts | 273 +- .../decorators/prop/state-to-prop.test.ts | 134 +- .../consume-basic-type.test.ts | 216 + .../consume-complex-type.test.ts | 476 ++ .../provide-annotation-usage.test.ts | 205 +- .../provide-basic-type.test.ts | 122 +- .../provide-complex-type.test.ts | 269 +- .../provide-to-consume.test.ts | 250 + .../consumer-basic-type.test.ts | 172 + .../consumer-complex-type.test.ts | 351 ++ .../provider-basic-type.test.ts | 172 + .../provider-complex-type.test.ts | 351 ++ .../provider-to-consumer.test.ts | 261 + .../decorators/require/basic-require.test.ts | 354 ++ .../resource/resource-in-build.test.ts | 121 +- .../resource/resource-in-property.test.ts | 49 +- .../reusable/reusable-basic.test.ts | 85 +- .../reusable/reusable-complex.test.ts | 120 +- .../decorators/state/state-basic-type.test.ts | 131 +- .../state/state-complex-type.test.ts | 274 +- .../decorators/state/state-to-state.test.ts | 104 +- .../storagelink-appstorage.test.ts | 80 +- .../storagelink-complex-type.test.ts | 145 +- .../storagelink-primitive-type.test.ts | 82 +- .../storageprop-ref-complex-type.test.ts | 281 + .../storageprop-ref-primitive-type.test.ts | 172 + .../storageprop-appstorage.test.ts | 71 +- .../storageprop-complex-type.test.ts | 144 +- .../storageprop-primitive-type.test.ts | 78 +- .../decorators/watch/watch-basic.test.ts | 249 +- .../double-dollar-griditem.test.ts | 163 + .../double-dollar-toggle.test.ts | 164 + .../ut/ui-plugins/entry/entry-only.test.ts | 95 + .../storage-use-shared-storage-false.test.ts | 99 + .../storage-use-shared-storage-true.test.ts | 99 + .../entry/localstorage/storage.test.ts | 100 + .../use-shared-storage-false.test.ts | 100 + .../use-shared-storage-true.test.ts | 100 + .../route-name-storage-shared.test.ts | 101 + .../entry/route-name/route-name.test.ts | 96 + .../ui-plugins/imports/import-struct.test.ts | 130 + .../ut/ui-plugins/imports/kit-import.test.ts | 239 + .../wrap-builder/builder-in-generic.test.ts | 303 + .../wrap-builder/init-with-builder.test.ts | 183 + .../wrap-builder-in-generic.test.ts | 311 + .../wrap-builder/wrap-builder-in-ui.test.ts | 255 + .../wrap-builder-with-lambda.test.ts | 390 ++ arkui-plugins/test/utils/artkts-config.ts | 242 +- arkui-plugins/test/utils/cache.ts | 47 +- arkui-plugins/test/utils/compile.ts | 297 +- arkui-plugins/test/utils/global.ts | 94 +- arkui-plugins/test/utils/hash-generator.ts | 48 + arkui-plugins/test/utils/path-config.ts | 19 +- arkui-plugins/test/utils/plugin-driver.ts | 39 +- arkui-plugins/test/utils/plugin-tester.ts | 256 +- .../utils/plugins/before-memo-no-recheck.ts | 47 + .../plugins/builder-lambda-no-recheck.ts | 5 +- arkui-plugins/test/utils/plugins/index.ts | 1 + .../test/utils/plugins/memo-no-recheck.ts | 18 +- arkui-plugins/test/utils/plugins/recheck.ts | 2 +- .../test/utils/plugins/struct-no-recheck.ts | 3 +- .../test/utils/plugins/struct-to-component.ts | 6 +- .../test/utils/plugins/ui-no-recheck.ts | 3 +- arkui-plugins/test/utils/processor-builder.ts | 38 + .../test/utils/processors/base-processor.ts | 69 + .../test/utils/processors/main-processor.ts | 96 + .../test/utils/processors/task-processor.ts | 517 ++ arkui-plugins/test/utils/safe-types.ts | 10 +- arkui-plugins/test/utils/serializable.ts | 58 + arkui-plugins/test/utils/shared-types.ts | 143 + arkui-plugins/tsconfig.build.json | 1 + arkui-plugins/tsconfig.json | 8 +- .../builder-factory.ts | 122 + .../builder-lambda-transformer.ts | 13 + .../cache/conditionBreakCache.ts | 91 + .../condition-scope-visitor.ts | 92 + .../builder-lambda-translators/factory.ts | 557 +- .../builder-lambda-translators/utils.ts | 202 +- .../ui-plugins/checked-transformer.ts | 559 +- .../ui-plugins/component-transformer.ts | 516 +- .../ui-plugins/entry-translators/factory.ts | 212 +- .../ui-plugins/entry-translators/utils.ts | 55 +- arkui-plugins/ui-plugins/index.ts | 48 +- arkui-plugins/ui-plugins/initstatevar.ts | 290 - .../ui-plugins/interop/builder-interop.ts | 538 ++ .../ui-plugins/interop/initstatevar.ts | 248 + .../ui-plugins/{ => interop}/interop.ts | 653 +- .../{ => interop}/legacy-transformer.ts | 198 +- .../ui-plugins/interop/predefines.ts | 75 + arkui-plugins/ui-plugins/interop/utils.ts | 223 + .../ui-plugins/preprocessor-transform.ts | 287 - .../ui-plugins/property-translators/base.ts | 120 +- .../property-translators/builderParam.ts | 126 +- .../cache/monitorCache.ts | 62 + .../cache/propertyCache.ts | 73 + .../property-translators/computed.ts | 88 + .../property-translators/consume.ts | 91 +- .../property-translators/consumer.ts | 137 + .../property-translators/factory.ts | 741 ++- .../ui-plugins/property-translators/index.ts | 297 +- .../ui-plugins/property-translators/link.ts | 98 +- .../ui-plugins/property-translators/local.ts | 174 + .../localStoragePropRef.ts | 157 + .../property-translators/localstoragelink.ts | 179 +- .../property-translators/localstorageprop.ts | 136 +- .../property-translators/monitor.ts | 63 + .../property-translators/objectlink.ts | 143 +- .../property-translators/observedTrack.ts | 340 +- .../property-translators/observedV2Trace.ts | 274 + .../ui-plugins/property-translators/once.ts | 140 + .../ui-plugins/property-translators/param.ts | 147 + .../ui-plugins/property-translators/prop.ts | 153 +- .../property-translators/propRef.ts | 168 + .../property-translators/provide.ts | 92 +- .../property-translators/provider.ts | 137 + .../property-translators/regularProperty.ts | 71 +- .../return-transformer.ts | 42 + .../ui-plugins/property-translators/state.ts | 112 +- .../property-translators/staticProperty.ts | 10 +- .../property-translators/storageProp.ts | 151 +- .../property-translators/storagePropRef.ts | 158 + .../property-translators/storagelink.ts | 147 +- .../ui-plugins/property-translators/types.ts | 2 +- .../ui-plugins/property-translators/utils.ts | 301 +- .../ui-plugins/struct-translators/factory.ts | 1240 +++- .../struct-translators/struct-transformer.ts | 217 +- .../ui-plugins/struct-translators/utils.ts | 598 +- arkui-plugins/ui-plugins/ui-factory.ts | 380 +- arkui-plugins/ui-plugins/utils.ts | 402 +- arkui-plugins/ui-syntax-plugins/index.ts | 119 +- .../ui-syntax-plugins/processor/index.ts | 210 +- .../rules/attribute-no-invoke.ts | 61 + .../rules/build-root-node.ts | 184 +- .../rules/builderparam-decorator-check.ts | 138 + .../check-construct-private-parameter.ts | 141 +- .../rules/check-decorated-property-type.ts | 108 +- .../rules/check-property-modifiers.ts | 134 + .../rules/component-componentV2-init-check.ts | 91 + .../component-componentV2-mix-use-check.ts | 288 +- .../rules/componentV2-mix-check.ts | 76 +- .../componentV2-state-usage-validation.ts | 459 +- .../rules/computed-decorator-check.ts | 312 + .../rules/construct-parameter-literal.ts | 199 +- .../rules/construct-parameter.ts | 316 + .../consumer-provider-decorator-check.ts | 550 +- .../rules/custom-dialog-missing-controller.ts | 118 +- .../rules/decorators-in-ui-component-only.ts | 102 - .../rules/entry-localstorage-check.ts | 75 +- .../rules/entry-struct-no-export.ts | 43 +- .../ui-syntax-plugins/rules/index.ts | 190 +- .../rules/main-pages-entry-check.ts | 65 + .../rules/monitor-decorator-check.ts | 335 +- .../rules/nested-relationship.ts | 333 +- .../rules/nested-reuse-component-check.ts | 201 + .../rules/no-child-in-button.ts | 114 +- .../rules/no-duplicate-decorators.ts | 104 - .../rules/no-duplicate-entry.ts | 117 +- .../rules/no-duplicate-id.ts | 97 + .../rules/no-duplicate-preview.ts | 110 +- .../rules/no-duplicate-state-manager.ts | 74 - .../rules/no-prop-link-objectlink-in-entry.ts | 115 +- .../rules/no-same-as-built-in-attribute.ts | 56 +- .../observed-heritage-compatible-check.ts | 180 +- .../rules/observed-observedV2-check.ts | 50 +- .../observedV2-trace-usage-validation.ts | 326 +- .../rules/old-new-decorator-mix-use-check.ts | 199 +- .../rules/once-decorator-check.ts | 271 +- .../rules/one-decorator-on-function-method.ts | 143 +- .../ui-syntax-plugins/rules/property-type.ts | 411 ++ .../rules/require-decorator-regular.ts | 69 + .../rules/reusable-component-in-V2-check.ts | 80 + .../rules/reusableV2-decorator-check.ts | 82 + .../rules/reuse-attribute-check.ts | 172 +- .../rules/specific-component-children.ts | 108 + .../rules/static-param-require.ts | 110 + .../rules/struct-missing-decorator.ts | 75 +- ...-builder-check.ts => struct-no-extends.ts} | 65 +- .../rules/struct-property-decorator.ts | 131 +- .../rules/struct-property-optional.ts | 80 + .../rules/struct-variable-initialization.ts | 128 +- .../rules/track-decorator-check.ts | 225 +- .../rules/type-decorator-check.ts | 167 - .../rules/ui-consistent-check.ts | 275 + .../ui-syntax-plugins/rules/ui-syntax-rule.ts | 73 +- .../rules/validate-build-in-struct.ts | 235 +- .../rules/validate-decorator-target.ts | 123 + ...iable-initialization-via-component-cons.ts | 210 - ...nitialization-via-component-constructor.ts | 206 + .../rules/watch-decorator-function.ts | 268 +- .../rules/watch-decorator-regular.ts | 71 +- .../rules/wrap-builder-check.ts | 141 +- ...mer.ts => ui-syntax-linter-transformer.ts} | 14 +- .../transformers/ui-syntax-linter-visitor.ts | 22 +- .../ui-syntax-plugins/utils/index.ts | 643 +- compiler/package-lock.json | 131 +- .../koalaui/interop/src/cpp/crashdump.h | 4 +- .../interop/src/cpp/ets/convertors-ets.cc | 2 +- koala-wrapper/native/BUILD.gn | 9 +- koala-wrapper/native/include/common.h | 14 +- koala-wrapper/native/include/memoryTracker.h | 52 + koala-wrapper/native/src/bridges.cc | 304 +- koala-wrapper/native/src/common.cc | 119 +- koala-wrapper/native/src/generated/bridges.cc | 77 +- koala-wrapper/native/src/memoryTracker.cc | 178 + koala-wrapper/package-lock.json | 1792 ------ koala-wrapper/src/Es2pandaEnums.ts | 12 +- koala-wrapper/src/Es2pandaNativeModule.ts | 135 +- koala-wrapper/src/arkts-api/class-by-peer.ts | 5 +- .../src/arkts-api/factory/nodeFactory.ts | 568 +- .../src/arkts-api/factory/nodeTests.ts | 32 +- koala-wrapper/src/arkts-api/index.ts | 16 + .../node-utilities/AnnotationDeclaration.ts | 32 + .../node-utilities/ArrowFunctionExpression.ts | 7 +- .../node-utilities/BreakStatement.ts | 27 + .../node-utilities/CallExpression.ts | 7 +- .../node-utilities/ClassDefinition.ts | 19 +- .../arkts-api/node-utilities/ClassProperty.ts | 7 +- .../node-utilities/ClassStaticBlock.ts | 28 + .../node-utilities/ETSFunctionType.ts | 7 +- .../node-utilities/ETSParameterExpression.ts | 9 +- .../arkts-api/node-utilities/ETSUnionType.ts | 7 +- .../node-utilities/FunctionExpression.ts | 11 +- .../arkts-api/node-utilities/Identifier.ts | 7 +- .../node-utilities/MethodDefinition.ts | 7 +- .../src/arkts-api/node-utilities/Property.ts | 8 +- .../node-utilities/ReturnStatement.ts | 7 +- .../node-utilities/ScriptFunction.ts | 7 +- .../arkts-api/node-utilities/SpreadElement.ts | 34 + .../node-utilities/SwitchCaseStatement.ts | 31 + .../node-utilities/SwitchStatement.ts | 31 + .../node-utilities/TSClassImplements.ts | 31 + .../node-utilities/TSInterfaceDeclaration.ts | 6 +- .../node-utilities/TSTypeAliasDeclaration.ts | 7 +- .../node-utilities/VariableDeclarator.ts | 7 +- koala-wrapper/src/arkts-api/peers/AstNode.ts | 127 +- koala-wrapper/src/arkts-api/peers/Context.ts | 34 +- .../src/arkts-api/peers/Diagnostic.ts | 38 + .../src/arkts-api/peers/DiagnosticInfo.ts | 34 + .../src/arkts-api/peers/DiagnosticKind.ts | 36 + .../src/arkts-api/peers/ImportPathManager.ts | 4 - koala-wrapper/src/arkts-api/peers/Program.ts | 56 +- .../src/arkts-api/peers/SourcePosition.ts | 4 + .../src/arkts-api/peers/SuggestionInfo.ts | 35 + koala-wrapper/src/arkts-api/static/global.ts | 4 +- koala-wrapper/src/arkts-api/types.ts | 17 +- .../src/arkts-api/utilities/nodeCache.ts | 105 + .../src/arkts-api/utilities/performance.ts | 199 +- .../src/arkts-api/utilities/private.ts | 4 - .../src/arkts-api/utilities/public.ts | 96 +- koala-wrapper/src/arkts-api/visitor.ts | 100 +- koala-wrapper/src/generated/Es2pandaEnums.ts | 171 +- .../src/generated/Es2pandaNativeModule.ts | 48 +- koala-wrapper/src/generated/index.ts | 1 + .../src/generated/peers/AnnotatedAstNode.ts | 13 +- .../generated/peers/AnnotatedExpression.ts | 10 +- .../src/generated/peers/BreakStatement.ts | 8 +- .../src/generated/peers/ClassDefinition.ts | 10 + .../src/generated/peers/ClassStaticBlock.ts | 19 +- .../src/generated/peers/ETSFunctionType.ts | 6 - .../src/generated/peers/ScriptFunction.ts | 8 +- .../src/generated/peers/SpreadElement.ts | 77 +- .../src/generated/peers/TryStatement.ts | 2 +- 543 files changed, 53622 insertions(+), 19439 deletions(-) create mode 100644 arkui-plugins/collectors/memo-collectors/factory.ts create mode 100644 arkui-plugins/collectors/memo-collectors/function-collector.ts create mode 100644 arkui-plugins/collectors/memo-collectors/memo-visitor.ts create mode 100644 arkui-plugins/collectors/memo-collectors/utils.ts create mode 100644 arkui-plugins/common/declaration-collector.ts create mode 100644 arkui-plugins/common/import-collector.ts create mode 100644 arkui-plugins/common/log-collector.ts create mode 100644 arkui-plugins/common/safe-types.ts create mode 100644 arkui-plugins/memo-plugins/memo-cache-factory.ts delete mode 100755 arkui-plugins/npm_preinstall.sh delete mode 100644 arkui-plugins/package-lock.json create mode 100644 arkui-plugins/test/demo/interop/builder_interop.ets create mode 100644 arkui-plugins/test/demo/interop/link_link_interop.ets create mode 100644 arkui-plugins/test/demo/interop/provide_consume_interop.ets rename arkui-plugins/test/demo/interop/{1.2State-1.1Link-example.ets => state_link_interop.ets} (96%) create mode 100644 arkui-plugins/test/demo/interop/state_prop_interop.ets rename arkui-plugins/test/demo/localtest/entry/{ => src/main/ets/pages}/index.ets (100%) rename arkui-plugins/test/demo/localtest/entry/{ => src/main/ets/pages}/new.ets (36%) create mode 100644 arkui-plugins/test/demo/localtest/entry/src/main/module.json5 create mode 100644 arkui-plugins/test/demo/localtest/entry/src/main/resources/base/profile/main_pages.json create mode 100644 arkui-plugins/test/demo/mock/animation/animatable-extend-basic.ets create mode 100644 arkui-plugins/test/demo/mock/builder-lambda/condition-scope/block-in-switch-case.ets create mode 100644 arkui-plugins/test/demo/mock/builder-lambda/condition-scope/else-if-in-content.ets create mode 100644 arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-break-in-nested-content.ets create mode 100644 arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-else-in-content.ets create mode 100644 arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-in-switch-in-content.ets rename arkui-plugins/test/{local/@ohos.arkui.component.units.d.ets => demo/mock/builder-lambda/condition-scope/non-builder-within-builder.ets} (58%) create mode 100644 arkui-plugins/test/demo/mock/builder-lambda/condition-scope/switch-case-in-content.ets create mode 100644 arkui-plugins/test/demo/mock/builder-lambda/condition-scope/switch-in-if-in-content.ets create mode 100644 arkui-plugins/test/demo/mock/builder-lambda/condition-scope/with-builder.ets create mode 100644 arkui-plugins/test/demo/mock/common-utils/annotation.ets rename arkui-plugins/test/{local/@ohos.arkui.component.d.ets => demo/mock/component/declare-component.ets} (64%) create mode 100644 arkui-plugins/test/demo/mock/component/for-each.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/builder-param/optional-builder-param.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/computed/computed-in-observedv2-class.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/computed/computed-in-struct.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/computed/computed-no-return-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/computed/static-computed.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/custom-dialog/base-custom-dialog.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/custom-dialog/builder-dialog-options.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/custom-dialog/controller-in-build.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/custom-dialog/controller-in-method.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/custom-dialog/declare-custom-dialog.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/custom-dialog/extends-dialog-controller.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/decorator-no-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/event/event-initialize.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/local/local-basic-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/local/local-complex-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/local/static-local.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/localstoragelink/localstoragelink-complex-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/localstoragelink/localstoragelink-primitive-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/localstorageprop-ref/localstorageprop-ref-complex-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/localstorageprop-ref/localstorageprop-ref-primitive-type.ets rename arkui-plugins/test/{local/@ohos.arkui.component.column.d.ets => demo/mock/decorators/monitor/enum-monitor-params.ets} (47%) create mode 100644 arkui-plugins/test/demo/mock/decorators/monitor/monitor-before-state-variable.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/monitor/monitor-in-observedv2-class.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/monitor/monitor-in-struct.ets rename arkui-plugins/test/{local/@ohos.arkui.component.customComponent.d.ets => demo/mock/decorators/monitor/monitor-params.ets} (34%) create mode 100644 arkui-plugins/test/demo/mock/decorators/observed-track/observed-jsonrename.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/observed-track/observed-jsonstringifyignore.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/observedv2-trace/base-observedv2-trace.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-serialization.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-extends.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-implements.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-types.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/observedv2-trace/static-observedv2-trace.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/observedv2-trace/trace-with-constructor.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/once/once-basic-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/once/once-complex-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/once/once-only.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/once/once-with-require.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/param/param-basic-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/param/param-complex-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/param/param-with-require.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-basic-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-complex-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-without-initialization.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/prop-ref/state-to-prop-ref.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/provide-and-consume/consume-basic-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/provide-and-consume/consume-complex-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-to-consume.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/provider-and-consumer/consumer-basic-type.ets rename arkui-plugins/test/{local/@ohos.arkui.component.common.d.ets => demo/mock/decorators/provider-and-consumer/consumer-complex-type.ets} (38%) create mode 100644 arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-basic-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-complex-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-to-consumer.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/require/basic-require.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/storageprop-ref/storageprop-ref-complex-type.ets create mode 100644 arkui-plugins/test/demo/mock/decorators/storageprop-ref/storageprop-ref-primitive-type.ets create mode 100644 arkui-plugins/test/demo/mock/double-dollar/double-dollar-griditem.ets rename arkui-plugins/test/{local/@ohos.arkui.component.enums.d.ets => demo/mock/double-dollar/double-dollar-toggle.ets} (50%) rename arkui-plugins/test/{local/@ohos.arkui.external.resource.d.ets => demo/mock/entry/entry-only.ets} (83%) create mode 100644 arkui-plugins/test/demo/mock/entry/localstorage/storage-use-shared-storage-false.ets create mode 100644 arkui-plugins/test/demo/mock/entry/localstorage/storage-use-shared-storage-true.ets create mode 100644 arkui-plugins/test/demo/mock/entry/localstorage/storage.ets create mode 100644 arkui-plugins/test/demo/mock/entry/localstorage/use-shared-storage-false.ets create mode 100644 arkui-plugins/test/demo/mock/entry/localstorage/use-shared-storage-true.ets create mode 100644 arkui-plugins/test/demo/mock/entry/route-name/route-name-storage-shared.ets rename arkui-plugins/test/{local/@ohos.arkui.stateManagement.d.ets => demo/mock/entry/route-name/route-name.ets} (79%) create mode 100644 arkui-plugins/test/demo/mock/imports/import-struct.ets create mode 100644 arkui-plugins/test/demo/mock/imports/kit-import.ets create mode 100644 arkui-plugins/test/demo/mock/imports/utils/simple-struct.ets rename arkui-plugins/test/{local/@ohos.arkui.stateManagement.common.d.ets => demo/mock/memo/functions/complex-memo-intrinsic.ets} (33%) create mode 100644 arkui-plugins/test/demo/mock/memo/functions/internal-memo-arg.ets create mode 100644 arkui-plugins/test/demo/mock/resource/ResourceTable.txt create mode 100644 arkui-plugins/test/demo/mock/resource/rawfile/mock.txt rename arkui-plugins/test/{local/@ohos.arkui.component.styledString.d.ets => demo/mock/wrap-builder/builder-in-generic.ets} (41%) create mode 100644 arkui-plugins/test/demo/mock/wrap-builder/init-with-builder.ets create mode 100644 arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-in-generic.ets create mode 100644 arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-in-ui.ets create mode 100644 arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-with-lambda.ets delete mode 100644 arkui-plugins/test/demo/mock/xcomponent/xcomponent-basic.ets delete mode 100644 arkui-plugins/test/local/@ohos.arkui.component.text.d.ets delete mode 100644 arkui-plugins/test/local/@ohos.arkui.stateManagement.runtime.d.ets delete mode 100644 arkui-plugins/test/local/@ohos.arkui.stateManagement.storage.d.ets delete mode 100644 arkui-plugins/test/test.log create mode 100644 arkui-plugins/test/ut/memo-plugins/function-declarations/complex-memo-intrinsic.test.ts create mode 100644 arkui-plugins/test/ut/memo-plugins/function-declarations/internal-memo-arg.test.ts rename arkui-plugins/test/ut/ui-plugins/{xcomponent/xcomponent-basic.test.ts => animation/animatable-extend-basic.test.ts} (35%) create mode 100644 arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/block-in-switch-case.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/else-if-in-content.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-break-in-nested-content.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-else-in-content.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-in-switch-in-content.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/non-builder-within-builder.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/switch-case-in-content.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/switch-in-if-in-content.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/with-builder.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/component/declare-component.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/component/for-each.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/builder-param/optional-builder-param.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-in-observedv2-class.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-in-struct.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-no-return-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/computed/static-computed.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/base-custom-dialog.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/builder-dialog-options.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/controller-in-build.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/controller-in-method.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/declare-custom-dialog.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/extends-dialog-controller.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/decorator-no-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/event/event-initialize.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/local/local-basic-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/local/local-complex-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/local/static-local.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-complex-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-primitive-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/localstorageprop-ref/localstorageprop-ref-complex-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/localstorageprop-ref/localstorageprop-ref-primitive-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/monitor/enum-monitor-params.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-before-state-variable.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-in-observedv2-class.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-in-struct.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-params.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-basic.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-observed.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonrename.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonstringifyignore.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/base-observedv2-trace.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observed-serialization.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-extends.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-implements.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-types.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/static-observedv2-trace.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/trace-with-constructor.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/once/once-basic-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/once/once-complex-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/once/once-only.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/once/once-with-require.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/param/param-basic-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/param/param-complex-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/param/param-with-require.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-basic-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-complex-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-without-initialization.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/state-to-propref.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/consume-basic-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/consume-complex-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-to-consume.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/consumer-basic-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/consumer-complex-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-basic-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-complex-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-to-consumer.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/require/basic-require.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/storageprop-ref/storageprop-ref-complex-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/decorators/storageprop-ref/storageprop-ref-primitive-type.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-griditem.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-toggle.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/entry/entry-only.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-false.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-true.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-false.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-true.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name-storage-shared.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/imports/import-struct.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/wrap-builder/builder-in-generic.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/wrap-builder/init-with-builder.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-generic.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-ui.test.ts create mode 100644 arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-with-lambda.test.ts create mode 100644 arkui-plugins/test/utils/hash-generator.ts create mode 100644 arkui-plugins/test/utils/plugins/before-memo-no-recheck.ts create mode 100644 arkui-plugins/test/utils/processor-builder.ts create mode 100644 arkui-plugins/test/utils/processors/base-processor.ts create mode 100644 arkui-plugins/test/utils/processors/main-processor.ts create mode 100644 arkui-plugins/test/utils/processors/task-processor.ts create mode 100644 arkui-plugins/test/utils/serializable.ts create mode 100644 arkui-plugins/test/utils/shared-types.ts create mode 100644 arkui-plugins/ui-plugins/builder-lambda-translators/builder-factory.ts create mode 100644 arkui-plugins/ui-plugins/builder-lambda-translators/cache/conditionBreakCache.ts create mode 100644 arkui-plugins/ui-plugins/builder-lambda-translators/condition-scope-visitor.ts delete mode 100644 arkui-plugins/ui-plugins/initstatevar.ts create mode 100644 arkui-plugins/ui-plugins/interop/builder-interop.ts create mode 100644 arkui-plugins/ui-plugins/interop/initstatevar.ts rename arkui-plugins/ui-plugins/{ => interop}/interop.ts (36%) rename arkui-plugins/ui-plugins/{ => interop}/legacy-transformer.ts (55%) create mode 100644 arkui-plugins/ui-plugins/interop/predefines.ts create mode 100644 arkui-plugins/ui-plugins/interop/utils.ts delete mode 100644 arkui-plugins/ui-plugins/preprocessor-transform.ts create mode 100644 arkui-plugins/ui-plugins/property-translators/cache/monitorCache.ts create mode 100644 arkui-plugins/ui-plugins/property-translators/cache/propertyCache.ts create mode 100644 arkui-plugins/ui-plugins/property-translators/computed.ts create mode 100644 arkui-plugins/ui-plugins/property-translators/consumer.ts create mode 100644 arkui-plugins/ui-plugins/property-translators/local.ts create mode 100644 arkui-plugins/ui-plugins/property-translators/localStoragePropRef.ts create mode 100644 arkui-plugins/ui-plugins/property-translators/monitor.ts create mode 100644 arkui-plugins/ui-plugins/property-translators/observedV2Trace.ts create mode 100644 arkui-plugins/ui-plugins/property-translators/once.ts create mode 100644 arkui-plugins/ui-plugins/property-translators/param.ts create mode 100644 arkui-plugins/ui-plugins/property-translators/propRef.ts create mode 100644 arkui-plugins/ui-plugins/property-translators/provider.ts create mode 100644 arkui-plugins/ui-plugins/property-translators/return-transformer.ts create mode 100644 arkui-plugins/ui-plugins/property-translators/storagePropRef.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/attribute-no-invoke.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/builderparam-decorator-check.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/check-property-modifiers.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/component-componentV2-init-check.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/construct-parameter.ts delete mode 100644 arkui-plugins/ui-syntax-plugins/rules/decorators-in-ui-component-only.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/main-pages-entry-check.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/nested-reuse-component-check.ts delete mode 100644 arkui-plugins/ui-syntax-plugins/rules/no-duplicate-decorators.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/no-duplicate-id.ts delete mode 100644 arkui-plugins/ui-syntax-plugins/rules/no-duplicate-state-manager.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/property-type.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/require-decorator-regular.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/reusable-component-in-V2-check.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/reusableV2-decorator-check.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/specific-component-children.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/static-param-require.ts rename arkui-plugins/ui-syntax-plugins/rules/{local-builder-check.ts => struct-no-extends.ts} (32%) create mode 100644 arkui-plugins/ui-syntax-plugins/rules/struct-property-optional.ts delete mode 100644 arkui-plugins/ui-syntax-plugins/rules/type-decorator-check.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/ui-consistent-check.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/validate-decorator-target.ts delete mode 100644 arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-cons.ts create mode 100644 arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-constructor.ts rename arkui-plugins/ui-syntax-plugins/transformers/{parsed-ui-syntax-linter-transformer.ts => ui-syntax-linter-transformer.ts} (76%) create mode 100644 koala-wrapper/native/include/memoryTracker.h create mode 100644 koala-wrapper/native/src/memoryTracker.cc delete mode 100644 koala-wrapper/package-lock.json create mode 100644 koala-wrapper/src/arkts-api/node-utilities/AnnotationDeclaration.ts create mode 100644 koala-wrapper/src/arkts-api/node-utilities/BreakStatement.ts create mode 100644 koala-wrapper/src/arkts-api/node-utilities/ClassStaticBlock.ts create mode 100644 koala-wrapper/src/arkts-api/node-utilities/SpreadElement.ts create mode 100644 koala-wrapper/src/arkts-api/node-utilities/SwitchCaseStatement.ts create mode 100644 koala-wrapper/src/arkts-api/node-utilities/SwitchStatement.ts create mode 100644 koala-wrapper/src/arkts-api/node-utilities/TSClassImplements.ts create mode 100644 koala-wrapper/src/arkts-api/peers/Diagnostic.ts create mode 100644 koala-wrapper/src/arkts-api/peers/DiagnosticInfo.ts create mode 100644 koala-wrapper/src/arkts-api/peers/DiagnosticKind.ts create mode 100644 koala-wrapper/src/arkts-api/peers/SuggestionInfo.ts create mode 100644 koala-wrapper/src/arkts-api/utilities/nodeCache.ts diff --git a/arkui-plugins/BUILD.gn b/arkui-plugins/BUILD.gn index ef1316286..707355e1d 100755 --- a/arkui-plugins/BUILD.gn +++ b/arkui-plugins/BUILD.gn @@ -33,8 +33,25 @@ action("gen_ui_plugins") { outputs = [ "$target_gen_dir" ] } +action("build_ets_sysResource") { + deps = [":gen_ui_plugins"] + script = "//developtools/ace_ets2bundle/generateSysResource.py" + ets_sysResource = "$target_gen_dir" + "/lib/ui-plugins/sysResource.js" + outputs = [ ets_sysResource ] + + _id_defined_json = "//base/global/system_resources/systemres/main/resources/base/element/id_defined.json" + inputs = [ _id_defined_json ] + + args = [ + "--input-json", + rebase_path(_id_defined_json, root_build_dir), + "--output-js", + rebase_path(ets_sysResource, root_build_dir), + ] +} + ohos_copy("ui_plugin") { - deps = [ ":gen_ui_plugins" ] + deps = [":gen_ui_plugins", ":build_ets_sysResource" ] sources = [ rebase_path("$target_gen_dir") ] outputs = [ target_out_dir + "/$target_name" ] module_source_dir = target_out_dir + "/$target_name" @@ -44,7 +61,7 @@ ohos_copy("ui_plugin") { } ohos_copy("ohos_ets_ui_plugins") { - deps = [ ":gen_ui_plugins" ] + deps = [ ":gen_ui_plugins", ":build_ets_sysResource" ] sources = [ rebase_path("$target_gen_dir") ] outputs = [ ohos_ets_ui_plugins_path ] subsystem_name = "developtools" diff --git a/arkui-plugins/collectors/memo-collectors/factory.ts b/arkui-plugins/collectors/memo-collectors/factory.ts new file mode 100644 index 000000000..66d1dcede --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/factory.ts @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { + addMemoAnnotation, + collectMemoFromCallExpression, + collectMemoFromNewClass, + findCanAddMemoFromArrowFunction, + findCanAddMemoFromClassProperty, + findCanAddMemoFromMethod, + findCanAddMemoFromParameter, + findCanAddMemoFromProperty, + findCanAddMemoFromTypeAlias, +} from './utils'; +import { coerceToAstNode } from '../../common/arkts-utils'; + +export type RewriteAfterFoundFn = ( + node: T, + nodeType: arkts.Es2pandaAstNodeType +) => T; + +export function findAndCollectMemoableNode(node: arkts.AstNode, rewriteFn?: RewriteAfterFoundFn): arkts.AstNode { + const type = arkts.nodeType(node); + if (collectByType.has(type)) { + return collectByType.get(type)!(node, rewriteFn); + } + return node; +} + +export class factory { + /** + * Find and collect possible `@memo` property with arrow function value. + * + * @param node `arkts.Property` node + * @param rewriteFn function callback to rewrite node when it is `@memo` property + * @returns `arkts.Property` node + */ + static findAndCollectMemoableProperty( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + let found: boolean = false; + if (findCanAddMemoFromProperty(node)) { + found = true; + addMemoAnnotation(node.value! as arkts.ArrowFunctionExpression); + } + if (found && !!rewriteFn) { + return rewriteFn(node, arkts.Es2pandaAstNodeType.AST_NODE_TYPE_PROPERTY); + } + return node; + } + + /** + * Find and collect possible `@memo` class property with arrow function value. + * + * @param node `arkts.ClassProperty` node + * @param rewriteFn function callback to rewrite node when it is `@memo` class property + * @returns `arkts.ClassProperty` node + */ + static findAndCollectMemoableClassProperty( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + let found: boolean = false; + if (findCanAddMemoFromClassProperty(node)) { + found = true; + addMemoAnnotation(node); + } + if (found && !!rewriteFn) { + return rewriteFn(node, arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY); + } + return node; + } + + /** + * Find and collect possible `@memo` type alias with function type. + * + * @param node `arkts.TSTypeAliasDeclaration` node + * @param rewriteFn function callback to rewrite node when it is `@memo` type alias + * @returns `arkts.TSTypeAliasDeclaration` node + */ + static findAndCollectMemoableTypeAlias( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + let found: boolean = false; + if (findCanAddMemoFromTypeAlias(node)) { + found = true; + addMemoAnnotation(node); + } + if (found && !!rewriteFn) { + return rewriteFn(node, arkts.Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION); + } + return node; + } + + /** + * Find and collect possible `@memo` parameter with function type. + * + * @param node `arkts.ETSParameterExpression` node + * @param rewriteFn function callback to rewrite node when it is `@memo` parameter + * @returns `arkts.ETSParameterExpression` node + */ + static findAndCollectMemoableParameter( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + let found: boolean = false; + if (findCanAddMemoFromParameter(node)) { + found = true; + addMemoAnnotation(node); + } + if (found && !!rewriteFn) { + return rewriteFn(node, arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION); + } + return node; + } + + /** + * Find and collect possible `@memo` method. + * + * @param node `arkts.MethodDefinition` node + * @param rewriteFn function callback to rewrite node when it is `@memo` method + * @returns `arkts.MethodDefinition` node + */ + static findAndCollectMemoableMethod( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + let found: boolean = false; + if (findCanAddMemoFromMethod(node)) { + found = true; + addMemoAnnotation(node.scriptFunction); + } + if (found && !!rewriteFn) { + return rewriteFn(node, arkts.Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION); + } + return node; + } + + /** + * Find and collect possible `@memo` arrow function. + * + * @param node `arkts.ArrowFunctionExpression` node + * @param rewriteFn function callback to rewrite node when it is `@memo` arrow function + * @returns `arkts.ArrowFunctionExpression` node + */ + static findAndCollectMemoableArrowFunction( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + let found: boolean = false; + if (findCanAddMemoFromArrowFunction(node)) { + found = true; + addMemoAnnotation(node.scriptFunction); + } + if (found && !!rewriteFn) { + return rewriteFn(node, arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION); + } + return node; + } + + /** + * Find and collect possible `@memo` function call. + * + * @param node `arkts.CallExpression` node + * @returns `arkts.CallExpression` node + */ + static findAndCollectMemoableCallExpression( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + const _node = coerceToAstNode(node); + collectMemoFromCallExpression(_node); + return node; + } + + /** + * Find and collect new class instance with possible `@memo` type parameters. + * + * @param node `arkts.ETSNewClassInstanceExpression` node + * @returns `arkts.ETSNewClassInstanceExpression` node + */ + static findAndCollectMemoableNewClass( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + const _node = coerceToAstNode(node); + collectMemoFromNewClass(_node); + return node; + } +} + +type CollectFactoryFn = (node: T, rewriteFn?: RewriteAfterFoundFn) => T; + +const collectByType = new Map([ + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_PROPERTY, factory.findAndCollectMemoableProperty], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY, factory.findAndCollectMemoableClassProperty], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION, factory.findAndCollectMemoableTypeAlias], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION, factory.findAndCollectMemoableParameter], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION, factory.findAndCollectMemoableMethod], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION, factory.findAndCollectMemoableArrowFunction], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CALL_EXPRESSION, factory.findAndCollectMemoableCallExpression], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NEW_CLASS_INSTANCE_EXPRESSION, factory.findAndCollectMemoableNewClass], +]); diff --git a/arkui-plugins/collectors/memo-collectors/function-collector.ts b/arkui-plugins/collectors/memo-collectors/function-collector.ts new file mode 100644 index 000000000..2f9d07b8c --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/function-collector.ts @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { AbstractVisitor } from '../../common/abstract-visitor'; +import { + checkIsMemoFromMemoableInfo, + collectMemoableInfoInFunctionReturnType, + collectMemoableInfoInScriptFunction, + collectMemoableInfoInVariableDeclarator, + collectMemoableInfoMapInFunctionParams, + collectMemoScriptFunctionBody, + collectScriptFunctionReturnTypeFromInfo, + findIdentifierFromCallee, + getDeclResolveAlias, + MemoableInfo, +} from './utils'; + +export class MemoFunctionCollector extends AbstractVisitor { + private returnMemoableInfo: MemoableInfo | undefined; + private paramMemoableInfoMap: Map | undefined; + private _disableCollectReturn: boolean = false; + private _shouldCollectReturn: boolean = true; + + private get shouldCollectReturn(): boolean { + if (this._disableCollectReturn) { + return false; + } + return this._shouldCollectReturn; + } + + private set shouldCollectReturn(newValue: boolean) { + if (this._disableCollectReturn) { + return; + } + this._shouldCollectReturn = newValue; + } + + private disableCollectReturnBeforeCallback(callbackFn: () => void): void { + const tempValue = this.shouldCollectReturn; + this.shouldCollectReturn = false; + callbackFn(); + this.shouldCollectReturn = tempValue; + } + + private collectMemoAstNode(node: arkts.AstNode, info: MemoableInfo): void { + if (checkIsMemoFromMemoableInfo(info, false)) { + arkts.NodeCache.getInstance().collect(node); + } + } + + private collectCallWithDeclaredPeerInParamMap(node: arkts.CallExpression, peer: arkts.AstNode['peer']): void { + const memoableInfo = this.paramMemoableInfoMap!.get(peer)!; + if (checkIsMemoFromMemoableInfo(memoableInfo, true)) { + arkts.NodeCache.getInstance().collect(node); + } + } + + private collectCallWithDeclaredIdInVariableDeclarator( + node: arkts.CallExpression, + declarator: arkts.VariableDeclarator + ): void { + const shouldCollect = + arkts.NodeCache.getInstance().has(declarator) || + (!!declarator.initializer && arkts.NodeCache.getInstance().has(declarator.initializer)); + if (shouldCollect) { + arkts.NodeCache.getInstance().collect(node); + } + } + + private visitVariableDeclarator(node: arkts.VariableDeclarator): arkts.AstNode { + let memoableInfo: MemoableInfo; + if (this.paramMemoableInfoMap?.has(node.name.peer)) { + memoableInfo = this.paramMemoableInfoMap.get(node.name.peer)!; + } else { + memoableInfo = collectMemoableInfoInVariableDeclarator(node); + } + this.collectMemoAstNode(node, memoableInfo); + if (!node.initializer) { + return node; + } + if (arkts.isArrowFunctionExpression(node.initializer)) { + const func = node.initializer.scriptFunction; + const localInfo = collectMemoableInfoInScriptFunction(func); + const shouldCollectParameter = + (localInfo.hasBuilder || localInfo.hasMemo) && !localInfo.hasMemoEntry && !localInfo.hasMemoIntrinsic; + const shouldCollectReturn = + localInfo.hasBuilder || localInfo.hasMemo || memoableInfo.hasBuilder || memoableInfo.hasMemo; + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(func); + collectScriptFunctionReturnTypeFromInfo(func, returnMemoableInfo); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams( + func, + shouldCollectParameter + ); + const body = func.body; + if (!!body && arkts.isBlockStatement(body)) { + collectMemoScriptFunctionBody( + body, + returnMemoableInfo, + paramMemoableInfoMap, + gensymCount, + !shouldCollectReturn + ); + } + return node; + } + this.shouldCollectReturn = !!memoableInfo.hasMemo || !!memoableInfo.hasBuilder; + this.visitor(node.initializer); + return node; + } + + private visitCallExpression(node: arkts.CallExpression): arkts.AstNode { + if (arkts.NodeCache.getInstance().has(node)) { + this.disableCollectReturnBeforeCallback(() => { + this.visitEachChild(node); + }); + return node; + } + const expr = findIdentifierFromCallee(node.expression); + const decl = (expr && getDeclResolveAlias(expr)) ?? node.expression; + if (!decl) { + this.disableCollectReturnBeforeCallback(() => { + this.visitEachChild(node); + }); + return node; + } + if (arkts.NodeCache.getInstance().has(decl)) { + arkts.NodeCache.getInstance().collect(node); + } + if (this.paramMemoableInfoMap?.has(decl.peer)) { + this.collectCallWithDeclaredPeerInParamMap(node, decl.peer); + } else if (arkts.isEtsParameterExpression(decl) && this.paramMemoableInfoMap?.has(decl.identifier.peer)) { + this.collectCallWithDeclaredPeerInParamMap(node, decl.identifier.peer); + } else if (arkts.isIdentifier(decl) && !!decl.parent && arkts.isVariableDeclarator(decl.parent)) { + this.collectCallWithDeclaredIdInVariableDeclarator(node, decl.parent); + } + this.disableCollectReturnBeforeCallback(() => { + this.visitEachChild(node); + }); + return node; + } + + private visitIdentifier(node: arkts.Identifier): arkts.AstNode { + const decl = getDeclResolveAlias(node); + if (!decl) { + return node; + } + if (this.paramMemoableInfoMap?.has(decl.peer)) { + arkts.NodeCache.getInstance().collect(node); + } else if (arkts.isEtsParameterExpression(decl) && this.paramMemoableInfoMap?.has(decl.identifier.peer)) { + arkts.NodeCache.getInstance().collect(node); + } + return node; + } + + private visitReturnStatement(node: arkts.ReturnStatement): arkts.AstNode { + if (!!this.returnMemoableInfo && !!node.argument && arkts.isArrowFunctionExpression(node.argument)) { + this.collectMemoAstNode(node.argument, this.returnMemoableInfo); + } + arkts.NodeCache.getInstance().collect(node); + this.visitEachChild(node); + return node; + } + + registerReturnInfo(info: MemoableInfo): this { + this.returnMemoableInfo = info; + return this; + } + + registerParamInfoMap(infoMap: Map): this { + this.paramMemoableInfoMap = infoMap; + return this; + } + + disableCollectReturn(): this { + this._disableCollectReturn = true; + return this; + } + + enableCollectReturn(): this { + this._disableCollectReturn = false; + return this; + } + + reset(): void { + this.returnMemoableInfo = undefined; + this.paramMemoableInfoMap = undefined; + this._shouldCollectReturn = true; + this._disableCollectReturn = false; + } + + visitor(node: arkts.AstNode): arkts.AstNode { + if (arkts.isVariableDeclarator(node)) { + return this.visitVariableDeclarator(node); + } + if (arkts.isCallExpression(node)) { + return this.visitCallExpression(node); + } + if (!!this.paramMemoableInfoMap && arkts.isIdentifier(node)) { + return this.visitIdentifier(node); + } + if (arkts.isReturnStatement(node) && this.shouldCollectReturn) { + return this.visitReturnStatement(node); + } + if ( + arkts.isArrowFunctionExpression(node) && + !arkts.NodeCache.getInstance().has(node) && + !arkts.NodeCache.getInstance().has(node.scriptFunction) + ) { + this.shouldCollectReturn = false; + } + return this.visitEachChild(node); + } +} diff --git a/arkui-plugins/collectors/memo-collectors/memo-visitor.ts b/arkui-plugins/collectors/memo-collectors/memo-visitor.ts new file mode 100644 index 000000000..233288748 --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/memo-visitor.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { AbstractVisitor } from '../../common/abstract-visitor'; +import { findAndCollectMemoableNode } from './factory'; + +export class MemoVisitor extends AbstractVisitor { + visitor(node: arkts.AstNode): arkts.AstNode { + const newNode = this.visitEachChild(node); + findAndCollectMemoableNode(newNode); + return newNode; + } +} diff --git a/arkui-plugins/collectors/memo-collectors/utils.ts b/arkui-plugins/collectors/memo-collectors/utils.ts new file mode 100644 index 000000000..61eaeda41 --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/utils.ts @@ -0,0 +1,913 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { annotation, forEachArgWithParam, isDecoratorAnnotation } from '../../common/arkts-utils'; +import { ImportCollector } from '../../common/import-collector'; +import { DecoratorNames, GenSymPrefix, MEMO_IMPORT_SOURCE_NAME } from '../../common/predefines'; +import { MemoFunctionCollector } from './function-collector'; + +export enum MemoNames { + MEMO = 'memo', + MEMO_SKIP = 'memo_skip', + MEMO_INTRINSIC = 'memo_intrinsic', + MEMO_ENTRY = 'memo_entry', +} + +export type MemoAstNode = + | arkts.ScriptFunction + | arkts.ETSParameterExpression + | arkts.ClassProperty + | arkts.TSTypeAliasDeclaration + | arkts.ETSFunctionType + | arkts.ArrowFunctionExpression + | arkts.ETSUnionType + | arkts.VariableDeclaration; + +interface MemoableAnnotationInfo { + hasMemo?: boolean; + hasMemoSkip?: boolean; + hasMemoIntrinsic?: boolean; + hasMemoEntry?: boolean; + hasBuilder?: boolean; + hasBuilderParam?: boolean; +} + +export type MemoableInfo = MemoableAnnotationInfo & { + hasProperType?: boolean; + isWithinTypeParams?: boolean; +}; + +export function isMemoAnnotation(node: arkts.AnnotationUsage, memoName: MemoNames): boolean { + return node.expr !== undefined && arkts.isIdentifier(node.expr) && node.expr.name === memoName; +} + +export function hasMemoAnnotation(node: T): boolean { + return node.annotations.some((it) => isMemoAnnotation(it, MemoNames.MEMO)); +} + +export function addMemoAnnotation(node: T, memoName: MemoNames = MemoNames.MEMO): T { + collectMemoAnnotationSource(memoName); + if (arkts.isETSUnionType(node)) { + return arkts.factory.updateUnionType( + node, + node.types.map((type) => { + if (arkts.isETSFunctionType(type)) { + return addMemoAnnotation(type, memoName); + } + return type; + }) + ) as T; + } + const newAnnotations: arkts.AnnotationUsage[] = [ + ...node.annotations.filter((it) => !isMemoAnnotation(it, memoName)), + annotation(memoName), + ]; + collectMemoAnnotationImport(memoName); + if (arkts.isEtsParameterExpression(node)) { + node.annotations = newAnnotations; + arkts.NodeCache.getInstance().collect(node); + return node; + } + const newNode = node.setAnnotations(newAnnotations) as T; + arkts.NodeCache.getInstance().collect(newNode); + return newNode; +} + +export function hasMemoableAnnotation(node: T): MemoableAnnotationInfo { + let hasBuilder: boolean = false; + let hasBuilderParam: boolean = false; + let hasMemo: boolean = false; + let hasMemoSkip: boolean = false; + let hasMemoIntrinsic: boolean = false; + let hasMemoEntry: boolean = false; + node.annotations.forEach((it) => { + hasBuilder ||= isDecoratorAnnotation(it, DecoratorNames.BUILDER); + hasBuilderParam ||= isDecoratorAnnotation(it, DecoratorNames.BUILDER_PARAM); + hasMemo ||= isMemoAnnotation(it, MemoNames.MEMO); + hasMemoSkip ||= isMemoAnnotation(it, MemoNames.MEMO_SKIP); + hasMemoIntrinsic ||= isMemoAnnotation(it, MemoNames.MEMO_INTRINSIC); + hasMemoEntry ||= isMemoAnnotation(it, MemoNames.MEMO_ENTRY); + }); + return { + ...(hasMemo ? { hasMemo } : {}), + ...(hasMemoSkip ? { hasMemoSkip } : {}), + ...(hasMemoIntrinsic ? { hasMemoIntrinsic } : {}), + ...(hasMemoEntry ? { hasMemoEntry } : {}), + ...(hasBuilder ? { hasBuilder } : {}), + ...(hasBuilderParam ? { hasBuilderParam } : {}), + }; +} + +export function collectMemoAnnotationImport(memoName: MemoNames = MemoNames.MEMO): void { + ImportCollector.getInstance().collectImport(memoName); +} + +export function collectMemoAnnotationSource(memoName: MemoNames = MemoNames.MEMO): void { + ImportCollector.getInstance().collectSource(memoName, MEMO_IMPORT_SOURCE_NAME); +} + +export function collectMemoableInfoInUnionType(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isETSUnionType(node)) { + return currInfo; + } + node.types.forEach((t) => { + currInfo = { + ...currInfo, + ...collectMemoableInfoInTypeReference(t), + ...collectMemoableInfoInFunctionType(t), + ...collectMemoableInfoInUnionType(t), + }; + }); + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + return currInfo; +} + +function collectMemoableInfoInTypeReferencePart(node: arkts.ETSTypeReferencePart): MemoableInfo { + let currInfo: MemoableInfo = {}; + if (!node.typeParams) { + return currInfo; + } + node.typeParams.params.forEach((t) => { + currInfo = { + ...currInfo, + ...collectMemoableInfoInUnionType(t), + ...collectMemoableInfoInFunctionType(t), + }; + if (arkts.isETSTypeReference(t) && !!t.part && !!arkts.isETSTypeReferencePart(t.part)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInTypeReferencePart(t.part), + }; + } + }); + if (checkIsMemoFromMemoableInfo(currInfo)) { + return { isWithinTypeParams: true }; + } + return { isWithinTypeParams: currInfo.isWithinTypeParams }; +} + +export function collectMemoableInfoInTypeReference(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + const metadata = arkts.NodeCache.getInstance().get(node)?.metadata; + return { ...currInfo, ...metadata }; + } + if (!arkts.isETSTypeReference(node) || !node.part || !arkts.isETSTypeReferencePart(node.part)) { + return currInfo; + } + currInfo = { + ...currInfo, + ...collectMemoableInfoInTypeReferencePart(node.part), + }; + const expr = node.part.name; + let decl: arkts.AstNode | undefined; + if (!expr || !(decl = arkts.getDecl(expr))) { + return currInfo; + } + return { + ...currInfo, + ...collectMemoableInfoInTypeAlias(decl), + }; +} + +export function collectMemoableInfoInFunctionType(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isETSFunctionType(node)) { + return currInfo; + } + currInfo.hasProperType = true; + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + return currInfo; +} + +export function collectMemoableInfoInTypeAlias(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isTSTypeAliasDeclaration(node)) { + return currInfo; + } + currInfo = { + ...currInfo, + ...hasMemoableAnnotation(node), + }; + if (!!node.typeAnnotation) { + return { + ...currInfo, + ...collectMemoableInfoInType(node.typeAnnotation), + }; + } + return currInfo; +} + +export function collectMemoableInfoInParameter(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + const metadata = arkts.NodeCache.getInstance().get(node)?.metadata; + return { ...currInfo, ...metadata }; + } + if (!arkts.isEtsParameterExpression(node)) { + return currInfo; + } + currInfo = { + ...currInfo, + ...hasMemoableAnnotation(node), + }; + if (!!node.type) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInType(node.type), + }; + } + if (!!node.initializer) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInArrowFunction(node.initializer), + }; + } + if (!!currInfo.isWithinTypeParams) { + const forbidTypeRewrite = !checkIsMemoFromMemoableInfo(currInfo); + arkts.NodeCache.getInstance().collect(node, { forbidTypeRewrite, isWithinTypeParams: true }); + } + return currInfo; +} + +export function collectMemoableInfoInVariableDeclarator(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isVariableDeclarator(node)) { + return currInfo; + } + if (!!node.name.typeAnnotation) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInType(node.name.typeAnnotation), + }; + } + if (!!node.initializer && arkts.isArrowFunctionExpression(node.initializer)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInArrowFunction(node.initializer), + }; + } + if (!!node.parent && arkts.isVariableDeclaration(node.parent)) { + currInfo = { + ...currInfo, + ...hasMemoableAnnotation(node.parent), + }; + } + const decl = arkts.getDecl(node.name); + if (!decl) { + return currInfo; + } + if (arkts.isMethodDefinition(decl)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInScriptFunction(decl.scriptFunction), + }; + } else if (arkts.isClassProperty(decl)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInClassProperty(decl), + }; + } + return currInfo; +} + +export function collectMemoableInfoInProperty(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + const property = node as arkts.Property; + const hasProperType = !!property.value && arkts.isArrowFunctionExpression(property.value); + return { ...currInfo, hasMemo: true, hasProperType }; + } + if (!arkts.isProperty(node) || !node.key || !arkts.isIdentifier(node.key)) { + return currInfo; + } + const decl = arkts.getDecl(node.key); + if (!decl) { + return currInfo; + } + if (arkts.isMethodDefinition(decl)) { + const newInfo = collectMemoableInfoInMethod(decl); + currInfo = { ...currInfo, ...newInfo }; + } else if (arkts.isClassProperty(decl)) { + const newInfo = collectMemoableInfoInClassProperty(decl); + currInfo = { ...currInfo, ...newInfo }; + } + currInfo.hasProperType = false; + if (!!node.value && arkts.isArrowFunctionExpression(node.value)) { + currInfo.hasProperType = true; + currInfo = { + ...currInfo, + ...collectMemoableInfoInScriptFunction(node.value.scriptFunction), + }; + } + return currInfo; +} + +export function collectMemoableInfoInClassProperty(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isClassProperty(node)) { + return currInfo; + } + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + if (!!node.typeAnnotation) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInType(node.typeAnnotation), + }; + } + if (!!node.value) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInArrowFunction(node.value), + }; + } + return currInfo; +} + +export function collectMemoableInfoInArrowFunction(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isArrowFunctionExpression(node)) { + return currInfo; + } + currInfo.hasProperType = true; + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + if (!!node.scriptFunction) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInScriptFunction(node.scriptFunction), + }; + } + if (!!node.parent && arkts.isAssignmentExpression(node.parent) && !!node.parent.left) { + const expr = arkts.isMemberExpression(node.parent.left) ? node.parent.left.property : node.parent.left; + const decl = arkts.getDecl(expr); + if (!decl) { + return currInfo; + } + if (arkts.isClassProperty(decl)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInClassProperty(decl), + }; + } + } + return currInfo; +} + +export function collectMemoableInfoInScriptFunction(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isScriptFunction(node)) { + return currInfo; + } + currInfo.hasProperType = true; + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + return currInfo; +} + +export function collectMemoableInfoInMethod(node: arkts.MethodDefinition): MemoableInfo { + const hasReceiver = node.scriptFunction.hasReceiver; + const isSetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET; + const isGetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; + let info: MemoableInfo = {}; + if (isSetter && node.scriptFunction.params.length > 0) { + if (hasReceiver && node.scriptFunction.params.length === 2) { + info = collectMemoableInfoInParameter(node.scriptFunction.params.at(1)!); + } else { + info = collectMemoableInfoInParameter(node.scriptFunction.params.at(0)!); + } + } else if (isGetter) { + info = collectMemoableInfoInFunctionReturnType(node.scriptFunction); + } + return collectMemoableInfoInScriptFunction(node.scriptFunction, info); +} + +export function collectMemoableInfoInType(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + return { + ...currInfo, + ...collectMemoableInfoInFunctionType(node), + ...collectMemoableInfoInUnionType(node), + ...collectMemoableInfoInTypeReference(node), + }; +} + +export function collectMemoableInfoInFunctionReturnType(node: arkts.ScriptFunction): MemoableInfo { + if (!!node.returnTypeAnnotation) { + let memoableInfo: MemoableInfo; + if (arkts.NodeCache.getInstance().has(node.returnTypeAnnotation)) { + memoableInfo = { hasMemo: true, hasProperType: true }; + } else { + memoableInfo = collectMemoableInfoInType(node.returnTypeAnnotation); + } + if ((memoableInfo.hasMemo || memoableInfo.hasBuilder) && memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(node.returnTypeAnnotation); + } + return memoableInfo; + } + return {}; +} + +export function collectScriptFunctionReturnTypeFromInfo(node: arkts.ScriptFunction, info: MemoableInfo): void { + const returnType = node.returnTypeAnnotation; + if (!returnType || arkts.NodeCache.getInstance().has(returnType)) { + return; + } + const isMemoReturnType = checkIsMemoFromMemoableInfo(info); + const isWithinTypeParams = info.isWithinTypeParams; + if (isMemoReturnType || isWithinTypeParams) { + const forbidTypeRewrite = !isMemoReturnType; + arkts.NodeCache.getInstance().collect(returnType, { forbidTypeRewrite, isWithinTypeParams }); + } +} + +export function collectGensymDeclarator(declarator: arkts.VariableDeclarator, info: MemoableInfo): void { + if (!info.hasMemo && !info.hasBuilder) { + return; + } + arkts.NodeCache.getInstance().collect(declarator); + const initializer = declarator.initializer; + if (!initializer || !arkts.isConditionalExpression(initializer)) { + return; + } + const alternate = initializer.alternate; + if (!alternate) { + return; + } + let arrowFunc: arkts.ArrowFunctionExpression | undefined; + if (arkts.isTSAsExpression(alternate) && !!alternate.expr && arkts.isArrowFunctionExpression(alternate.expr)) { + arrowFunc = alternate.expr; + } else if (arkts.isArrowFunctionExpression(alternate)) { + arrowFunc = alternate; + } + if (!!arrowFunc) { + const func = arrowFunc.scriptFunction; + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(func); + collectScriptFunctionReturnTypeFromInfo(func, returnMemoableInfo); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams(func); + const body = func.body; + if (!!body && arkts.isBlockStatement(body)) { + collectMemoScriptFunctionBody(body, returnMemoableInfo, paramMemoableInfoMap, gensymCount); + } + } +} + +export function collectMemoableInfoMapInFunctionParams( + node: arkts.ScriptFunction, + shouldCollectParameter: boolean = true +): [Map, number] { + const hasReceiver = node.hasReceiver; + const paramMap: Map = new Map(); + let gensymCount: number = 0; + node.params.slice(hasReceiver ? 1 : 0).forEach((p) => { + const info = collectMemoableInfoInFunctionParam(node, p, gensymCount, shouldCollectParameter); + gensymCount = info.gensymCount; + info.peers.forEach((peer) => paramMap.set(peer, info.memoableInfo)); + }); + return [paramMap, gensymCount]; +} + +interface FunctionParamCollectInfo { + peers: arkts.AstNode['peer'][]; + gensymCount: number; + memoableInfo: MemoableInfo; +} + +function collectMemoableInfoInFunctionParam( + node: arkts.ScriptFunction, + param: arkts.Expression, + gensymCount: number, + shouldCollectParameter: boolean = true +): FunctionParamCollectInfo { + const peers: arkts.AstNode['peer'][] = []; + let memoableInfo: MemoableInfo; + const _param = param as arkts.ETSParameterExpression; + if (arkts.NodeCache.getInstance().has(_param)) { + const metadata = arkts.NodeCache.getInstance().get(_param)!.metadata ?? {}; + const { hasMemoSkip } = metadata; + memoableInfo = { hasMemo: true, hasMemoSkip, hasProperType: true }; + } else { + memoableInfo = collectMemoableInfoInParameter(_param); + } + if (_param.identifier.name.startsWith(GenSymPrefix.INTRINSIC) && !!node.body && arkts.isBlockStatement(node.body)) { + const declaration = node.body.statements.at(gensymCount); + if (!!declaration && arkts.isVariableDeclaration(declaration) && declaration.declarators.length > 0) { + const declarator = declaration.declarators[0]; + collectGensymDeclarator(declarator, memoableInfo); + if (!memoableInfo.hasMemoSkip && shouldCollectParameter) { + peers.push(declarator.name.peer); + } + gensymCount++; + } + } + if (checkIsMemoFromMemoableInfo(memoableInfo)) { + arkts.NodeCache.getInstance().collect(_param, { hasMemoSkip: memoableInfo.hasMemoSkip }); + } + if (!memoableInfo.hasMemoSkip && shouldCollectParameter) { + peers.push(_param.identifier.peer); + } + return { peers, memoableInfo, gensymCount }; +} + +/** + * Collect `@memo` annotated `arkts.TypeNode` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.TypeNode` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromTypeAnnotation( + typeAnnotation: arkts.AstNode | undefined +): typeAnnotation is arkts.ETSFunctionType { + if (!typeAnnotation) { + return false; + } + const memoableInfo = collectMemoableInfoInType(typeAnnotation); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(typeAnnotation); + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.Property` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.Property` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromProperty(property: arkts.AstNode): property is arkts.Property { + const memoableInfo = collectMemoableInfoInProperty(property); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(property); + } + const hasBuilder = !!memoableInfo.hasBuilder || !!memoableInfo.hasBuilderParam; + return hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.ClassProperty` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.ClassProperty` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromClassProperty(property: arkts.AstNode): property is arkts.ClassProperty { + const memoableInfo = collectMemoableInfoInClassProperty(property); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(property); + } + const hasBuilderType = !!memoableInfo.hasBuilder || !!memoableInfo.hasBuilderParam; + if (!!memoableInfo.isWithinTypeParams) { + arkts.NodeCache.getInstance().collect(property, { isWithinTypeParams: true }); + } + return hasBuilderType && !memoableInfo.hasMemo && !!memoableInfo.hasProperType && !memoableInfo.isWithinTypeParams; +} + +/** + * Collect `@memo` annotated `arkts.ETSParameterExpression` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.ETSParameterExpression` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromParameter(param: arkts.AstNode | undefined): param is arkts.ETSParameterExpression { + if (!param) { + return false; + } + const memoableInfo = collectMemoableInfoInParameter(param); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(param, { hasMemoSkip: memoableInfo.hasMemoSkip }); + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.ArrowFunctionExpression` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.ArrowFunctionExpression` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromArrowFunction(node: arkts.AstNode): node is arkts.ArrowFunctionExpression { + if (!arkts.isArrowFunctionExpression(node)) { + return false; + } + const memoableInfo = collectMemoableInfoInArrowFunction(node); + const { hasMemoEntry, hasMemoIntrinsic } = memoableInfo; + const func = node.scriptFunction; + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(func); + collectScriptFunctionReturnTypeFromInfo(func, returnMemoableInfo); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams( + func, + !hasMemoEntry && !hasMemoIntrinsic + ); + const isMemo = checkIsMemoFromMemoableInfo(memoableInfo); + if (isMemo && !arkts.NodeCache.getInstance().has(node)) { + arkts.NodeCache.getInstance().collect(node, { hasMemoEntry, hasMemoIntrinsic }); + const body = func.body; + if (!!body && arkts.isBlockStatement(body)) { + const disableCollectReturn = hasMemoEntry || hasMemoIntrinsic; + collectMemoScriptFunctionBody( + body, + returnMemoableInfo, + paramMemoableInfoMap, + gensymCount, + disableCollectReturn + ); + } + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.TSTypeAliasDeclaration` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.TSTypeAliasDeclaration` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromTypeAlias(node: arkts.AstNode): node is arkts.TSTypeAliasDeclaration { + const memoableInfo = collectMemoableInfoInTypeAlias(node); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(node); + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.MethodDefinition` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.MethodDefinition` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromMethod(node: arkts.AstNode): node is arkts.MethodDefinition { + if (!arkts.isMethodDefinition(node)) { + return false; + } + const memoableInfo = collectMemoableInfoInMethod(node); + const { hasMemoEntry, hasMemoIntrinsic } = memoableInfo; + const func = node.scriptFunction; + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(func); + collectScriptFunctionReturnTypeFromInfo(func, returnMemoableInfo); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams( + func, + !hasMemoEntry && !hasMemoIntrinsic + ); + const isMemo = checkIsMemoFromMemoableInfo(memoableInfo); + if (isMemo && !arkts.NodeCache.getInstance().has(node)) { + const metadata = collectMetadataInMethod(node); + arkts.NodeCache.getInstance().collect(node, { + ...metadata, + hasMemoEntry, + hasMemoIntrinsic, + }); + const body = func.body; + if (!!body && arkts.isBlockStatement(body)) { + const disableCollectReturn = hasMemoEntry || hasMemoIntrinsic; + collectMemoScriptFunctionBody( + body, + returnMemoableInfo, + paramMemoableInfoMap, + gensymCount, + disableCollectReturn + ); + } + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.TSTypeParameterInstantiation` node from corresponding call's typeParams. + * + * @param node `arkts.TSTypeParameterInstantiation` node. + */ +export function collectMemoFromTSTypeParameterInstantiation(node: arkts.TSTypeParameterInstantiation): void { + node.params.forEach((t) => { + const typeInfo = collectMemoableInfoInType(t); + if (checkIsMemoFromMemoableInfo(typeInfo)) { + arkts.NodeCache.getInstance().collect(t); + } + }); +} + +/** + * Collect `@memo` annotated `arkts.ETSNewClassInstanceExpression` node from corresponding new class type reference. + * + * @param node `arkts.ETSNewClassInstanceExpression` node. + */ +export function collectMemoFromNewClass(node: arkts.ETSNewClassInstanceExpression): void { + const typeRef = node.getTypeRef; + if (!typeRef || !arkts.isETSTypeReference(typeRef)) { + return; + } + const typeInfo = collectMemoableInfoInTypeReference(typeRef); + if (typeInfo.isWithinTypeParams) { + arkts.NodeCache.getInstance().collect(typeRef, { isWithinTypeParams: true }); + } +} + +/** + * Collect `@memo` annotated `arkts.CallExpression` node from corresponding declared method, + * as well as collect each `@memo` annotated argument from corresponding declared method parameter. + * + * @param node `arkts.CallExpression` node. + */ +export function collectMemoFromCallExpression(node: arkts.CallExpression): void { + if (arkts.NodeCache.getInstance().has(node)) { + return; + } + const typeParams = node.typeParams; + if (!!typeParams) { + collectMemoFromTSTypeParameterInstantiation(typeParams); + } + const expr = findIdentifierFromCallee(node.expression); + const decl = (expr && getDeclResolveAlias(expr)) ?? node.expression; + if (!decl) { + return; + } + let isCollected: boolean = false; + if (arkts.NodeCache.getInstance().has(decl)) { + arkts.NodeCache.getInstance().collect(node); + isCollected = true; + } + if (arkts.isMethodDefinition(decl)) { + isCollected = collectCallWithDeclaredMethod(node, decl); + } else if (arkts.isClassProperty(decl)) { + isCollected = collectCallWithDeclaredClassProperty(node, decl); + } + if (isCollected && arkts.isTSAsExpression(node.expression) && node.expression.typeAnnotation) { + arkts.NodeCache.getInstance().collect(node.expression.typeAnnotation); + } +} + +export function collectCallWithDeclaredClassProperty(node: arkts.CallExpression, decl: arkts.ClassProperty): boolean { + if (arkts.NodeCache.getInstance().has(decl)) { + arkts.NodeCache.getInstance().collect(node); + return true; + } + const memoableInfo = collectMemoableInfoInClassProperty(decl); + if (checkIsMemoFromMemoableInfo(memoableInfo, false) || memoableInfo.hasBuilder || memoableInfo.hasBuilderParam) { + arkts.NodeCache.getInstance().collect(node); + return true; + } + return false; +} + +export function collectCallWithDeclaredMethod(node: arkts.CallExpression, decl: arkts.MethodDefinition): boolean { + const hasReceiver = decl.scriptFunction.hasReceiver; + const params = decl.scriptFunction.params; + const args = node.arguments; + const hasRestParameter = decl.scriptFunction.hasRestParameter; + const isTrailingCall = node.isTrailingCall; + const options = { hasRestParameter, isTrailingCall }; + forEachArgWithParam(args, params, collectCallArgsWithMethodParams, options); + if (arkts.NodeCache.getInstance().has(decl)) { + const { hasMemoEntry, hasMemoIntrinsic } = arkts.NodeCache.getInstance().get(decl)!.metadata ?? {}; + arkts.NodeCache.getInstance().collect(node, { hasReceiver, hasMemoEntry, hasMemoIntrinsic }); + return true; + } else { + const memoableInfo = collectMemoableInfoInScriptFunction(decl.scriptFunction); + if (checkIsMemoFromMemoableInfo(memoableInfo, true)) { + const { hasMemoEntry, hasMemoIntrinsic } = memoableInfo; + arkts.NodeCache.getInstance().collect(node, { hasReceiver, hasMemoEntry, hasMemoIntrinsic }); + return true; + } + } + return false; +} + +export function collectCallArgsWithMethodParams(arg: arkts.Expression | undefined, param: arkts.Expression): void { + if (!arg) { + return; + } + let info: MemoableInfo; + if (arkts.NodeCache.getInstance().has(param)) { + info = { hasMemo: true, hasProperType: true }; + } else { + info = collectMemoableInfoInParameter(param); + } + if (checkIsMemoFromMemoableInfo(info) && arkts.isArrowFunctionExpression(arg)) { + arkts.NodeCache.getInstance().collect(arg); + const func = arg.scriptFunction; + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(func); + collectScriptFunctionReturnTypeFromInfo(func, returnMemoableInfo); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams(func); + const body = func.body; + if (!!body && arkts.isBlockStatement(body)) { + collectMemoScriptFunctionBody(body, returnMemoableInfo, paramMemoableInfoMap, gensymCount); + } + } +} + +export function findIdentifierFromCallee(callee: arkts.AstNode | undefined): arkts.Identifier | undefined { + if (!callee) { + return undefined; + } + if (arkts.isIdentifier(callee)) { + return callee; + } + if (arkts.isMemberExpression(callee)) { + return findIdentifierFromCallee(callee.property); + } + if (arkts.isTSAsExpression(callee)) { + return findIdentifierFromCallee(callee.expr); + } + if (arkts.isTSNonNullExpression(callee)) { + return findIdentifierFromCallee(callee.expr); + } + return undefined; +} + +export function collectMemoScriptFunctionBody( + body: arkts.BlockStatement, + returnMemoableInfo: MemoableInfo, + paramMemoableInfoMap: Map, + gensymCount: number, + disableCollectReturn?: boolean +): void { + const collector = new MemoFunctionCollector(); + body.statements.forEach((st, index) => { + if (index < gensymCount) { + return; + } + if (disableCollectReturn) { + collector.disableCollectReturn(); + } + collector.registerReturnInfo(returnMemoableInfo).registerParamInfoMap(paramMemoableInfoMap).visitor(st); + collector.reset(); + }); +} + +export function collectMetadataInMethod(node: arkts.MethodDefinition): arkts.AstNodeCacheValue['metadata'] { + const callName = node.name.name; + const hasReceiver = node.scriptFunction.hasReceiver; + const isSetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET; + const isGetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; + return { callName, hasReceiver, isSetter, isGetter }; +} + +export function checkIsMemoFromMemoableInfo(info: MemoableInfo, ignoreType: boolean = false): boolean { + return ( + (!!info.hasMemo || !!info.hasMemoIntrinsic || !!info.hasMemoEntry || !!info.hasBuilder) && + (ignoreType || !!info.hasProperType) + ); +} + +export function getDeclResolveAlias(node: arkts.AstNode): arkts.AstNode | undefined { + const decl = arkts.getDecl(node); + if (!!decl && !!decl.parent && arkts.isIdentifier(decl) && arkts.isVariableDeclarator(decl.parent)) { + if (!!decl.parent.initializer && arkts.isIdentifier(decl.parent.initializer)) { + return getDeclResolveAlias(decl.parent.initializer); + } + if (!!decl.parent.initializer && arkts.isMemberExpression(decl.parent.initializer)) { + return getDeclResolveAlias(decl.parent.initializer.property); + } + } + return decl; +} + +export function parametersBlockHasReceiver(params: readonly arkts.Expression[]): boolean { + return params.length > 0 && arkts.isEtsParameterExpression(params[0]) && isThisParam(params[0]); +} + +export function parametrizedNodeHasReceiver(node: arkts.ScriptFunction | arkts.ETSFunctionType | undefined): boolean { + if (node === undefined) { + return false; + } + return parametersBlockHasReceiver(node.params); +} + +function isThisParam(node: arkts.Expression | undefined): boolean { + if (node === undefined || !arkts.isEtsParameterExpression(node)) { + return false; + } + return node.identifier?.isReceiver ?? false; +} diff --git a/arkui-plugins/common/arkts-utils.ts b/arkui-plugins/common/arkts-utils.ts index e62d9c991..ff2e195a8 100644 --- a/arkui-plugins/common/arkts-utils.ts +++ b/arkui-plugins/common/arkts-utils.ts @@ -14,6 +14,37 @@ */ import * as arkts from '@koalaui/libarkts'; +import { DeclarationCollector } from './declaration-collector'; +import { ARKUI_IMPORT_PREFIX_NAMES, DecoratorNames } from './predefines'; + +export function coerceToAstNode(node: arkts.AstNode): T { + return node as T; +} + +/** + * create and insert `import { as } from ` to the top of script's statements. + */ +export function createAndInsertImportDeclaration( + source: arkts.StringLiteral, + imported: arkts.Identifier, + local: arkts.Identifier, + importKind: arkts.Es2pandaImportKinds, + program: arkts.Program +): void { + const importDecl: arkts.ETSImportDeclaration = arkts.factory.createImportDeclaration( + source, + [arkts.factory.createImportSpecifier(imported, local)], + importKind, + program, + arkts.Es2pandaImportFlags.IMPORT_FLAGS_NONE + ); + arkts.importDeclarationInsert(importDecl, program); + return; +} + +export function isNumeric(str: string): boolean { + return /^\d+$/.test(str); +} export function annotation(name: string): arkts.AnnotationUsage { const ident: arkts.Identifier = arkts.factory.createIdentifier(name).setAnnotationUsage(); @@ -29,6 +60,28 @@ export function isAnnotation(node: arkts.AnnotationUsage, annoName: string) { return node.expr !== undefined && arkts.isIdentifier(node.expr) && node.expr.name === annoName; } +export function isDecoratorAnnotation( + anno: arkts.AnnotationUsage, + decoratorName: DecoratorNames, + ignoreDecl?: boolean +): boolean { + if (!(!!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === decoratorName)) { + return false; + } + if (!ignoreDecl) { + const decl = arkts.getDecl(anno.expr); + if (!decl) { + return false; + } + const moduleName: string = arkts.getProgramFromAstNode(decl).moduleName; + if (!moduleName || !matchPrefix(ARKUI_IMPORT_PREFIX_NAMES, moduleName)) { + return false; + } + DeclarationCollector.getInstance().collect(decl); + } + return true; +} + export function removeAnnotationByName( annotations: readonly arkts.AnnotationUsage[], annoName: string @@ -80,24 +133,6 @@ export function matchPrefix(prefixCollection: (string | RegExp)[], name: string) return false; } -export function updateStructMetadata( - structInfo: arkts.StructInfo, - propertyName: string, - properties: string[], - modifiers: arkts.Es2pandaModifierFlags, - hasStateManagementType?: boolean -): arkts.StructInfo { - const metadata: Record = structInfo.metadata ?? {}; - metadata[propertyName] = { - name: propertyName, - properties, - modifiers, - hasStateManagementType, - }; - structInfo.metadata = metadata; - return structInfo; -} - export function moveToFront(arr: T[], idx: number): T[] { if (idx < 0 || idx >= arr.length) { throw new Error(`Index ${idx} is out of bounds for array of length ${arr.length}`); @@ -106,4 +141,40 @@ export function moveToFront(arr: T[], idx: number): T[] { const copy = [...arr]; const [item] = copy.splice(idx, 1); return [item, ...copy]; -} \ No newline at end of file +} + +/** + * Performs the specified action for each argument in a `arkts.CallExpression`'s arguments array + * paired with corresponding parameter from the function declaration node. + * + * @param args An arguments array from a `arkts.CallExpression` node. + * @param params A parameters array from a function declaration node. + * @param callbackFn A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array. + * @param options Additional options field that accepts special conditions of calls and function, used for pairing arguments with parameters. + */ +export function forEachArgWithParam( + args: readonly arkts.Expression[], + params: readonly arkts.Expression[], + callbackFn: (arg: arkts.Expression | undefined, param: arkts.Expression, index?: number) => void, + options?: { isTrailingCall?: boolean; hasReceiver?: boolean; hasRestParameter?: boolean } +): void { + const argLen: number = args.length; + const paramLen: number = params.length; + if (argLen === 0 || paramLen === 0) { + return; + } + const hasRestParam: boolean = !!options?.hasRestParameter; + const isTrailingCall: boolean = !!options?.isTrailingCall; + const maxLen = hasRestParam ? argLen : paramLen; + let index: number = 0; + while (index < maxLen - 1) { + const param = params.at(index) ?? params.at(paramLen - 1)!; + const argument = isTrailingCall && index >= argLen - 1 ? undefined : args.at(index); + callbackFn(argument, param, index); + index++; + } + const lastParam = params.at(paramLen - 1)!; + const lastIndex = isTrailingCall ? argLen - 1 : maxLen - 1; + const lastArg = args.at(lastIndex); + callbackFn(lastArg, lastParam, maxLen - 1); +} diff --git a/arkui-plugins/common/debug.ts b/arkui-plugins/common/debug.ts index f39940e8f..5be9e280c 100644 --- a/arkui-plugins/common/debug.ts +++ b/arkui-plugins/common/debug.ts @@ -19,8 +19,9 @@ import * as arkts from '@koalaui/libarkts'; const isDebugLog: boolean = false; const isDebugDump: boolean = false; const isPerformance: boolean = false; +const enableMemoryTracker: boolean = false; arkts.Performance.getInstance().skip(!isPerformance); - +arkts.Performance.getInstance().enableMemoryTracker(enableMemoryTracker); export function getEnumName(enumType: any, value: number): string | undefined { return enumType[value]; } @@ -46,7 +47,7 @@ export function debugDump( const outputDir: string = cachePath ? path.resolve(currentDirectory, cachePath, modifiedFileName) : path.resolve(currentDirectory, 'dist', 'cache', modifiedFileName); - const filePath: string = path.resolve(outputDir, fileName); + const filePath: string = path.resolve(outputDir, fileName.replaceAll('\/', '_')); if (!fs.existsSync(outputDir)) { mkDir(outputDir); } diff --git a/arkui-plugins/common/declaration-collector.ts b/arkui-plugins/common/declaration-collector.ts new file mode 100644 index 000000000..d65f0d40d --- /dev/null +++ b/arkui-plugins/common/declaration-collector.ts @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as arkts from '@koalaui/libarkts'; +import { IMPORT_SOURCE_MAP_V2, INTERMEDIATE_IMPORT_SOURCE } from './predefines'; +import { ImportCollector } from './import-collector'; + +export class DeclarationCollector { + private fromExternalSourceNameMap: Map; + private fromExternalSourceNodePeerMap: Map; + static instance: DeclarationCollector; + + private constructor() { + this.fromExternalSourceNameMap = new Map(); + this.fromExternalSourceNodePeerMap = new Map(); + } + + static getInstance(): DeclarationCollector { + if (!this.instance) { + this.instance = new DeclarationCollector(); + } + return this.instance; + } + + private collectIntermediateImportSource(symbol: string, declSourceName: string): void { + let sourceName: string; + if (IMPORT_SOURCE_MAP_V2.has(symbol)) { + sourceName = IMPORT_SOURCE_MAP_V2.get(symbol)!; + } else { + sourceName = declSourceName; + } + ImportCollector.getInstance().collectSource(symbol, sourceName); + } + + collect(decl: arkts.AstNode | undefined): void { + if (!decl) { + return; + } + let declName: string | undefined; + if (arkts.isAnnotationDeclaration(decl) && !!decl.expr && arkts.isIdentifier(decl.expr)) { + declName = decl.expr.name; + } else if (arkts.isMethodDefinition(decl)) { + declName = decl.name.name; + } else if (arkts.isIdentifier(decl)) { + declName = decl.name; + } else if (arkts.isClassProperty(decl) && !!decl.key && arkts.isIdentifier(decl.key)) { + declName = decl.key.name; + } else if (arkts.isEtsParameterExpression(decl)) { + declName = decl.identifier.name; + } + if (!declName) { + return; + } + let sourceName: string = arkts.getProgramFromAstNode(decl).moduleName; + this.fromExternalSourceNameMap.set(declName, sourceName); + this.fromExternalSourceNodePeerMap.set(decl.peer, sourceName); + + INTERMEDIATE_IMPORT_SOURCE.get(declName)?.forEach((symbol) => { + this.collectIntermediateImportSource(symbol, sourceName); + }); + } + + findExternalSourceFromName(declName: string): string | undefined { + return this.fromExternalSourceNameMap.get(declName); + } + + findExternalSourceFromNode(decl: arkts.AstNode): string | undefined { + return this.fromExternalSourceNodePeerMap.get(decl.peer); + } + + reset(): void { + this.fromExternalSourceNameMap.clear(); + this.fromExternalSourceNodePeerMap.clear(); + } +} diff --git a/arkui-plugins/common/import-collector.ts b/arkui-plugins/common/import-collector.ts new file mode 100644 index 000000000..935b339af --- /dev/null +++ b/arkui-plugins/common/import-collector.ts @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { createAndInsertImportDeclaration } from './arkts-utils'; + +interface ImportInfo { + imported: string; + local: string; + source: string; + kind: arkts.Es2pandaImportKinds; +} + +function insertImport(importInfo: ImportInfo, program?: arkts.Program): void { + const source: arkts.StringLiteral = arkts.factory.create1StringLiteral(importInfo.source); + const imported: arkts.Identifier = arkts.factory.createIdentifier(importInfo.imported); + const local: arkts.Identifier = arkts.factory.createIdentifier(importInfo.local); + // Insert this import at the top of the script's statements. + if (!program) { + throw Error('Failed to insert import: Transformer has no program'); + } + createAndInsertImportDeclaration(source, imported, local, importInfo.kind, program); +} + +export class ImportCollector { + public importInfos: ImportInfo[]; + public localMap: Map; + public sourceMap: Map; + private static instance: ImportCollector; + + /** this set is used for keeping the import sentence unique */ + private imported: Set; + + private constructor() { + this.importInfos = []; + this.imported = new Set(); + this.localMap = new Map(); + this.sourceMap = new Map(); + } + + static getInstance(): ImportCollector { + if (!this.instance) { + this.instance = new ImportCollector(); + } + return this.instance; + } + + reset(): void { + this.importInfos = []; + this.imported.clear(); + this.localMap.clear(); + this.sourceMap.clear(); + } + + collectSource(imported: string, source: string): void { + if (!this.sourceMap.has(imported)) { + this.sourceMap.set(imported, source); + } + } + + collectImport( + imported: string, + local?: string, + kind: arkts.Es2pandaImportKinds = arkts.Es2pandaImportKinds.IMPORT_KINDS_TYPE + ): void { + if (!this.sourceMap.has(imported)) { + throw new Error(`ImportCollector: import ${imported}'s source haven't been collected yet.`); + } + if (this.imported.has(imported)) { + return; + } + const source: string = this.sourceMap.get(imported)!; + const _local: string = local ?? imported; + this.importInfos.push({ + source, + imported, + local: _local, + kind, + }); + this.localMap.set(imported, _local); + this.imported.add(imported); + } + + getLocal(imported: string): string | undefined { + return this.localMap.get(imported); + } + + insertCurrentImports(program?: arkts.Program): void { + this.importInfos.forEach((importInfo) => { + insertImport(importInfo, program); + }); + } +} diff --git a/arkui-plugins/common/log-collector.ts b/arkui-plugins/common/log-collector.ts new file mode 100644 index 000000000..5fa9ed7af --- /dev/null +++ b/arkui-plugins/common/log-collector.ts @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { LogType } from './predefines'; + +interface LogInfo { + type: LogType; + message: string; + node: arkts.AstNode; + code: string; +} + +export function generateDiagnosticKind(logItem: LogInfo): arkts.DiagnosticKind { + return arkts.DiagnosticKind.create( + `${logItem.code}: ${logItem.message}`, + logItem.type === LogType.ERROR + ? arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_ERROR + : arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_WARNING + ); +} + +export class LogCollector { + public logInfos: LogInfo[]; + private static instance: LogCollector; + private ignoreError: boolean; + + private constructor() { + this.logInfos = []; + this.ignoreError = false; + } + + static getInstance(): LogCollector { + if (!this.instance) { + this.instance = new LogCollector(); + } + return this.instance; + } + + reset(): void { + this.logInfos = []; + this.ignoreError = false; + } + + collectLogInfo(logItem: LogInfo): void { + this.logInfos.push(logItem); + } + + emitLogInfo(): void { + if (this.ignoreError) { + return; + } + this.logInfos.forEach((logItem: LogInfo) => { + arkts.Diagnostic.logDiagnostic(generateDiagnosticKind(logItem), arkts.getStartPosition(logItem.node)); + }); + } + + shouldIgnoreError(ignoreError: boolean | undefined): void { + if (!!ignoreError) { + this.ignoreError = true; + } + } +} diff --git a/arkui-plugins/common/plugin-context.ts b/arkui-plugins/common/plugin-context.ts index 95cce9152..f661350a0 100644 --- a/arkui-plugins/common/plugin-context.ts +++ b/arkui-plugins/common/plugin-context.ts @@ -21,12 +21,14 @@ export class PluginContext { private program: arkts.Program | undefined; private projectConfig: ProjectConfig | undefined; private contextPtr: number | undefined; + private codingFilePath: string | undefined; constructor() { this.ast = undefined; this.program = undefined; this.projectConfig = undefined; this.contextPtr = undefined; + this.codingFilePath = undefined; } /** @@ -58,7 +60,7 @@ export class PluginContext { } public setProjectConfig(projectConfig: ProjectConfig): void { - throw new Error('do not set projectConfig!'); + this.projectConfig = projectConfig; } public getProjectConfig(): ProjectConfig | undefined { @@ -72,12 +74,53 @@ export class PluginContext { public getContextPtr(): number | undefined { return this.contextPtr; } + + public setCodingFilePath(codingFilePath: string): void { + this.codingFilePath = codingFilePath; + } + + public getCodingFilePath(): string | undefined { + return this.codingFilePath; + } + + public isCoding(): boolean { + return this.codingFilePath !== undefined; + } +} + +export interface DependentModuleConfig { + packageName: string; + moduleName: string; + moduleType: string; + modulePath: string; + sourceRoots: string[]; + entryFile: string; + language: string; + declFilesPath?: string; + dependencies?: string[]; } export interface ProjectConfig { bundleName: string; moduleName: string; cachePath: string; + dependentModuleList: DependentModuleConfig[]; + appResource: string; + rawFileResource: string; + buildLoaderJson: string; + hspResourcesMap: boolean; + compileHar: boolean; + byteCodeHar: boolean; + uiTransformOptimization: boolean; + resetBundleName: boolean; + allowEmptyBundleName: boolean; + moduleType: string; + moduleRootPath: string; + aceModuleJsonPath: string; + ignoreError: boolean; + projectPath: string, + projectRootPath: string, + integratedHsp: boolean frameworkMode?: string; } diff --git a/arkui-plugins/common/predefines.ts b/arkui-plugins/common/predefines.ts index 64b90a29b..b180fdddc 100644 --- a/arkui-plugins/common/predefines.ts +++ b/arkui-plugins/common/predefines.ts @@ -16,15 +16,9 @@ export const EXTERNAL_SOURCE_PREFIX_NAMES: (string | RegExp)[] = [ 'std', 'escompat', - 'security', - 'application', - 'permissions', - 'bundleManager', - 'commonEvent', /@arkts\..*/, /@ohos\.(?!arkui).*/, /@system\..*/, - /ability\..*/, ]; export const EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK: (string | RegExp)[] = [ @@ -33,62 +27,364 @@ export const EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK: (string | RegExp)[] = [ /@arkts\..*/ ]; -export const ARKUI_COMPONENT_IMPORT_NAME: string = '@ohos.arkui.component'; -export const ARKUI_STATEMANAGEMENT_IMPORT_NAME: string = '@ohos.arkui.stateManagement'; -export const KIT_ARKUI_NAME: string = '@kit.ArkUI'; +export const ARKUI_IMPORT_PREFIX_NAMES: (string | RegExp)[] = [/arkui\..*/, /@ohos\..*/, /@kit\..*/]; -export const EXTERNAL_SOURCE_ALLOWED_IMPORT_INSERT_NAMES: string[] = [ - ARKUI_COMPONENT_IMPORT_NAME, - ARKUI_STATEMANAGEMENT_IMPORT_NAME, -]; +export const MEMO_IMPORT_SOURCE_NAME: string = 'arkui.stateManagement.runtime'; +export const CUSTOM_COMPONENT_IMPORT_SOURCE_NAME: string = 'arkui.component.customComponent'; +export const CUSTOM_DIALOG_CONTROLLER_SOURCE_NAME: string = 'arkui.component.customDialogController'; +export const ENTRY_POINT_IMPORT_SOURCE_NAME: string = 'arkui.UserView'; +export const ARKUI_COMPONENT_COMMON_SOURCE_NAME: string = 'arkui.component.common'; +export const ARKUI_FOREACH_SOURCE_NAME: string = 'arkui.component.forEach'; +export const ARKUI_BUILDER_SOURCE_NAME: string = 'arkui.component.builder'; + +export enum ModuleType { + HAR = 'har', + ENTRY = 'entry', + FEATURE = 'feature', + SHARED = 'shared', +} + +export enum DefaultConfiguration { + HAR_DEFAULT_MODULE_NAME = '__harDefaultModuleName__', + HAR_DEFAULT_BUNDLE_NAME = '__harDefaultBundleName__', + DYNAMIC_MODULE_NAME = '__MODULE_NAME__', + DYNAMIC_BUNDLE_NAME = '__BUNDLE_NAME__', +} + +export enum LogType { + ERROR = 'ERROR', + WARN = 'WARN', +} + +export enum Dollars { + DOLLAR_RESOURCE = '$r', + DOLLAR_RAWFILE = '$rawfile', + DOLLAR_DOLLAR = '$$', + TRANSFORM_DOLLAR_RESOURCE = '_r', + TRANSFORM_DOLLAR_RAWFILE = '_rawfile', +} -export const IMPORT_SOURCE_MAP: Map> = new Map>([ - ['arkui.stateManagement.runtime', new Set(['memo', '__memo_context_type', '__memo_id_type'])] +export enum BindableDecl { + BINDABLE = 'Bindable', +} + +export enum StructDecoratorNames { + ENTRY = 'Entry', + COMPONENT = 'Component', + COMPONENT_V2 = 'ComponentV2', + RESUABLE = 'Reusable', + RESUABLE_V2 = 'ReusableV2', + CUSTOM_LAYOUT = 'CustomLayout', + CUSTOMDIALOG = 'CustomDialog', +} + +export enum EntryWrapperNames { + ENTRY_FUNC = 'entry', + WRAPPER_CLASS_NAME = '__EntryWrapper', + ENTRY_STORAGE_LOCAL_STORAGE_PROPERTY_NAME = '_entry_local_storage_', + ENTRY_POINT_CLASS_NAME = 'EntryPoint', + REGISTER_NAMED_ROUTER = 'RegisterNamedRouter', + ROUTER_NAME = 'routerName', + INSTANCE = 'instance', + PARAM = 'param' +} + +export enum ObservedNames { + V1_RERENDER_ID = '____V1RenderId', + RERENDER_ID = 'renderId', + SET_V1_RERENDER_ID = 'setV1RenderId', + META = 'meta', + CONDITIONAL_ADD_REF = 'conditionalAddRef', + ADD_REF = 'addRef', + SHOULD_ADD_REF = 'shouldAddRef', + NEW_NAME = 'newName', + PROPERTY_PREFIX = '', + NEW_VALUE = 'newValue', + FIRE_CHANGE = 'fireChange', + EXECUATE_WATCHES = 'executeOnSubscribingWatches', +} + +export enum MonitorNames { + PATH = 'path', + VALUE_CALL_CACK = 'valueCallback', + I_MONITOR = 'IMonitor', + M_PARAM = '_m', +} + +export enum EntryParamNames { + ENTRY_STORAGE = 'storage', + ENTRY_USE_SHARED_STORAGE = 'useSharedStorage', + ENTRY_ROUTE_NAME = 'routeName' +} + +export enum InnerComponentNames { + FOR_EACH = 'ForEach', +} + +export enum DecoratorNames { + STATE = 'State', + STORAGE_LINK = 'StorageLink', + STORAGE_PROP = 'StorageProp', + LINK = 'Link', + PROP = 'Prop', + PROVIDE = 'Provide', + CONSUME = 'Consume', + OBJECT_LINK = 'ObjectLink', + OBSERVED = 'Observed', + OBSERVED_V2 = 'ObservedV2', + WATCH = 'Watch', + BUILDER_PARAM = 'BuilderParam', + BUILDER = 'Builder', + CUSTOM_DIALOG = 'CustomDialog', + LOCAL_STORAGE_PROP = 'LocalStorageProp', + LOCAL_STORAGE_LINK = 'LocalStorageLink', + REUSABLE = 'Reusable', + TRACK = 'Track', + TRACE = 'Trace', + JSONSTRINGIFYIGNORE = 'JSONStringifyIgnore', + JSONRENAME = 'JSONRename', + ANIMATABLE_EXTEND = 'AnimatableExtend', + PROP_REF = 'PropRef', + LOCAL = 'Local', + LOCAL_STORAGE_PROP_REF = 'LocalStoragePropRef', + STORAGE_PROP_REF = 'StoragePropRef', + PARAM = 'Param', + ONCE = 'Once', + PROVIDER = 'Provider', + CONSUMER = 'Consumer', + MONITOR = 'Monitor', + COMPUTED = 'Computed', + EVENT = 'Event', + REQUIRE = 'Require' +} + +export enum TypeNames { + NULLISH_TYPE = 'NullishType', + ANY = 'Any', + ARRAY = 'Array', +} + +export enum DecoratorIntrinsicNames { + LINK = '__Link_intrinsic', +} + +export enum StateManagementTypes { + STATE_MANAGEMENT_FACTORY = 'STATE_MGMT_FACTORY', + STATE_DECORATED = 'IStateDecoratedVariable', + LINK_DECORATED = 'ILinkDecoratedVariable', + LINK_SOURCE_TYPE = 'LinkSourceType', + STORAGE_LINK_DECORATED = 'IStorageLinkDecoratedVariable', + STORAGE_PROP_REF_DECORATED = 'IStoragePropRefDecoratedVariable', + LOCAL_STORAGE_LINK_DECORATED = 'ILocalStorageLinkDecoratedVariable', + PROP_DECORATED = 'IPropDecoratedVariable', + SYNCED_PROPERTY = 'SyncedProperty', + PROVIDE_DECORATED = 'IProvideDecoratedVariable', + CONSUME_DECORATED = 'IConsumeDecoratedVariable', + OBJECT_LINK_DECORATED = 'IObjectLinkDecoratedVariable', + PROP_REF_DECORATED = 'IPropRefDecoratedVariable', + LOCAL_DECORATED = 'ILocalDecoratedVariable', + LOCAL_STORAGE_PROP_REF_DECORATED = 'ILocalStoragePropRefDecoratedVariable', + PARAM_DECORATED = 'IParamDecoratedVariable', + ONCE_DECORATED = 'IParamOnceDecoratedVariable', + PROVIDER_DECORATED = 'IProviderDecoratedVariable', + CONSUMER_DECORATED = 'IConsumerDecoratedVariable', + COMPUTED_DECORATED = 'IComputedDecoratedVariable', + MONITOR_DECORATED = 'IMonitorDecoratedVariable', + MUTABLE_STATE_META = 'IMutableStateMeta', + OBSERVED_OBJECT = 'IObservedObject', + WATCH_ID_TYPE = 'WatchIdType', + RENDER_ID_TYPE = 'RenderIdType', + OBSERVE = 'OBSERVE', + META = '__meta', + SUBSCRIBED_WATCHES = 'ISubscribedWatches', + STORAGE_LINK_STATE = 'StorageLinkState', + OBSERVABLE_PROXY = 'observableProxy', + PROP_STATE = 'propState', + UPDATE = 'update', + MAKE_STATE = 'makeState', + MAKE_LINK = 'makeLink', + MAKE_PROP = 'makeProp', + MAKE_PROP_REF = 'makePropRef', + MAKE_LOCAL = 'makeLocal', + MAKE_STATIC_LOCAL = 'makeStaticLocal', + MAKE_STORAGE_PROP_REF = 'makeStoragePropRef', + MAKE_LOCAL_STORAGE_PROP_REF = 'makeLocalStoragePropRef', + MAKE_STORAGE_LINK = 'makeStorageLink', + MAKE_LOCAL_STORAGE_LINK = 'makeLocalStorageLink', + MAKE_PROVIDE = 'makeProvide', + MAKE_CONSUME = 'makeConsume', + MAKE_OBJECT_LINK = 'makeObjectLink', + MAKE_SUBSCRIBED_WATCHES = 'makeSubscribedWatches', + MAKE_MUTABLESTATE_META = 'makeMutableStateMeta', + MAKE_PARAM = 'makeParam', + MAKE_PARAM_ONCE = 'makeParamOnce', + MAKE_PROVIDER = 'makeProvider', + MAKE_CONSUMER = 'makeConsumer', + MAKE_COMPUTED = 'makeComputed', + MAKE_MONITOR = 'makeMonitor', + UI_UTILS = 'UIUtils', + MAKE_OBSERVED = 'makeObserved', +} + +export enum AnimationNames { + ANIMATABLE_ARITHMETIC = 'AnimatableArithmetic', + CREATE_OR_SET_ANIMATABLEPROPERTY = '__createOrSetAnimatableProperty', + ANIMATION = 'animation', + ANIMATION_START = 'animationStart', + ANIMATION_STOP = 'animationStop', +} + +export enum NavigationNames { + NAVINTERFACE = 'NavInterface', + BUNDLE_NAME = 'bundleName', + MODULE_NAME = 'moduleName', + PAGE_PATH = 'pagePath', + PAGE_FULL_PATH = 'pageFullPath', + INTEGRATED_HSP = 'integratedHsp', +} + +export enum ConditionNames { + CONDITION_SCOPE = 'ConditionScope', + CONDITION_BRANCH = 'ConditionBranch', +} + +export enum ArkTsDefaultNames { + DEFAULT_STATIC_BLOCK_NAME = '', +} + +export const RESOURCE_TYPE: Record = { + color: 10001, + float: 10002, + string: 10003, + plural: 10004, + boolean: 10005, + intarray: 10006, + integer: 10007, + pattern: 10008, + strarray: 10009, + media: 20000, + rawfile: 30000, + symbol: 40000, +}; + +export const DECORATOR_TYPE_MAP = new Map([ + [DecoratorNames.STATE, StateManagementTypes.STATE_DECORATED], + [DecoratorNames.LINK, StateManagementTypes.LINK_SOURCE_TYPE], + [DecoratorNames.PROP, StateManagementTypes.PROP_DECORATED], + [DecoratorNames.PROP_REF, StateManagementTypes.PROP_REF_DECORATED], + [DecoratorNames.STORAGE_LINK, StateManagementTypes.STORAGE_LINK_DECORATED], + [DecoratorNames.STORAGE_PROP, StateManagementTypes.STORAGE_PROP_REF_DECORATED], + [DecoratorNames.STORAGE_PROP_REF, StateManagementTypes.STORAGE_PROP_REF_DECORATED], + [DecoratorNames.LOCAL_STORAGE_PROP_REF, StateManagementTypes.LOCAL_STORAGE_PROP_REF_DECORATED], + [DecoratorNames.LOCAL_STORAGE_PROP, StateManagementTypes.SYNCED_PROPERTY], + [DecoratorNames.LOCAL_STORAGE_LINK, StateManagementTypes.LOCAL_STORAGE_LINK_DECORATED], + [DecoratorNames.OBJECT_LINK, StateManagementTypes.OBJECT_LINK_DECORATED], + [DecoratorNames.PROVIDE, StateManagementTypes.PROVIDE_DECORATED], + [DecoratorNames.CONSUME, StateManagementTypes.CONSUME_DECORATED], + [DecoratorNames.LOCAL, StateManagementTypes.LOCAL_DECORATED], + [DecoratorNames.PARAM, StateManagementTypes.PARAM_DECORATED], + [DecoratorNames.ONCE, StateManagementTypes.ONCE_DECORATED], + [DecoratorNames.PROVIDER, StateManagementTypes.PROVIDER_DECORATED], + [DecoratorNames.CONSUMER, StateManagementTypes.CONSUMER_DECORATED], ]); -export const OUTPUT_DEPENDENCY_MAP: Map = new Map([ - ['$r', ['_r']], - ['$rawfile', ['_rawfile']], - ['State', ['StateDecoratedVariable']], - ['Link', ['LinkDecoratedVariable', 'DecoratedV1VariableBase']], - ['Prop', ['PropDecoratedVariable']], - ['Provide', ['ProvideDecoratedVariable']], - ['Consume', ['ConsumeDecoratedVariable']], - ['StorageProp', ['StoragePropDecoratedVariable']], - ['StorageLink', ['StorageLinkDecoratedVariable']], - ['LocalStorageLink', ['StorageLinkState', 'MutableState', 'observableProxy']], - ['LocalStorageProp', ['StorageLinkState', 'SyncedProperty', 'observableProxy', 'propState']], - ['ObjectLink', ['ObjectLinkDecoratedVariable']], - ['Observed', ['MutableStateMeta', 'BackingValue', 'setObservationDepth', 'IObservedObject', 'int32', 'WatchIdType', 'SubscribedWatches']], - ['Track', ['MutableStateMeta', 'BackingValue', 'setObservationDepth', 'IObservedObject', 'int32', 'WatchIdType', 'SubscribedWatches']], - ['$$', ['Bindable']], +export const INTERMEDIATE_IMPORT_SOURCE: Map = new Map([ + [Dollars.DOLLAR_RESOURCE, [Dollars.TRANSFORM_DOLLAR_RESOURCE]], + [Dollars.DOLLAR_RAWFILE, [Dollars.TRANSFORM_DOLLAR_RAWFILE]], + [Dollars.DOLLAR_DOLLAR, [BindableDecl.BINDABLE]], + [DecoratorNames.STATE, [StateManagementTypes.STATE_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.LINK, [StateManagementTypes.LINK_DECORATED, StateManagementTypes.LINK_SOURCE_TYPE, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.PROP, [StateManagementTypes.PROP_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.PROP_REF, [StateManagementTypes.PROP_REF_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.PROVIDE, [StateManagementTypes.PROVIDE_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.CONSUME, [StateManagementTypes.CONSUME_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.STORAGE_PROP, [StateManagementTypes.STORAGE_PROP_REF_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.STORAGE_PROP_REF, [StateManagementTypes.STORAGE_PROP_REF_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.LOCAL_STORAGE_PROP_REF, [StateManagementTypes.LOCAL_STORAGE_PROP_REF_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.STORAGE_LINK, [StateManagementTypes.STORAGE_LINK_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.OBJECT_LINK, [StateManagementTypes.OBJECT_LINK_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.LOCAL_STORAGE_LINK, [StateManagementTypes.LOCAL_STORAGE_LINK_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.LOCAL, [StateManagementTypes.LOCAL_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.PARAM, [StateManagementTypes.PARAM_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.ONCE, [StateManagementTypes.ONCE_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.PROVIDER, [StateManagementTypes.PROVIDER_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.CONSUMER, [StateManagementTypes.CONSUMER_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.COMPUTED, [StateManagementTypes.COMPUTED_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.MONITOR, [StateManagementTypes.MONITOR_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [ + DecoratorNames.LOCAL_STORAGE_PROP, + [ + StateManagementTypes.STORAGE_LINK_STATE, + StateManagementTypes.SYNCED_PROPERTY, + StateManagementTypes.OBSERVABLE_PROXY, + StateManagementTypes.PROP_STATE, + ], + ], + [ + DecoratorNames.OBSERVED, + [ + StateManagementTypes.MUTABLE_STATE_META, + StateManagementTypes.OBSERVED_OBJECT, + StateManagementTypes.WATCH_ID_TYPE, + StateManagementTypes.RENDER_ID_TYPE, + StateManagementTypes.OBSERVE, + StateManagementTypes.SUBSCRIBED_WATCHES, + StateManagementTypes.STATE_MANAGEMENT_FACTORY + ], + ], + [ + DecoratorNames.TRACK, + [ + StateManagementTypes.MUTABLE_STATE_META, + StateManagementTypes.OBSERVED_OBJECT, + StateManagementTypes.WATCH_ID_TYPE, + StateManagementTypes.RENDER_ID_TYPE, + StateManagementTypes.OBSERVE, + StateManagementTypes.SUBSCRIBED_WATCHES, + StateManagementTypes.STATE_MANAGEMENT_FACTORY + ], + ], + [ + DecoratorNames.OBSERVED_V2, + [ + StateManagementTypes.MUTABLE_STATE_META, + StateManagementTypes.OBSERVED_OBJECT, + StateManagementTypes.WATCH_ID_TYPE, + StateManagementTypes.RENDER_ID_TYPE, + StateManagementTypes.SUBSCRIBED_WATCHES, + StateManagementTypes.STATE_MANAGEMENT_FACTORY + ], + ], + [ + DecoratorNames.TRACE, + [ + StateManagementTypes.STATE_MANAGEMENT_FACTORY, + StateManagementTypes.UI_UTILS + ], + ], + [DecoratorNames.ANIMATABLE_EXTEND, [AnimationNames.ANIMATABLE_ARITHMETIC]] ]); +/** + * @deprecated + */ +export const IMPORT_SOURCE_MAP_V2: Map = new Map([ + [Dollars.TRANSFORM_DOLLAR_RESOURCE, 'arkui.component.resources'], + [Dollars.TRANSFORM_DOLLAR_RAWFILE, 'arkui.component.resources'], + [StateManagementTypes.SYNCED_PROPERTY, 'arkui.stateManagement.runtime'], + [StateManagementTypes.STORAGE_LINK_STATE, 'arkui.stateManagement.runtime'], + [StateManagementTypes.OBSERVABLE_PROXY, 'arkui.stateManagement.runtime'], + [StateManagementTypes.PROP_STATE, 'arkui.stateManagement.runtime'], + [StateManagementTypes.UI_UTILS, 'arkui.stateManagement.utils'], + [AnimationNames.ANIMATABLE_ARITHMETIC, 'arkui.component.common'] +]); -export enum InteroperAbilityNames { - ARKTS_1_1 = '1.1', - ARKTS_1_2 = '1.2', - ARKUICOMPATIBLE = 'ArkUICompatible', - ESVALUE = 'ESValue', - ELMTID = 'elmtId', - INITEMPTYOBJECT = 'instantiateEmptyObject', - SETPROPERTY = 'setProperty', - NUMBER = 'number', - PARENT = 'parent', - INSTANCE = 'instance', - PARAM = 'param', - EXTRAINFO = 'extraInfo', - COMPONENT = 'component', - GETPROPERTY = 'getProperty', - CONSTRUCTOR = 'constructor', - MODULE = 'module', - LOAD = 'load', - STRUCTOBJECT = 'structObject', - INSTANTIATE = 'instantiate', - WRAP = 'wrap', - WRAPINT = 'wrapInt', - WRAPSTRING = 'wrapString', - PARAMSLAMBDA = 'paramsLambda', - INTEROPCOMPONENT = 'interopComponent', - OHMURL = '@normalized:N&entry&com.example.Interop2use1&har1/src/main/ets/components/MainPage&1.0.0', +export enum GetSetTypes { + GET = 'get', + SET = 'set', } + +export enum GenSymPrefix { + INTRINSIC = 'gensym%%', + UI = 'gensym__' +} \ No newline at end of file diff --git a/arkui-plugins/common/program-visitor.ts b/arkui-plugins/common/program-visitor.ts index 9281c0b78..55ac1aad4 100644 --- a/arkui-plugins/common/program-visitor.ts +++ b/arkui-plugins/common/program-visitor.ts @@ -17,9 +17,9 @@ import * as arkts from '@koalaui/libarkts'; import { AbstractVisitor, VisitorOptions } from './abstract-visitor'; import { matchPrefix } from './arkts-utils'; import { debugDump, getDumpFileName } from './debug'; -import { InteroperAbilityNames, ARKUI_COMPONENT_IMPORT_NAME, KIT_ARKUI_NAME } from './predefines'; +import { InteroperAbilityNames } from '../ui-plugins/interop/predefines'; import { PluginContext } from './plugin-context'; -import { LegacyTransformer } from '../ui-plugins/legacy-transformer'; +import { LegacyTransformer } from '../ui-plugins/interop/legacy-transformer'; import { ComponentTransformer } from '../ui-plugins/component-transformer'; export interface ProgramVisitorOptions extends VisitorOptions { @@ -59,28 +59,9 @@ function flattenVisitorsInHooks( ]; } -function sortExternalSources(externalSources: arkts.ExternalSource[]): arkts.ExternalSource[] { - return externalSources.sort((a, b) => { - const prefix = ARKUI_COMPONENT_IMPORT_NAME || KIT_ARKUI_NAME; - const hasPrefixA = a.getName().startsWith(prefix); - const hasPrefixB = b.getName().startsWith(prefix); - - // If both have the prefix, maintain their original order - if (hasPrefixA && hasPrefixB) { - return 0; - } - // If neither has the prefix, maintain their original order - if (!hasPrefixA && !hasPrefixB) { - return 0; - } - // If only one has the prefix, the one with the prefix comes first - return hasPrefixA ? -1 : 1; - }); -} - export interface StructMap { [key: string]: string; -} +} export class ProgramVisitor extends AbstractVisitor { private readonly pluginName: string; @@ -113,7 +94,7 @@ export class ProgramVisitor extends AbstractVisitor { this.legacyModuleList = []; } - getLegacyModule(): void { + private getLegacyModule(): void { const moduleList = this.pluginContext?.getProjectConfig()?.dependentModuleList; if (moduleList === undefined) { return; @@ -125,143 +106,158 @@ export class ProgramVisitor extends AbstractVisitor { continue; } if (!this.legacyStructMap.has(moduleName)) { - this.legacyStructMap.set(moduleName, {}); + this.legacyStructMap.set(moduleName, {}); this.legacyModuleList.push(moduleName); } } } - dumpHeaders( - currProgram: arkts.Program, + private dumpExternalSource( + script: arkts.AstNode, name: string, cachePath: string | undefined, prefixName: string, extensionName: string ): void { debugDump( - currProgram.astNode.dumpSrc(), - getDumpFileName(this.state, prefixName, undefined, name), + script.dumpSrc(), + getDumpFileName(this.state, prefixName, undefined, name), true, cachePath, extensionName ); } - programVisitor(program: arkts.Program): arkts.Program { - const skipPrefixes: (string | RegExp)[] = this.skipPrefixNames; + private visitLegacyInExternalSource(currProgram: arkts.Program, name: string): void { + if (this.state === arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED) { + const structList = this.visitorLegacy(currProgram.astNode, currProgram, name); + const moduleName = name.split('/')[0]; + const structMap = this.legacyStructMap.get(moduleName)!; + for (const struct of structList) { + structMap[struct] = name; + } + } + } + + private visitNonLegacyInExternalSource( + program: arkts.Program, + currProgram: arkts.Program, + name: string, + cachePath?: string + ): void { + const extensionName: string = program.fileNameWithExtension; + this.dumpExternalSource(currProgram.astNode, name, cachePath, 'ORI', extensionName); + const script = this.visitor(currProgram.astNode, currProgram, name); + if (script) { + this.dumpExternalSource(script, name, cachePath, this.pluginName, extensionName); + } + } + + private visitNextProgramInQueue( + queue: arkts.Program[], + visited: Set, + externalSource: arkts.ExternalSource + ): void { + const nextProgramArr: arkts.Program[] = externalSource.programs ?? []; + for (const nextProgram of nextProgramArr) { + this.filenames.set(nextProgram.peer, externalSource.getName()); + if (!visited.has(nextProgram.peer)) { + queue.push(nextProgram); + } + } + } + + private visitExternalSources( + program: arkts.Program, + programQueue: arkts.Program[] + ): void { const visited = new Set(); - const queue: arkts.Program[] = [program]; + const queue: arkts.Program[] = programQueue; this.getLegacyModule(); while (queue.length > 0) { const currProgram = queue.shift()!; - if (visited.has(currProgram.peer)) { + if (visited.has(currProgram.peer) || currProgram.isASTLowered()) { continue; } if (currProgram.peer !== program.peer) { const name: string = this.filenames.get(currProgram.peer)!; const cachePath: string | undefined = this.pluginContext?.getProjectConfig()?.cachePath; if (this.legacyModuleList && matchPrefix(this.legacyModuleList, name)) { - if (this.state === arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED) { - const structList = this.visitorLegacy(currProgram.astNode, currProgram, name); - const moduleName = name.split('/')[0]; - const structMap = this.legacyStructMap.get(moduleName)!; - for (const struct of structList) { - structMap[struct] = name; - } - } + this.visitLegacyInExternalSource(currProgram, name); } else { - this.dumpHeaders(currProgram, name, cachePath, 'Ori', program.programFileNameWithExtension); - const script = this.visitor(currProgram.astNode, currProgram, name); - if (script) { - this.dumpHeaders(currProgram, name, cachePath, this.pluginName, program.programFileNameWithExtension); - } + this.visitNonLegacyInExternalSource(program, currProgram, name, cachePath); } } visited.add(currProgram.peer); - for (const externalSource of sortExternalSources(currProgram.externalSources)) { - if (matchPrefix(skipPrefixes, externalSource.getName())) { + for (const externalSource of currProgram.externalSources) { + if (matchPrefix(this.skipPrefixNames, externalSource.getName())) { continue; } - const nextProgramArr: arkts.Program[] = externalSource.programs ?? []; - for (const nextProgram of nextProgramArr) { - this.filenames.set(nextProgram.peer, externalSource.getName()); - if (!visited.has(nextProgram.peer)) { - queue.push(nextProgram); - } - } + this.visitNextProgramInQueue(queue, visited, externalSource); } } - const hasLegacy = this.legacyStructMap.size ? true : false; + } + + programVisitor(program: arkts.Program): arkts.Program { + this.visitExternalSources(program, [program]); + let programScript = program.astNode; - programScript = this.visitor(programScript, program, this.externalSourceName, hasLegacy); + programScript = this.visitor(programScript, program, this.externalSourceName); + const visitorsToReset = flattenVisitorsInHooks(this.hooks, this.state); visitorsToReset.forEach((visitor) => visitor.reset()); + return program; } - preVisitor( + private preVisitor( + hook: ProgramHookLifeCycle | undefined, node: arkts.AstNode, program?: arkts.Program, externalSourceName?: string ): void { - const isExternal: boolean = !!externalSourceName; - let hook: ProgramHookLifeCycle | undefined = isExternal ? this.hooks?.external : this.hooks?.source; let script: arkts.EtsScript = node as arkts.EtsScript; - const preVisitors = hook?.pre?.visitors ?? []; for (const transformer of preVisitors) { - transformer.isExternal = isExternal; - transformer.externalSourceName = externalSourceName; - transformer.program = program; - transformer.visitor(script); + this.visitTransformer(transformer, script, externalSourceName, program); if (!this.hooks?.external?.pre?.resetAfter) { transformer.reset(); } } } - postVisitor( + private postVisitor( + hook: ProgramHookLifeCycle | undefined, node: arkts.AstNode, program?: arkts.Program, externalSourceName?: string ): void { - const isExternal: boolean = !!externalSourceName; - let hook: ProgramHookLifeCycle | undefined = isExternal ? this.hooks?.external : this.hooks?.source; let script: arkts.EtsScript = node as arkts.EtsScript; - const postVisitors = hook?.post?.visitors ?? []; for (const transformer of postVisitors) { - transformer.isExternal = isExternal; - transformer.externalSourceName = externalSourceName; - transformer.program = program; - transformer.visitor(script); + this.visitTransformer(transformer, script, externalSourceName, program); if (!this.hooks?.external?.pre?.resetAfter) { transformer.reset(); } } } - visitor( - node: arkts.AstNode, - program?: arkts.Program, - externalSourceName?: string, - hasLegacy: boolean = false - ): arkts.EtsScript { + visitor(node: arkts.AstNode, program?: arkts.Program, externalSourceName?: string): arkts.EtsScript { + let hook: ProgramHookLifeCycle | undefined; + let script: arkts.EtsScript = node as arkts.EtsScript; let count: number = 0; const isExternal: boolean = !!externalSourceName; // pre-run visitors - this.preVisitor(node, program, externalSourceName); + hook = isExternal ? this.hooks?.external : this.hooks?.source; + this.preVisitor(hook, node, program, externalSourceName); for (const transformer of this.visitors) { - transformer.isExternal = isExternal; - transformer.externalSourceName = externalSourceName; - transformer.program = program; - if (hasLegacy && transformer instanceof ComponentTransformer) { + if (this.legacyStructMap.size > 0 && transformer instanceof ComponentTransformer) { transformer.registerMap(this.legacyStructMap); } - script = transformer.visitor(script) as arkts.EtsScript; + this.visitTransformer(transformer, script, externalSourceName, program); transformer.reset(); arkts.setAllParents(script); if (!transformer.isExternal) { @@ -270,26 +266,38 @@ export class ProgramVisitor extends AbstractVisitor { getDumpFileName(this.state, this.pluginName, count, transformer.constructor.name), true, this.pluginContext?.getProjectConfig()?.cachePath, - program!.programFileNameWithExtension + program!.fileNameWithExtension ); count += 1; } } // post-run visitors - this.postVisitor(node, program, externalSourceName); - + hook = isExternal ? this.hooks?.external : this.hooks?.source; + this.postVisitor(hook, node, program, externalSourceName); return script; } - visitorLegacy( - node: arkts.AstNode, - program?: arkts.Program, - externalSourceName?: string, - ): string[] { - const visitor = new LegacyTransformer(); - const script = visitor.visitor(node) as arkts.EtsScript; - const structList = visitor.getList(); + private visitorLegacy(node: arkts.AstNode, program?: arkts.Program, externalSourceName?: string): string[] { + const transformer = new LegacyTransformer(); + transformer.isExternal = !!externalSourceName; + transformer.externalSourceName = externalSourceName; + transformer.program = program; + transformer.visitor(node); + const structList = transformer.getList(); return structList; } + + private visitTransformer( + transformer: AbstractVisitor, + script: arkts.EtsScript, + externalSourceName?: string, + program?: arkts.Program + ): arkts.EtsScript { + transformer.isExternal = !!externalSourceName; + transformer.externalSourceName = externalSourceName; + transformer.program = program; + const newScript = transformer.visitor(script) as arkts.EtsScript; + return newScript; + } } diff --git a/arkui-plugins/common/safe-types.ts b/arkui-plugins/common/safe-types.ts new file mode 100644 index 000000000..87345cf19 --- /dev/null +++ b/arkui-plugins/common/safe-types.ts @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +export type PartialExcept = Partial & Pick; + +type PartialArray = T extends readonly any[] | any[] ? T | undefined : T; +type PartialAstNode = T extends arkts.AstNode ? T | undefined : T; +type PartialObject = T extends object ? { [P in keyof T]?: T[P] } : T; +type PartialPrimitive = T; + +export type PartialNested = { + [P in keyof T]?: T[P] extends readonly any[] | any[] + ? PartialArray + : T[P] extends arkts.AstNode + ? PartialAstNode + : T[P] extends object + ? PartialObject + : PartialPrimitive; +}; + +type NestedKey = { + [P in keyof T]: P extends K ? T[P] : T[P] extends object ? NestedKey : T[P]; + }; + +export type PickNested = { + [P in keyof T]: P extends K ? T[P] : T[P] extends object ? NestedKey : T[P]; +}; + +export type PartialNestedExcept = PartialNested> & PickNested; + +export type AstNodePointer = arkts.AstNode['peer']; \ No newline at end of file diff --git a/arkui-plugins/interop-plugins/decl_transformer.ts b/arkui-plugins/interop-plugins/decl_transformer.ts index 58d5a6a6d..32d6a610e 100644 --- a/arkui-plugins/interop-plugins/decl_transformer.ts +++ b/arkui-plugins/interop-plugins/decl_transformer.ts @@ -69,6 +69,8 @@ export class DeclTransformer extends AbstractVisitor { return this.transformMethodDefinition(astNode); } return astNode; + } else if (arkts.isClassProperty(astNode)) { + astNode.setAnnotations([]); } return node; } @@ -119,7 +121,7 @@ export class DeclTransformer extends AbstractVisitor { } updateImportDeclaration(astNode: arkts.AstNode):arkts.AstNode { - if (!arkts.isETSImportDeclaration(astNode) || astNode?.source?.str !== '@ohos.arkui.component') { + if (!arkts.isETSImportDeclaration(astNode) || (astNode?.source?.str !== '@ohos.arkui.component' && astNode?.source?.str !== '@ohos.arkui.stateManagement')) { return astNode; } astNode.specifiers.forEach((element) => { diff --git a/arkui-plugins/interop-plugins/index.ts b/arkui-plugins/interop-plugins/index.ts index 841f74e11..dc69677c9 100644 --- a/arkui-plugins/interop-plugins/index.ts +++ b/arkui-plugins/interop-plugins/index.ts @@ -88,7 +88,6 @@ function checkedTransform(this: PluginContext): arkts.EtsScript | undefined { program = programVisitor.programVisitor(program); script = program.astNode; - arkts.GlobalInfo.getInfoInstance()?.reset(); arkts.recheckSubtree(script); this.setArkTSAst(script); debugLog('interopTransform:checked exit'); diff --git a/arkui-plugins/jest-test.config.js b/arkui-plugins/jest-test.config.js index 541b1dc39..cbca1f13e 100644 --- a/arkui-plugins/jest-test.config.js +++ b/arkui-plugins/jest-test.config.js @@ -19,6 +19,7 @@ const rootPath = path.resolve(__dirname, '../../../'); const sdkPath = path.resolve(rootPath, './out/sdk/ohos-sdk/linux/ets/ets1.2'); const pandaSdkPath = path.resolve(sdkPath, './build-tools/ets2panda'); const apiPath = path.resolve(sdkPath, './api'); +const kitPath = path.resolve(sdkPath, './kits'); module.exports = { testEnvironment: 'node', @@ -29,6 +30,7 @@ module.exports = { moduleFileExtensions: ['ts', 'js', 'json', 'node'], coverageDirectory: './test/report', collectCoverageFrom: [ + 'collectors/**', 'common/**', 'memo-plugins/**', 'ui-plugins/**' @@ -45,12 +47,13 @@ module.exports = { 'ui-plugins/printer-transformer.ts', 'ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts', 'ui-plugins/entry-translators/entry-transformer.ts', - 'ui-plugins/struct-translators/struct-transformer.ts' + 'ui-plugins/struct-translators/struct-transformer.ts', ], verbose: true, globals: { SDK_PATH: sdkPath, PANDA_SDK_PATH: pandaSdkPath, API_PATH: apiPath, + KIT_PATH: kitPath, }, }; diff --git a/arkui-plugins/memo-plugins/function-transformer.ts b/arkui-plugins/memo-plugins/function-transformer.ts index 68bc9083d..4f2c55f1f 100644 --- a/arkui-plugins/memo-plugins/function-transformer.ts +++ b/arkui-plugins/memo-plugins/function-transformer.ts @@ -48,13 +48,14 @@ import { isStandaloneArrowFunction, isThisAttributeAssignment, removeMemoAnnotation, - parametrizedNodeHasReceiver + parametrizedNodeHasReceiver, } from './utils'; import { ParameterTransformer } from './parameter-transformer'; import { ReturnTransformer } from './return-transformer'; import { SignatureTransformer } from './signature-transformer'; import { moveToFront } from '../common/arkts-utils'; import { InternalsTransformer } from './internal-transformer'; +import { CachedMetadata, rewriteByType } from './memo-cache-factory'; interface ScopeInfo extends MemoInfo { regardAsSameScope?: boolean; @@ -66,6 +67,7 @@ export interface FunctionTransformerOptions extends VisitorOptions { returnTransformer: ReturnTransformer; signatureTransformer: SignatureTransformer; internalsTransformer?: InternalsTransformer; + useCache?: boolean; } export class FunctionTransformer extends AbstractVisitor { @@ -74,6 +76,7 @@ export class FunctionTransformer extends AbstractVisitor { private readonly returnTransformer: ReturnTransformer; private readonly signatureTransformer: SignatureTransformer; private readonly internalsTransformer?: InternalsTransformer; + private readonly useCache: boolean; /* Tracking whether should import `__memo_context_type` and `__memo_id_type` */ private modified = false; @@ -85,6 +88,7 @@ export class FunctionTransformer extends AbstractVisitor { this.returnTransformer = options.returnTransformer; this.signatureTransformer = options.signatureTransformer; this.internalsTransformer = options.internalsTransformer; + this.useCache = !!options.useCache; } private scopes: ScopeInfo[] = []; @@ -380,12 +384,13 @@ export class FunctionTransformer extends AbstractVisitor { this.signatureTransformer.visitor(node.expression.scriptFunction) ); } + const that = this; const updatedArguments: arkts.AstNode[] = node.arguments.map((it) => { if (arkts.isArrowFunctionExpression(it) && isMemoArrowFunction(it)) { - this.enterAnonymousScope(it.scriptFunction); - const res = this.updateScriptFunction(it.scriptFunction); - this.exitAnonymousScope(); - this.modified = true; + that.enterAnonymousScope(it.scriptFunction); + const res = that.updateScriptFunction(it.scriptFunction); + that.exitAnonymousScope(); + that.modified = true; return arkts.factory.updateArrowFunction(it, res); } return it; @@ -676,7 +681,26 @@ export class FunctionTransformer extends AbstractVisitor { ); } + private visitorWithCache(beforeChildren: arkts.AstNode): arkts.AstNode { + const node = this.visitEachChild(beforeChildren); + if (arkts.NodeCache.getInstance().has(node)) { + const value = arkts.NodeCache.getInstance().get(node)!; + if (rewriteByType.has(value.type)) { + this.modified = true; + const metadata: CachedMetadata = { ...value.metadata, internalsTransformer: this.internalsTransformer }; + return rewriteByType.get(value.type)!(node, metadata); + } + } + if (arkts.isEtsScript(node) && this.modified) { + factory.createContextTypesImportDeclaration(this.program); + } + return node; + } + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { + if (this.useCache) { + return this.visitorWithCache(beforeChildren); + } this.enter(beforeChildren); const node = this.visitEachChild(beforeChildren); this.exit(beforeChildren); diff --git a/arkui-plugins/memo-plugins/index.ts b/arkui-plugins/memo-plugins/index.ts index beb7edff9..5c19bc841 100644 --- a/arkui-plugins/memo-plugins/index.ts +++ b/arkui-plugins/memo-plugins/index.ts @@ -37,20 +37,22 @@ export function unmemoizeTransform(): Plugins { function checkedTransform(this: PluginContext): arkts.EtsScript | undefined { console.log('[MEMO PLUGIN] AFTER CHECKED ENTER'); - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + arkts.Performance.getInstance().memoryTrackerReset(); + arkts.Performance.getInstance().startMemRecord('Node:UIPlugin:Memo-AfterCheck'); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; let script = program.astNode; debugLog('[BEFORE MEMO SCRIPT] script: ', script.dumpSrc()); const cachePath: string | undefined = this.getProjectConfig()?.cachePath; const isFrameworkMode = !!this.getProjectConfig()?.frameworkMode; - const canSkipPhases = false; + const canSkipPhases = !isFrameworkMode && program.canSkipPhases(); debugDump( script.dumpSrc(), getDumpFileName(0, 'SRC', 5, 'MEMO_AfterCheck_Begin'), true, cachePath, - program.programFileNameWithExtension + program.fileNameWithExtension ); arkts.Performance.getInstance().createEvent('memo-checked'); program = checkedProgramVisit(program, this, canSkipPhases, isFrameworkMode); @@ -62,13 +64,20 @@ function checkedTransform(this: PluginContext): arkts.EtsScript | undefined { getDumpFileName(0, 'SRC', 6, 'MEMO_AfterCheck_End'), true, cachePath, - program.programFileNameWithExtension + program.fileNameWithExtension ); + + arkts.Performance.getInstance().memoryTrackerGetDelta('UIPlugin:Memo-AfterCheck'); + arkts.Performance.getInstance().memoryTrackerReset(); + arkts.Performance.getInstance().stopMemRecord('Node:UIPlugin:Memo-AfterCheck'); + arkts.Performance.getInstance().startMemRecord('Node:ArkTS:Recheck'); arkts.Performance.getInstance().createEvent('memo-recheck'); arkts.recheckSubtree(script); arkts.Performance.getInstance().stopEvent('memo-recheck', true); - arkts.Performance.getInstance().clearAllEvents(); this.setArkTSAst(script); + arkts.Performance.getInstance().memoryTrackerGetDelta('ArkTS:Recheck'); + arkts.Performance.getInstance().stopMemRecord('Node:ArkTS:Recheck'); + arkts.Performance.getInstance().memoryTrackerPrintCurrent('UIPlugin:End'); console.log('[MEMO PLUGIN] AFTER CHECKED EXIT'); return script; } @@ -83,22 +92,24 @@ function checkedProgramVisit( isFrameworkMode: boolean = false ): arkts.Program { if (canSkipPhases) { - debugLog('[SKIP PHASE] phase: memo-checked, moduleName: ', program?.moduleName); + debugLog('[SKIP PHASE] phase: memo-checked, moduleName: ', program.moduleName); } else { - debugLog('[CANT SKIP PHASE] phase: memo-checked, moduleName: ', program?.moduleName); + debugLog('[CANT SKIP PHASE] phase: memo-checked, moduleName: ', program.moduleName); const positionalIdTracker = new PositionalIdTracker(arkts.getFileName(), false); - const parameterTransformer = new ParameterTransformer({ - positionalIdTracker, - }); + const parameterTransformer = new ParameterTransformer({ positionalIdTracker }); const returnTransformer = new ReturnTransformer(); const signatureTransformer = new SignatureTransformer(); - const internalsTransformer = new InternalsTransformer({ positionalIdTracker }); + let internalsTransformer: InternalsTransformer | undefined; + if (isFrameworkMode) { + internalsTransformer = new InternalsTransformer({ positionalIdTracker }); + } const functionTransformer = new FunctionTransformer({ positionalIdTracker, parameterTransformer, returnTransformer, signatureTransformer, - internalsTransformer + internalsTransformer, + useCache: arkts.NodeCache.getInstance().isCollected(), }); const skipPrefixNames = isFrameworkMode ? EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK @@ -111,6 +122,7 @@ function checkedProgramVisit( pluginContext, }); program = programVisitor.programVisitor(program); + arkts.NodeCache.getInstance().clear(); } return program; -} \ No newline at end of file +} diff --git a/arkui-plugins/memo-plugins/internal-transformer.ts b/arkui-plugins/memo-plugins/internal-transformer.ts index 7419778b2..3541fb7c3 100644 --- a/arkui-plugins/memo-plugins/internal-transformer.ts +++ b/arkui-plugins/memo-plugins/internal-transformer.ts @@ -46,4 +46,4 @@ export class InternalsTransformer extends AbstractVisitor { } return node; } -} \ No newline at end of file +} diff --git a/arkui-plugins/memo-plugins/memo-cache-factory.ts b/arkui-plugins/memo-plugins/memo-cache-factory.ts new file mode 100644 index 000000000..1ca4234ed --- /dev/null +++ b/arkui-plugins/memo-plugins/memo-cache-factory.ts @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2022-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { factory } from './memo-factory'; +import { + filterMemoSkipParams, + findLocalReturnTypeFromTypeAnnotation, + findUnmemoizedScopeInFunctionBody, + fixGensymParams, + getFunctionParamsBeforeUnmemoized, + hasMemoAnnotation, + isVoidType, + mayAddLastReturn, + parametrizedNodeHasReceiver, + ParamInfo, + PositionalIdTracker, +} from './utils'; +import { InternalsTransformer } from './internal-transformer'; +import { GenSymPrefix } from '../common/predefines'; + +export interface CachedMetadata extends arkts.AstNodeCacheValueMetadata { + internalsTransformer?: InternalsTransformer; +} + +export class RewriteFactory { + static rewriteUnionType(node: arkts.ETSUnionType, metadata?: CachedMetadata): arkts.ETSUnionType { + return arkts.factory.updateUnionType( + node, + node.types.map((t) => { + if (arkts.isETSFunctionType(t)) { + return RewriteFactory.rewriteFunctionType(t, metadata); + } + if (arkts.isETSUnionType(t)) { + return RewriteFactory.rewriteUnionType(t, metadata); + } + return t; + }) + ); + } + + static rewriteFunctionType(node: arkts.ETSFunctionType, metadata?: CachedMetadata): arkts.ETSFunctionType { + const canRewriteType = !metadata?.forbidTypeRewrite; + const isWithinTypeParams = !!metadata?.isWithinTypeParams && hasMemoAnnotation(node); + if (!canRewriteType && !isWithinTypeParams) { + return node; + } + const hasReceiver = metadata?.hasReceiver ?? parametrizedNodeHasReceiver(node); + return factory.updateFunctionTypeWithMemoParameters(node, hasReceiver); + } + + static rewriteETSTypeReference(node: arkts.ETSTypeReference, metadata?: CachedMetadata): arkts.ETSTypeReference { + if (!metadata?.isWithinTypeParams) { + return node; + } + const part = node.part; + if (!part) { + return node; + } + const typeParams = part.typeParams; + if (!typeParams) { + return node; + } + const newTypeParams = arkts.factory.updateTSTypeParameterInstantiation( + typeParams, + typeParams.params.map((t) => RewriteFactory.rewriteType(t, metadata)!) + ); + return arkts.factory.updateTypeReference( + node, + arkts.factory.updateTypeReferencePart(part, part.name, newTypeParams, part.previous) + ); + } + + /** + * @internal + */ + static rewriteType(node: arkts.TypeNode | undefined, metadata?: CachedMetadata): arkts.TypeNode | undefined { + let newNodeType = node; + if (!!newNodeType && arkts.isETSFunctionType(newNodeType)) { + newNodeType = RewriteFactory.rewriteFunctionType(newNodeType, metadata); + } else if (!!newNodeType && arkts.isETSUnionType(newNodeType)) { + newNodeType = RewriteFactory.rewriteUnionType(newNodeType, metadata); + } else if (!!newNodeType && arkts.isETSTypeReference(newNodeType)) { + return RewriteFactory.rewriteETSTypeReference(newNodeType, metadata); + } + return newNodeType; + } + + static rewriteTypeAlias( + node: arkts.TSTypeAliasDeclaration, + metadata?: CachedMetadata + ): arkts.TSTypeAliasDeclaration { + if (!node.typeAnnotation) { + return node; + } + const newNodeType = RewriteFactory.rewriteType(node.typeAnnotation); + return arkts.factory.updateTSTypeAliasDeclaration(node, node.id, node.typeParams, newNodeType); + } + + static rewriteParameter( + node: arkts.ETSParameterExpression, + metadata?: CachedMetadata + ): arkts.ETSParameterExpression { + if (!node.type && !node.initializer) { + return node; + } + node.type = RewriteFactory.rewriteType(node.type as arkts.TypeNode, metadata); + return arkts.factory.updateParameterDeclaration(node, node.identifier, node.initializer); + } + + static rewriteProperty(node: arkts.Property, metadata?: CachedMetadata): arkts.Property { + if (!node.value || !arkts.isArrowFunctionExpression(node.value)) { + return node; + } + return arkts.factory.updateProperty(node, node.key, RewriteFactory.rewriteArrowFunction(node.value, metadata)); + } + + static rewriteClassProperty(node: arkts.ClassProperty, metadata?: CachedMetadata): arkts.ClassProperty { + const newType = !!node.typeAnnotation ? RewriteFactory.rewriteType(node.typeAnnotation, metadata) : undefined; + const newValue = + !!node.value && arkts.isArrowFunctionExpression(node.value) + ? RewriteFactory.rewriteArrowFunction(node.value, metadata) + : node.value; + return arkts.factory.updateClassProperty(node, node.key, newValue, newType, node.modifiers, node.isComputed); + } + + static rewriteArrowFunction( + node: arkts.ArrowFunctionExpression, + metadata?: arkts.AstNodeCacheValueMetadata, + expectReturn?: arkts.TypeNode + ): arkts.ArrowFunctionExpression { + return arkts.factory.updateArrowFunction( + node, + RewriteFactory.rewriteScriptFunction(node.scriptFunction, metadata, expectReturn) + ); + } + + /** + * @internal + */ + static rewriteScriptFunctionBody( + node: arkts.ScriptFunction, + body: arkts.BlockStatement, + positionalIdTracker: PositionalIdTracker, + callName?: string, + hasReceiver?: boolean, + expectReturn?: arkts.TypeNode + ): arkts.BlockStatement { + const _hasReceiver = hasReceiver ?? node.hasReceiver; + const _callName = callName ?? node.id?.name; + const parameters = getFunctionParamsBeforeUnmemoized(node.params, _hasReceiver); + const filteredParameters = filterMemoSkipParams(parameters); + const declaredParams: ParamInfo[] = filteredParameters.map((p) => { + const param = p as arkts.ETSParameterExpression; + return { ident: param.identifier, param }; + }); + const _gensymCount = fixGensymParams(declaredParams, body); + if (findUnmemoizedScopeInFunctionBody(body, _gensymCount)) { + return body; + } + const returnType = + node.returnTypeAnnotation ?? + expectReturn ?? + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID); + const _isVoidReturn = isVoidType(returnType); + const scopeDeclaration = factory.createScopeDeclaration( + returnType, + positionalIdTracker.id(_callName), + declaredParams.length + ); + const memoParametersDeclaration = node.params.length + ? factory.createMemoParameterDeclaration(declaredParams.map((p) => p.ident.name)) + : undefined; + const syntheticReturnStatement = factory.createSyntheticReturnStatement(false); + const unchangedCheck = factory.createIfStatementWithSyntheticReturnStatement( + syntheticReturnStatement, + _isVoidReturn + ); + const lastReturn = mayAddLastReturn(body) + ? factory.createWrappedReturnStatement(factory.createRecacheCall(), _isVoidReturn) + : undefined; + return arkts.factory.updateBlock(body, [ + ...body.statements.slice(0, _gensymCount), + scopeDeclaration, + ...(!!memoParametersDeclaration ? [memoParametersDeclaration] : []), + unchangedCheck, + ...body.statements.slice(_gensymCount), + ...(!!lastReturn ? [lastReturn] : []), + ]); + } + + static rewriteScriptFunction( + node: arkts.ScriptFunction, + metadata?: CachedMetadata, + expectReturn?: arkts.TypeNode + ): arkts.ScriptFunction { + const _callName = metadata?.callName; + const _hasReceiver = metadata?.hasReceiver ?? node.hasReceiver; + const _isSetter = !!metadata?.isSetter; + const _isGetter = !!metadata?.isGetter; + const _hasMemoEntry = !!metadata?.hasMemoEntry; + const _hasMemoIntrinsic = !!metadata?.hasMemoIntrinsic; + const _internalsTransformer = metadata?.internalsTransformer; + const _isDecl = arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE); + const newParams: readonly arkts.Expression[] = prepareRewriteScriptFunctionParameters( + node, + _isSetter, + _isGetter, + _hasReceiver + ); + const newReturnType: arkts.TypeNode | undefined = prepareRewriteScriptFunctionReturnType( + node, + _isGetter, + _hasReceiver + ); + const newBody: arkts.AstNode | undefined = prepareRewriteScriptFunctionBody( + node, + expectReturn, + _internalsTransformer, + _isDecl, + _hasMemoEntry, + _hasMemoIntrinsic, + _callName, + _hasReceiver, + _isGetter, + _isSetter + ); + return arkts.factory.updateScriptFunction( + node, + newBody, + arkts.factory.createFunctionSignature(node.typeParams, newParams, newReturnType, _hasReceiver), + node.flags, + node.modifiers + ); + } + + static rewriteMethodDefinition(node: arkts.MethodDefinition, metadata?: CachedMetadata): arkts.MethodDefinition { + const isSetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET; + const isGetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; + if (node.overloads.length > 0) { + node.setOverloads(node.overloads.map((o) => RewriteFactory.rewriteMethodDefinition(o, metadata))); + } + return arkts.factory.updateMethodDefinition( + node, + node.kind, + node.name, + RewriteFactory.rewriteScriptFunction(node.scriptFunction, { + callName: node.name.name, + ...metadata, + isSetter, + isGetter, + }), + node.modifiers, + false + ); + } + + static rewriteCallExpression(node: arkts.CallExpression, metadata?: CachedMetadata): arkts.CallExpression { + const _hasMemoEntry = !!metadata?.hasMemoEntry; + if (_hasMemoEntry) { + return node; + } + const _hasReceiver = metadata?.hasReceiver; + let _callName: string | undefined = metadata?.callName; + if (!!_callName && arkts.isIdentifier(node.expression)) { + _callName = node.expression.name; + } else if ( + !!_callName && + arkts.isMemberExpression(node.expression) && + arkts.isIdentifier(node.expression.property) + ) { + _callName = node.expression.property.name; + } + return factory.insertHiddenArgumentsToCall( + node, + PositionalIdTracker.getInstance(arkts.getFileName()).id(_callName), + _hasReceiver + ); + } + + static rewriteIdentifier( + node: arkts.Identifier, + metadata?: CachedMetadata + ): arkts.Identifier | arkts.MemberExpression { + if (!node.name.startsWith(GenSymPrefix.INTRINSIC) && !node.name.startsWith(GenSymPrefix.UI)) { + return factory.createMemoParameterAccess(node.name); + } + return node; + } + + static rewriteReturnStatement( + node: arkts.ReturnStatement, + metadata?: CachedMetadata + ): arkts.ReturnStatement | arkts.BlockStatement { + return factory.createWrappedReturnStatement(factory.createRecacheCall(node.argument), !node.argument); + } + + static rewriteVariableDeclarator( + node: arkts.VariableDeclarator, + metadata?: CachedMetadata + ): arkts.VariableDeclarator { + const expectReturnType = findLocalReturnTypeFromTypeAnnotation(node.name.typeAnnotation); + const variableType = RewriteFactory.rewriteType(node.name.typeAnnotation); + let initializer = node.initializer; + if (!!initializer && arkts.isConditionalExpression(initializer) && !!initializer.alternate) { + let alternate = initializer.alternate; + if (arkts.isTSAsExpression(alternate)) { + alternate = arkts.factory.updateTSAsExpression( + alternate, + !!alternate.expr && arkts.isArrowFunctionExpression(alternate.expr) + ? RewriteFactory.rewriteArrowFunction(alternate.expr, metadata, expectReturnType) + : alternate.expr, + RewriteFactory.rewriteType(alternate.typeAnnotation), + alternate.isConst + ); + } else if (arkts.isArrowFunctionExpression(alternate)) { + alternate = RewriteFactory.rewriteArrowFunction(alternate, metadata, expectReturnType); + } + initializer = arkts.factory.updateConditionalExpression( + initializer, + initializer.test, + initializer.consequent, + alternate + ); + } else if (!!initializer && arkts.isArrowFunctionExpression(initializer)) { + initializer = RewriteFactory.rewriteArrowFunction(initializer, metadata, expectReturnType); + } + return arkts.factory.updateVariableDeclarator( + node, + node.flag, + arkts.factory.updateIdentifier(node.name, node.name.name, variableType), + initializer + ); + } +} + +function prepareRewriteScriptFunctionParameters( + node: arkts.ScriptFunction, + isSetter?: boolean, + isGetter?: boolean, + hasReceiver?: boolean +): readonly arkts.Expression[] { + let newParams: readonly arkts.Expression[] = node.params; + if (!isSetter && !isGetter) { + newParams = factory.createHiddenParameterIfNotAdded(node.params, node.hasReceiver); + } else if (isSetter && node.params.length > 0) { + if (hasReceiver && node.params.length === 2) { + newParams = [ + node.params.at(0)!, + RewriteFactory.rewriteParameter(node.params.at(1)! as arkts.ETSParameterExpression), + ]; + } else { + newParams = [RewriteFactory.rewriteParameter(node.params.at(0)! as arkts.ETSParameterExpression)]; + } + } + return newParams; +} + +function prepareRewriteScriptFunctionReturnType( + node: arkts.ScriptFunction, + isGetter?: boolean, + hasReceiver?: boolean +): arkts.TypeNode | undefined { + let newReturnType: arkts.TypeNode | undefined = node.returnTypeAnnotation; + if (!!node.returnTypeAnnotation && isGetter) { + newReturnType = RewriteFactory.rewriteType(node.returnTypeAnnotation, { hasReceiver }); + } + return newReturnType; +} + +function prepareRewriteScriptFunctionBody( + node: arkts.ScriptFunction, + expectReturn?: arkts.TypeNode, + internalsTransformer?: InternalsTransformer, + isDecl?: boolean, + hasMemoEntry?: boolean, + hasMemoIntrinsic?: boolean, + callName?: string, + hasReceiver?: boolean, + isGetter?: boolean, + isSetter?: boolean +): arkts.AstNode | undefined { + if (isGetter || isSetter || isDecl || !node.body || !arkts.isBlockStatement(node.body)) { + return node.body; + } + + let newBody: arkts.AstNode | undefined; + const positionalIdTracker = PositionalIdTracker.getInstance(arkts.getFileName()); + newBody = internalsTransformer?.visitor(node.body) ?? node.body; + if (!hasMemoEntry && !hasMemoIntrinsic) { + newBody = RewriteFactory.rewriteScriptFunctionBody( + node, + newBody as arkts.BlockStatement, + positionalIdTracker, + callName, + hasReceiver, + expectReturn + ); + } + return newBody; +} + +export const rewriteByType = new Map arkts.AstNode>([ + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_UNION_TYPE, RewriteFactory.rewriteUnionType], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_FUNCTION_TYPE, RewriteFactory.rewriteFunctionType], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE, RewriteFactory.rewriteETSTypeReference], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION, RewriteFactory.rewriteTypeAlias], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION, RewriteFactory.rewriteParameter], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY, RewriteFactory.rewriteClassProperty], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION, RewriteFactory.rewriteArrowFunction], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_SCRIPT_FUNCTION, RewriteFactory.rewriteScriptFunction], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION, RewriteFactory.rewriteMethodDefinition], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CALL_EXPRESSION, RewriteFactory.rewriteCallExpression], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_IDENTIFIER, RewriteFactory.rewriteIdentifier], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_RETURN_STATEMENT, RewriteFactory.rewriteReturnStatement], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_VARIABLE_DECLARATOR, RewriteFactory.rewriteVariableDeclarator], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_PROPERTY, RewriteFactory.rewriteProperty], +]); diff --git a/arkui-plugins/memo-plugins/memo-factory.ts b/arkui-plugins/memo-plugins/memo-factory.ts index dac886843..948fb082e 100644 --- a/arkui-plugins/memo-plugins/memo-factory.ts +++ b/arkui-plugins/memo-plugins/memo-factory.ts @@ -17,12 +17,12 @@ import * as arkts from '@koalaui/libarkts'; import { fixGensymParams, buildeParamInfos, - isUnmemoizedInFunction, + isUnmemoizedInFunctionParams, mayAddLastReturn, ParamInfo, ReturnTypeInfo, RuntimeNames, - parametrizedNodeHasReceiver + parametrizedNodeHasReceiver, } from './utils'; import { moveToFront } from '../common/arkts-utils'; @@ -40,7 +40,6 @@ export class factory { arkts.factory.createIdentifier(RuntimeNames.ID_TYPE) ); } - static createContextTypesImportDeclaration(program?: arkts.Program): void { const source: arkts.StringLiteral = arkts.factory.createStringLiteral(RuntimeNames.MEMO_IMPORT_NAME); const importDecl: arkts.ETSImportDeclaration = arkts.factory.createImportDeclaration( @@ -89,7 +88,7 @@ export class factory { hasReceiver: boolean = false ): readonly arkts.Expression[] { const _params = params ?? []; - if (isUnmemoizedInFunction(_params)) { + if (isUnmemoizedInFunctionParams(_params)) { return _params; } let newParams: arkts.Expression[] = [...factory.createHiddenParameters(), ..._params]; @@ -98,12 +97,16 @@ export class factory { } return newParams; } - static updateFunctionTypeWithMemoParameters(type: arkts.ETSFunctionType): arkts.ETSFunctionType { + static updateFunctionTypeWithMemoParameters( + type: arkts.ETSFunctionType, + hasReceiver: boolean = false, + newTypeParams?: arkts.TSTypeParameterDeclaration + ): arkts.ETSFunctionType { return arkts.factory.updateFunctionType( type, arkts.factory.createFunctionSignature( - undefined, - factory.createHiddenParameterIfNotAdded(type.params), + newTypeParams, + factory.createHiddenParameterIfNotAdded(type.params, hasReceiver), type.returnType, false ), @@ -299,6 +302,18 @@ export class factory { returnStatement ); } + static createWrappedReturnStatement( + argument: arkts.Expression, + isReturnVoid: boolean + ): arkts.ReturnStatement | arkts.BlockStatement { + if (!isReturnVoid) { + return arkts.factory.createReturnStatement(argument); + } + return arkts.factory.createBlock([ + arkts.factory.createExpressionStatement(argument), + arkts.factory.createReturnStatement(), + ]); + } // Compute static createLambdaWrapper(node: arkts.Expression): arkts.ArrowFunctionExpression { @@ -390,11 +405,13 @@ export class factory { static insertHiddenArgumentsToCall( node: arkts.CallExpression, - hash: arkts.NumberLiteral | arkts.StringLiteral + hash: arkts.NumberLiteral | arkts.StringLiteral, + hasReceiver?: boolean ): arkts.CallExpression { - return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ - ...factory.createHiddenArguments(hash), - ...node.arguments, - ]); + let updatedArguments = [...factory.createHiddenArguments(hash), ...node.arguments]; + if (!!hasReceiver) { + updatedArguments = moveToFront(updatedArguments, 2); + } + return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, updatedArguments); } } diff --git a/arkui-plugins/memo-plugins/parameter-transformer.ts b/arkui-plugins/memo-plugins/parameter-transformer.ts index 050333da7..1947b60d1 100644 --- a/arkui-plugins/memo-plugins/parameter-transformer.ts +++ b/arkui-plugins/memo-plugins/parameter-transformer.ts @@ -22,8 +22,7 @@ import { findReturnTypeFromTypeAnnotation, isMemoETSParameterExpression, isMemoParametersDeclaration, - isUnmemoizedInFunction, - isVoidType, + isUnmemoizedInFunctionParams, MemoInfo, ParamInfo, PositionalIdTracker, @@ -140,7 +139,7 @@ export class ParameterTransformer extends AbstractVisitor { if (!scriptFunction.body || !arkts.isBlockStatement(scriptFunction.body)) { return initializer; } - if (isUnmemoizedInFunction(scriptFunction.params)) { + if (isUnmemoizedInFunctionParams(scriptFunction.params)) { return initializer; } const returnTypeInfo: ReturnTypeInfo = buildReturnTypeInfo( diff --git a/arkui-plugins/memo-plugins/signature-transformer.ts b/arkui-plugins/memo-plugins/signature-transformer.ts index b7fa8a62d..6c15cce2d 100644 --- a/arkui-plugins/memo-plugins/signature-transformer.ts +++ b/arkui-plugins/memo-plugins/signature-transformer.ts @@ -129,8 +129,7 @@ export class SignatureTransformer extends AbstractVisitor { const expr = node.part.name; const decl = arkts.getDecl(expr); if (!decl || !arkts.isTSTypeAliasDeclaration(decl)) { - console.error(`@memo parameter's type has not been declared`); - throw 'Invalid @memo usage'; + return node as any as T; } const memoDecl = isMemoTSTypeAliasDeclaration(decl); if (memoDecl) { diff --git a/arkui-plugins/memo-plugins/utils.ts b/arkui-plugins/memo-plugins/utils.ts index 2e193d099..e3b415e7f 100644 --- a/arkui-plugins/memo-plugins/utils.ts +++ b/arkui-plugins/memo-plugins/utils.ts @@ -71,6 +71,15 @@ export class PositionalIdTracker { // Global for the whole program. static callCount: number = 0; + private static instance: PositionalIdTracker; + + static getInstance(fileName: string): PositionalIdTracker { + if (!this.instance) { + this.instance = new PositionalIdTracker(fileName); + } + return this.instance; + } + // Set `stable` to true if you want to have more predictable values. // For example for tests. // Don't use it in production! @@ -494,6 +503,21 @@ export function findReturnTypeFromTypeAnnotation( return undefined; } +export function findLocalReturnTypeFromTypeAnnotation( + typeAnnotation: arkts.AstNode | undefined +): arkts.TypeNode | undefined { + if (!typeAnnotation) { + return undefined; + } + if (arkts.isETSFunctionType(typeAnnotation)) { + return typeAnnotation.returnType; + } + if (arkts.isETSUnionType(typeAnnotation)) { + return typeAnnotation.types.find((type) => arkts.isETSFunctionType(type))?.returnType; + } + return undefined; +} + export function getDeclResolveAlias(node: arkts.AstNode): arkts.AstNode | undefined { const decl = arkts.getDecl(node); if (!!decl && !!decl.parent && arkts.isIdentifier(decl) && arkts.isVariableDeclarator(decl.parent)) { @@ -508,11 +532,17 @@ export function getDeclResolveAlias(node: arkts.AstNode): arkts.AstNode | undefi } export function mayAddLastReturn(node: arkts.BlockStatement): boolean { - return ( - node.statements.length === 0 || - (!arkts.isReturnStatement(node.statements[node.statements.length - 1]) && - !arkts.isThrowStatement(node.statements[node.statements.length - 1])) - ); + if (node.statements.length === 0) { + return true; + } + const lastStatement = node.statements[node.statements.length - 1]; + if (arkts.isBlockStatement(lastStatement)) { + return mayAddLastReturn(lastStatement); + } + if (arkts.isReturnStatement(lastStatement) || arkts.isThrowStatement(lastStatement)) { + return false; + } + return true; } export function fixGensymParams(params: ParamInfo[], body: arkts.BlockStatement): number { @@ -536,16 +566,49 @@ export function fixGensymParams(params: ParamInfo[], body: arkts.BlockStatement) return gensymParamsCount; } -export function isUnmemoizedInFunction(params?: readonly arkts.Expression[]): boolean { +export function isMemoContextParamAdded(param: arkts.Expression): boolean { + return arkts.isEtsParameterExpression(param) && param.identifier.name === RuntimeNames.CONTEXT; +} + +export function isMemoIdParamAdded(param: arkts.Expression): boolean { + return arkts.isEtsParameterExpression(param) && param.identifier.name === RuntimeNames.ID; +} + +export function isUnmemoizedInFunctionParams(params?: readonly arkts.Expression[], hasReceiver?: boolean): boolean { const _params = params ?? []; - const first = _params.at(0); - const isContextAdded = - !!first && arkts.isEtsParameterExpression(first) && first.identifier.name === RuntimeNames.CONTEXT; - const second = _params.at(1); - const isIdAdded = !!second && arkts.isEtsParameterExpression(second) && second.identifier.name === RuntimeNames.ID; + const startIndex = hasReceiver ? 1 : 0; + const isContextAdded = !!_params.at(startIndex) && isMemoContextParamAdded(_params.at(startIndex)!); + const isIdAdded = !!_params.at(startIndex + 1) && isMemoIdParamAdded(_params.at(startIndex + 1)!); return isContextAdded && isIdAdded; } +export function getFunctionParamsBeforeUnmemoized( + params?: readonly arkts.Expression[], + hasReceiver?: boolean +): readonly arkts.Expression[] { + const _params = params ?? []; + if (isUnmemoizedInFunctionParams(_params, hasReceiver)) { + if (!!hasReceiver) { + return [_params.at(0)!, ..._params.slice(3)]; + } + return _params.slice(2); + } + return _params; +} + +export function findUnmemoizedScopeInFunctionBody(body: arkts.BlockStatement, gensymCount: number = 0): boolean { + const startIndex = gensymCount; + if (body.statements.length < startIndex + 1) { + return false; + } + const statement = body.statements.at(startIndex)!; + if (!arkts.isVariableDeclaration(statement)) { + return false; + } + const declarator = statement.declarators.at(0)!; + return declarator.name.name === RuntimeNames.SCOPE; +} + export function buildReturnTypeInfo( returnType: arkts.TypeNode | undefined, isMemo?: boolean, @@ -589,3 +652,7 @@ function isThisParam(node: arkts.Expression | undefined): boolean { } return node.identifier?.isReceiver ?? false; } + +export function filterMemoSkipParams(params: readonly arkts.Expression[]): readonly arkts.Expression[] { + return params.filter((p) => !hasMemoSkipAnnotation(p as arkts.ETSParameterExpression)); +} diff --git a/arkui-plugins/npm_preinstall.sh b/arkui-plugins/npm_preinstall.sh deleted file mode 100755 index 478471ab0..000000000 --- a/arkui-plugins/npm_preinstall.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -# coding: utf-8 -# Copyright (c) 2025 Huawei Device Co., Ltd. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if [ "$1" == "--init" ]; then - cd ../koala-wrapper - npm install - npm run compile - npm link - - cd ../ts_wrapper - npm install - npm run compile - npm link - - cd ../arkui-plugins - mkdir node_modules - npm link @koalaui/libarkts -fi \ No newline at end of file diff --git a/arkui-plugins/package-lock.json b/arkui-plugins/package-lock.json deleted file mode 100644 index 27a190aac..000000000 --- a/arkui-plugins/package-lock.json +++ /dev/null @@ -1,5590 +0,0 @@ -{ - "name": "arkui-plugin", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@babel/cli": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/cli/-/cli-7.20.7.tgz", - "integrity": "sha512-WylgcELHB66WwQqItxNILsMlaTd8/SO6SgTTjMp4uCI7P4QyH1r3nqgFmO3BfM4AtfniHgFMH3EpYFj/zynBkQ==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.8", - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.4.0", - "commander": "^4.0.1", - "convert-source-map": "^1.1.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.2.0", - "make-dir": "^2.1.0", - "slash": "^2.0.0" - } - }, - "@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - } - }, - "@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", - "dev": true - }, - "@babel/core": { - "version": "7.20.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.12", - "@babel/types": "^7.20.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", - "dev": true, - "requires": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "dev": true, - "requires": { - "@babel/types": "^7.27.3" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dev": true, - "requires": { - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", - "dev": true, - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "dev": true, - "requires": { - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "dev": true, - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", - "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", - "dev": true, - "requires": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helpers": { - "version": "7.27.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", - "dev": true, - "requires": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - } - }, - "@babel/parser": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", - "dev": true, - "requires": { - "@babel/types": "^7.28.0" - } - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - } - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", - "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", - "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", - "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", - "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.0" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", - "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", - "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - } - }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", - "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.28.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz", - "integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", - "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/preset-env": { - "version": "7.20.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-env/-/preset-env-7.20.2.tgz", - "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.1", - "@babel/helper-compilation-targets": "^7.20.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.20.1", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.2", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.20.2", - "@babel/plugin-transform-classes": "^7.20.2", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.20.2", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.19.6", - "@babel/plugin-transform-modules-commonjs": "^7.19.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.6", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.20.1", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.19.0", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.18.10", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/preset-modules": { - "version": "0.1.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-modules/-/preset-modules-0.1.6.tgz", - "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" - } - }, - "@babel/runtime": { - "version": "7.20.13", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/runtime/-/runtime-7.20.13.tgz", - "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, - "@babel/template": { - "version": "7.27.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - } - }, - "@babel/traverse": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", - "debug": "^4.3.1" - } - }, - "@babel/types": { - "version": "7.28.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.28.1.tgz", - "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/console": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "@jest/core": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "@jest/environment": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "requires": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - } - }, - "@jest/expect": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "requires": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - } - }, - "@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "requires": { - "jest-get-type": "^29.6.3" - } - }, - "@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "@jest/globals": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - } - }, - "@jest/reporters": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "@jest/schemas": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.27.8" - } - }, - "@jest/source-map": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "requires": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "@jest/transform": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "dependencies": { - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "@jest/types": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@koalaui/libarkts": { - "version": "file:../koala-wrapper", - "dependencies": { - "@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@babel/cli": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/cli/-/cli-7.20.7.tgz", - "integrity": "sha512-WylgcELHB66WwQqItxNILsMlaTd8/SO6SgTTjMp4uCI7P4QyH1r3nqgFmO3BfM4AtfniHgFMH3EpYFj/zynBkQ==", - "requires": { - "@jridgewell/trace-mapping": "^0.3.8", - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.4.0", - "commander": "^4.0.1", - "convert-source-map": "^1.1.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.2.0", - "make-dir": "^2.1.0", - "slash": "^2.0.0" - } - }, - "@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "requires": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - } - }, - "@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==" - }, - "@babel/core": { - "version": "7.20.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.12", - "@babel/types": "^7.20.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/generator": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", - "requires": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "requires": { - "@babel/types": "^7.27.3" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "requires": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", - "requires": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "requires": { - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==" - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "requires": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "requires": { - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==" - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", - "requires": { - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==" - }, - "@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==" - }, - "@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==" - }, - "@babel/helper-wrap-function": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", - "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", - "requires": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helpers": { - "version": "7.27.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", - "requires": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - } - }, - "@babel/parser": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", - "requires": { - "@babel/types": "^7.28.0" - } - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - } - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", - "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", - "requires": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", - "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", - "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", - "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.0" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", - "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "requires": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", - "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - } - }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", - "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.28.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz", - "integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", - "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/preset-env": { - "version": "7.20.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-env/-/preset-env-7.20.2.tgz", - "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", - "requires": { - "@babel/compat-data": "^7.20.1", - "@babel/helper-compilation-targets": "^7.20.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.20.1", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.2", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.20.2", - "@babel/plugin-transform-classes": "^7.20.2", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.20.2", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.19.6", - "@babel/plugin-transform-modules-commonjs": "^7.19.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.6", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.20.1", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.19.0", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.18.10", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/preset-modules": { - "version": "0.1.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-modules/-/preset-modules-0.1.6.tgz", - "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" - } - }, - "@babel/runtime": { - "version": "7.20.13", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/runtime/-/runtime-7.20.13.tgz", - "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, - "@babel/template": { - "version": "7.27.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - } - }, - "@babel/traverse": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", - "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", - "debug": "^4.3.1" - } - }, - "@babel/types": { - "version": "7.28.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.28.1.tgz", - "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", - "requires": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "requires": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" - }, - "@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==" - }, - "@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", - "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", - "optional": true - }, - "@tsconfig/recommended": { - "version": "1.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/@tsconfig/recommended/-/recommended-1.0.8.tgz", - "integrity": "sha512-TotjFaaXveVUdsrXCdalyF6E5RyG6+7hHHQVZonQtdlk1rJZ1myDIvPUUKPhoYv+JAzThb2lQJh9+9ZfF46hsA==" - }, - "@types/node": { - "version": "18.19.119", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/node/-/node-18.19.119.tgz", - "integrity": "sha512-d0F6m9itIPaKnrvEMlzE48UjwZaAnFW7Jwibacw9MNdqadjKNpUm9tfJYDwmShJmgqcoqYUX3EMKO1+RWiuuNg==", - "requires": { - "undici-types": "~5.26.4" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "optional": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "binary-extensions": { - "version": "2.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "optional": true - }, - "brace-expansion": { - "version": "1.1.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "optional": true, - "requires": { - "fill-range": "^7.1.1" - } - }, - "browserslist": { - "version": "4.25.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", - "requires": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - } - }, - "caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://repo.huaweicloud.com/repository/npm/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==" - }, - "chokidar": { - "version": "3.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "optional": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "commander": { - "version": "4.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "core-js-compat": { - "version": "3.44.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/core-js-compat/-/core-js-compat-3.44.0.tgz", - "integrity": "sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==", - "requires": { - "browserslist": "^4.25.1" - } - }, - "debug": { - "version": "4.4.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "requires": { - "ms": "^2.1.3" - } - }, - "electron-to-chromium": { - "version": "1.5.187", - "resolved": "https://repo.huaweicloud.com/repository/npm/electron-to-chromium/-/electron-to-chromium-1.5.187.tgz", - "integrity": "sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==" - }, - "escalade": { - "version": "3.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "optional": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" - }, - "glob": { - "version": "7.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "optional": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "hasown": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "requires": { - "function-bind": "^1.1.2" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "optional": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.16.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "requires": { - "hasown": "^2.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "optional": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "optional": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "optional": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "jsesc": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==" - }, - "json5": { - "version": "2.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node-addon-api": { - "version": "8.5.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/node-addon-api/-/node-addon-api-8.5.0.tgz", - "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==" - }, - "node-releases": { - "version": "2.0.19", - "resolved": "https://repo.huaweicloud.com/repository/npm/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "picocolors": { - "version": "1.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "optional": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "optional": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" - }, - "regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, - "regexpu-core": { - "version": "6.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", - "requires": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - } - }, - "regjsgen": { - "version": "0.8.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==" - }, - "regjsparser": { - "version": "0.12.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "requires": { - "jsesc": "~3.0.2" - }, - "dependencies": { - "jsesc": { - "version": "3.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==" - } - } - }, - "resolve": { - "version": "1.22.10", - "resolved": "https://repo.huaweicloud.com/repository/npm/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "requires": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" - }, - "slash": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "optional": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "typescript": { - "version": "5.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==" - }, - "undici-types": { - "version": "5.26.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==" - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==" - }, - "unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" - }, - "update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "requires": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - }, - "@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", - "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", - "dev": true, - "optional": true - }, - "@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^3.0.0" - } - }, - "@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "@types/babel__core": { - "version": "7.20.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, - "requires": { - "@babel/types": "^7.20.7" - } - }, - "@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "29.5.14", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "requires": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "@types/node": { - "version": "22.16.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/node/-/node-22.16.4.tgz", - "integrity": "sha512-PYRhNtZdm2wH/NT2k/oAJ6/f2VD2N2Dag0lGlx2vWgMSJXGNmlce5MiTQzoWAiIJtso30mjnfQCOKVH+kAQC/g==", - "dev": true, - "requires": { - "undici-types": "~6.21.0" - } - }, - "@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "@types/yargs": { - "version": "17.0.33", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, - "acorn": { - "version": "8.15.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true - }, - "acorn-walk": { - "version": "8.3.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "requires": { - "acorn": "^8.11.0" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://repo.huaweicloud.com/repository/npm/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "async": { - "version": "3.2.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true - }, - "babel-jest": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "requires": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "dependencies": { - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - } - }, - "babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "binary-extensions": { - "version": "2.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "requires": { - "fill-range": "^7.1.1" - } - }, - "browserslist": { - "version": "4.25.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://repo.huaweicloud.com/repository/npm/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "chokidar": { - "version": "3.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "ci-info": { - "version": "3.9.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "commander": { - "version": "4.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "core-js-compat": { - "version": "3.44.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/core-js-compat/-/core-js-compat-3.44.0.tgz", - "integrity": "sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==", - "dev": true, - "requires": { - "browserslist": "^4.25.1" - } - }, - "create-jest": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - } - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.4.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "requires": { - "ms": "^2.1.3" - } - }, - "dedent": { - "version": "1.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", - "dev": true - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "diff-sequences": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true - }, - "ejs": { - "version": "3.1.10", - "resolved": "https://repo.huaweicloud.com/repository/npm/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, - "requires": { - "jake": "^10.8.5" - } - }, - "electron-to-chromium": { - "version": "1.5.187", - "resolved": "https://repo.huaweicloud.com/repository/npm/electron-to-chromium/-/electron-to-chromium-1.5.187.tgz", - "integrity": "sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==", - "dev": true - }, - "emittery": { - "version": "0.13.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escalade": { - "version": "3.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "filelist": { - "version": "1.0.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "requires": { - "minimatch": "^5.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "hasown": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "requires": { - "function-bind": "^1.1.2" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "import-local": { - "version": "3.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "optional": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.16.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "requires": { - "hasown": "^2.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "requires": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "dependencies": { - "@babel/core": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "semver": { - "version": "7.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "make-dir": { - "version": "4.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "requires": { - "semver": "^7.5.3" - } - }, - "semver": { - "version": "7.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jake": { - "version": "10.9.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "requires": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - } - }, - "jest": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "requires": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "dependencies": { - "jest-cli": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "requires": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - } - } - } - }, - "jest-changed-files": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "dependencies": { - "p-limit": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - } - } - }, - "jest-circus": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "p-limit": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "jest-config": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "jest-diff": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-docblock": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - } - }, - "jest-environment-node": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "jest-get-type": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true - }, - "jest-haste-map": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "requires": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-message-util": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "jest-mock": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true - }, - "jest-regex-util": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true - }, - "jest-resolve": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "requires": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - } - }, - "jest-runner": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "dependencies": { - "p-limit": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - } - } - }, - "jest-runtime": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "jest-snapshot": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "dependencies": { - "semver": { - "version": "7.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true - } - } - }, - "jest-util": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "requires": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "requires": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsesc": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "micromatch": { - "version": "4.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "requires": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node-releases": { - "version": "2.0.19", - "resolved": "https://repo.huaweicloud.com/repository/npm/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "picocolors": { - "version": "1.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pirates": { - "version": "4.0.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "pretty-format": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "pure-rand": { - "version": "6.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true - }, - "react-is": { - "version": "18.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "optional": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", - "dev": true, - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true - }, - "regexpu-core": { - "version": "6.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", - "dev": true, - "requires": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - } - }, - "regjsgen": { - "version": "0.8.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true - }, - "regjsparser": { - "version": "0.12.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "dev": true, - "requires": { - "jsesc": "~3.0.2" - }, - "dependencies": { - "jsesc": { - "version": "3.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "resolve": { - "version": "1.22.10", - "resolved": "https://repo.huaweicloud.com/repository/npm/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "requires": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve.exports": { - "version": "2.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true - }, - "semver": { - "version": "5.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://repo.huaweicloud.com/repository/npm/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "stack-utils": { - "version": "2.0.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "ts-jest": { - "version": "29.4.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/ts-jest/-/ts-jest-29.4.0.tgz", - "integrity": "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==", - "dev": true, - "requires": { - "bs-logger": "^0.2.6", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "^2.1.0", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.2", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "dependencies": { - "semver": { - "version": "7.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true - }, - "type-fest": { - "version": "4.41.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true - } - } - }, - "ts-node": { - "version": "10.9.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - }, - "typescript": { - "version": "5.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true - }, - "undici-types": { - "version": "6.21.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true - }, - "update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "requires": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - } - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "dependencies": { - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - } - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "write-file-atomic": { - "version": "4.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } - } -} diff --git a/arkui-plugins/package.json b/arkui-plugins/package.json index 0267fb00d..9f115edfe 100644 --- a/arkui-plugins/package.json +++ b/arkui-plugins/package.json @@ -7,12 +7,12 @@ "./test" ], "scripts": { - "local:install": "chmod 777 ./npm_preinstall.sh && ./npm_preinstall.sh --init", "compile:plugins": "./node_modules/.bin/babel . --out-dir lib --extensions .ts", "compile:clean": "rm -rf lib", "clean:test": "rm -rf dist && rm -rf coverage", "prepare:test": "cp -rf $INIT_CWD/../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/koala-wrapper/build/native/ ../koala-wrapper/build/", - "test": "npm run clean:test && npm run prepare:test && LD_LIBRARY_PATH=$INIT_CWD/../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib jest --coverage --logHeapUsage --config ./jest-test.config.js --silent", + "test": "npm run clean:test && npm run prepare:test && LD_LIBRARY_PATH=$INIT_CWD/../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib jest --coverage --logHeapUsage --config ./jest-test.config.js --maxWorkers=50% --silent", + "test:gdb": "LD_LIBRARY_PATH=$INIT_CWD/../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib gdb --args node ./node_modules/.bin/jest --config ./jest-test.config.js", "compile": "npm run compile:clean && npm run compile:plugins && cp -rf ./lib $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/" }, "devDependencies": { diff --git a/arkui-plugins/test/demo/interop/builder_interop.ets b/arkui-plugins/test/demo/interop/builder_interop.ets new file mode 100644 index 000000000..b50fe49a3 --- /dev/null +++ b/arkui-plugins/test/demo/interop/builder_interop.ets @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// ArkTS1.2 +'use static' +import { Entry, Text, Column, Component, Button, ClickEvent } from '@ohos.arkui.component' +import { State } from '@ohos.arkui.stateManagement' +import hilog from '@ohos.hilog' +import { demoBuilder1_1, aa } from 'har1' + +@Entry +@Component +struct MyStateSample { + @State stateVar: string = 'state var'; + message: string = 'var'; + + build() { + Column(undefined) { + Text('Hello World').fontSize(20) + Button(this.message).backgroundColor('#FFFF00FF') + .onClick((e: ClickEvent) => { + hilog.info(0x0000, 'testTag', 'On Click'); + }) + Text(this.stateVar).fontSize(20) + demoBuilder1_1({ a:"a23", b:this.stateVar}) + let tmp:aa = { a:"a23", b:this.stateVar} + demoBuilder1_1(tmp) + } + } +} + + +//ArkT1.1 +@Builder +export function demoBuilder1_1( param1:aa) { + +} +export class aa { + a:string = '' + b:string = '' +} + + +//transform 1.1 Builder to compatibleComponent + +compatibleComponent((() => { + let global = ESValue.getGlobal(); + let viewStackProcessor = global.getProperty("ViewStackProcessor"); + let createId = viewStackProcessor.getProperty("AllocateNewElmetIdForNextComponent"); + let elmtId = createId.invoke(); + let createCompatibleNode = global.getProperty("createCompatibleNodeWithFunc"); + let paramObject0 = ESValue.instantiateEmptyObject(); + paramObject0.setProperty("a", ESValue.wrap("a23")); + paramObject0.setProperty("b", ESValue.wrap(this.stateVar)); + let component = createCompatibleNode.invoke(ESValue.wrap(demoBuilder1_1), elmtId, paramObject0); + let viewPUCreate = global.getProperty("viewPUCreate"); + viewPUCreate.invoke(component); + return { + component: component, + name: "demoBuilder1_1", + }; +}), ((instance: ESValue) => { + let param = instance.getProperty("arg1"); + param.setProperty("a", ESValue.wrap("a23")); + param.setProperty("b", ESValue.wrap(this.stateVar)); + let global = ESValue.getGlobal(); + let runPendingJobs = global.getProperty("runPendingJobs"); + runPendingJobs.invoke(); +})); + +compatibleComponent((() => { + let global = ESValue.getGlobal(); + let viewStackProcessor = global.getProperty("ViewStackProcessor"); + let createId = viewStackProcessor.getProperty("AllocateNewElmetIdForNextComponent"); + let elmtId = createId.invoke(); + let createCompatibleNode = global.getProperty("createCompatibleNodeWithFunc"); + let component = createCompatibleNode.invoke(ESValue.wrap(demoBuilder1_1), elmtId, ESValue.wrap(tmp)); + let viewPUCreate = global.getProperty("viewPUCreate"); + viewPUCreate.invoke(component); + return { + component: component, + name: "demoBuilder1_1", + }; +}), ((instance: ESValue) => { + let instanceParam = (instance.getProperty("arg1") as ESValue); + if (((instanceParam.typeOf()) != ("object"))) { + return; + } + let param_wrapped = ESValue.wrap(tmp); + let param_wrapped_it = param_wrapped.keys(); + while (true) { + let param_wrapped_key = param_wrapped_it.next(); + if (param_wrapped_key.done) { + break; + } + instanceParam.setProperty((param_wrapped_key.value as ESValue), param_wrapped.getProperty((param_wrapped_key.value as ESValue))); + } + let global = ESValue.getGlobal(); + let runPendingJobs = global.getProperty("runPendingJobs"); + runPendingJobs.invoke(); +})); diff --git a/arkui-plugins/test/demo/interop/link_link_interop.ets b/arkui-plugins/test/demo/interop/link_link_interop.ets new file mode 100644 index 000000000..e5d5f8b47 --- /dev/null +++ b/arkui-plugins/test/demo/interop/link_link_interop.ets @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// ArkTS1.2 +import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { ArkUICompatible, InteropComponent, Text, TextAttribute, Column, Component, Button, ButtonAttribute, ClickEvent, UserView, } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { State, StateDecoratedVariable, MutableState, stateOf, observableProxy, Observed, Track, Provide, Consume } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { MyText } from 'har2/src/main/ets/components/MainPage' +import { Child1 } from 'har1' + + +@Component +struct MyStateSample { + @State stateVar: new MyText(); + build() { + Column() { + Button(this.stateVar.text) + .onClick((e: ClickEvent) => { + this.stateVar.text += '~'; + }) + Child({stateVar: this.stateVar}) + } + } +} + +@Component +struct Child { + @Link stateVar; + build() { + Column() { + Button(this.stateVar.text) + .onClick((e: ClickEvent) => { + this.stateVar.text += '~'; + }) + Child1({stateVar: this.stateVar, text: this.stateVar}) + } + } +} + +class MyText { + text: string = 'MyText'; +} + + +//ArkT1.1 +import { MyText } from 'har2/src/main/ets/components/MainPage' + +@Component +export struct Child1{ + @Link stateVar: MyText; + @Link text: MyText; + build() { + Column() { + Button(this.stateVar.text) + .onClick(() => { + this.stateVar.text += '~'; + }) + Button(this.text.text) + .onClick(() => { + this.text.text = 'ArkTS1.1'; + }) + } + } +} + + +//transform 1.1struct 'Child1' to ArkUICompatible + +ArkUICompatible(__memo_context, ((__memo_id) + (252133223)), (() => { + let global = ESValue.getGlobal(); + let param = ESValue.instantiateEmptyObject(); + let createState = global.getProperty("createStateVariable"); + let stateVar_SetSource = ((value: MyText) => { + (this).stateVar = value; + }); + let stateVar_ProxyState = createState.invoke(ESValue.wrap((this).stateVar), ESValue.wrap(stateVar_SetSource)); + (this).__backing_stateVar!.setProxy(stateVar_ProxyState); + let stateVar_SetProxy = ((value: MyText) => { + stateVar_ProxyState.invokeMethod("set", ESValue.wrap(value)); + }); + (this).__backing_stateVar!.setProxyValue = stateVar_SetProxy; + let stateVar_NotifyCallback = ((propertyName: string) => { + stateVar_ProxyState.invokeMethod("notifyPropertyHasChangedPU"); + }); + (this).__backing_stateVar!.setNotifyCallback(stateVar_NotifyCallback); + param.setProperty("stateVar", stateVar_ProxyState); + param.setProperty("text", stateVar_ProxyState); + let extraInfo = ESValue.instantiateEmptyObject(); + extraInfo.setProperty("page", "har1/src/main/ets/components/MainPage"); + let esundefined = ESValue.wrap(undefined); + let blank = (() => {}); + let esblank = ESValue.wrap((blank as object)); + let viewStackProcessor = global.getProperty("ViewStackProcessor"); + let createId = viewStackProcessor.getProperty("AllocateNewElmetIdForNextComponent"); + let elmtId = createId.invoke(); + let har1 = ESValue.load("@normalized:N&entry&com.example.Interop2use1&har1/src/main/ets/components/MainPage&1.0.0"); + let structObject = har1.getProperty("Child1"); + let component = structObject.instantiate(esundefined, param, esundefined, elmtId, esblank, extraInfo); + let create = structObject.getProperty("create"); + create.invoke(component); + return { + component: component, + name: "Child1", + }; +}), ((instance: ESValue) => {})); \ No newline at end of file diff --git a/arkui-plugins/test/demo/interop/provide_consume_interop.ets b/arkui-plugins/test/demo/interop/provide_consume_interop.ets new file mode 100644 index 000000000..48076c791 --- /dev/null +++ b/arkui-plugins/test/demo/interop/provide_consume_interop.ets @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// ArkTS1.2 +import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { ArkUICompatible, InteropComponent, Text, TextAttribute, Column, Component, Button, ButtonAttribute, ClickEvent, UserView, } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { State, StateDecoratedVariable, MutableState, stateOf, observableProxy, Observed, Track, Provide, Consume } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { MyText } from 'har2/src/main/ets/components/MainPage' +import { Child1 } from 'har1' + + +@Component +struct MyStateSample { + @Provide stateVar: MyText = new MyText(); + build() { + Column() { + Button(this.stateVar.text) + .onClick((e: ClickEvent) => { + this.stateVar.text += '~'; + }) + Child1() + } + } +} + +class MyText { + text: string = 'MyText'; +} + + +//ArkT1.1 +import { MyText } from 'har2/src/main/ets/components/MainPage' + +@Component +export struct Child1{ + @Consume stateVar: MyText; + build() { + Column() { + Button(this.stateVar.text) + .onClick(() => { + this.stateVar.text += '~'; + }) + } + } +} + + + +//transform 1.1struct 'Child1' to ArkUICompatible + +ArkUICompatible(__memo_context, ((__memo_id) + (252133223)), (() => { + let global = ESValue.getGlobal(); + let param = ESValue.instantiateEmptyObject(); + let createState = global.getProperty("createStateVariable"); + let setFindProvideInterop = global.getProperty("setFindProvideInterop"); + let findProvideInterop = ((providedPropName: string): Object => { + let provide = (this).findProvide(providedPropName); + if (((provide!?.getProxy()) === (undefined))) { + let setSource = ((value: Object) => { + provide!.set(value); + }); + let proxyState = createState.invoke(ESValue.wrap(provide!.get()), ESValue.wrap(setSource)); + let setProxy = ((value: Object) => { + proxyState.invokeMethod("set", ESValue.wrap(value)); + }); + let notifyCallback = ((propertyName: string) => { + proxyState.invokeMethod("notifyPropertyHasChangedPU"); + }); + configureState(provide!, proxyState, setProxy, notifyCallback); + } + let proxy = provide!.getProxy(); + return (proxy as ESValue).unwrap()!; + }); + setFindProvideInterop.invoke(findProvideInterop); + let extraInfo = ESValue.instantiateEmptyObject(); + extraInfo.setProperty("page", "har1/src/main/ets/components/MainPage"); + let esundefined = ESValue.wrap(undefined); + let blank = (() => {}); + let esblank = ESValue.wrap((blank as object)); + let viewStackProcessor = global.getProperty("ViewStackProcessor"); + let createId = viewStackProcessor.getProperty("AllocateNewElmetIdForNextComponent"); + let elmtId = createId.invoke(); + let har1 = ESValue.load("@normalized:N&entry&com.example.Interop2use1&har1/src/main/ets/components/MainPage&1.0.0"); + let structObject = har1.getProperty("Child1"); + let component = structObject.instantiate(esundefined, param, esundefined, elmtId, esblank, extraInfo); + let create = structObject.getProperty("create"); + create.invoke(component); + structObject.invokeMethod("resetFindInterop") + return { + component: component, + name: "Child1", + }; + }), ((instance: ESValue) => {})); \ No newline at end of file diff --git a/arkui-plugins/test/demo/interop/1.2State-1.1Link-example.ets b/arkui-plugins/test/demo/interop/state_link_interop.ets similarity index 96% rename from arkui-plugins/test/demo/interop/1.2State-1.1Link-example.ets rename to arkui-plugins/test/demo/interop/state_link_interop.ets index f11af005f..ec3778ef2 100644 --- a/arkui-plugins/test/demo/interop/1.2State-1.1Link-example.ets +++ b/arkui-plugins/test/demo/interop/state_link_interop.ets @@ -24,7 +24,7 @@ import { Child1 } from 'har1' @Component struct MyStateSample { - @State stateVar: new MyText(); + @State stateVar: MyText = new MyText(); build() { Column() { Button(this.stateVar.text) @@ -69,12 +69,12 @@ ArkUICompatible(__memo_context, ((__memo_id) + (252133223)), (() => { let global = ESValue.getGlobal(); let param = ESValue.instantiateEmptyObject(); let createState = global.getProperty("createStateVariable"); - let stateVar_SetSource = ((value: B) => { + let stateVar_SetSource = ((value: MyText) => { (this).stateVar = value; }); let stateVar_ProxyState = createState.invoke(ESValue.wrap((this).stateVar), ESValue.wrap(stateVar_SetSource)); (this).__backing_stateVar!.setProxy(stateVar_ProxyState); - let stateVar_SetProxy = ((value: B) => { + let stateVar_SetProxy = ((value: MyText) => { stateVar_ProxyState.invokeMethod("set", ESValue.wrap(value)); }); (this).__backing_stateVar!.setProxyValue = stateVar_SetProxy; diff --git a/arkui-plugins/test/demo/interop/state_prop_interop.ets b/arkui-plugins/test/demo/interop/state_prop_interop.ets new file mode 100644 index 000000000..99a073bbb --- /dev/null +++ b/arkui-plugins/test/demo/interop/state_prop_interop.ets @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// ArkTS1.2 +import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { ArkUICompatible, InteropComponent, Text, TextAttribute, Column, Component, Button, ButtonAttribute, ClickEvent, UserView, } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { State, StateDecoratedVariable, MutableState, stateOf, observableProxy, Observed, Track, Provide, Consume } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { MyText } from 'har2/src/main/ets/components/MainPage' +import { Child1 } from 'har1' + + +@Component +struct MyStateSample { + @State stateVar: MyText = new MyText(); + build() { + Column() { + Button(this.stateVar.text) + .onClick((e: ClickEvent) => { + this.stateVar.text += '~'; + }) + Child1({stateVar: this.stateVar, text: this.stateVar}) + } + } +} + +class MyText { + text: string = 'MyText'; +} + + +//ArkT1.1 +import { MyText } from 'har2/src/main/ets/components/MainPage' + +@Component +export struct Child1{ + @Prop stateVar: MyText; + @Prop text: MyText; + build() { + Column() { + Button(this.stateVar.text) + .onClick(() => { + this.stateVar.text += '~'; + }) + Button(this.text.text) + .onClick(() => { + this.text.text = 'ArkTS1.1'; + }) + } + } +} + + +//transform 1.1struct 'Child1' to ArkUICompatible + +ArkUICompatible(__memo_context, ((__memo_id) + (252133223)), (() => { + let global = ESValue.getGlobal(); + let param = ESValue.instantiateEmptyObject(); + let createState = global.getProperty("createStateVariable"); + param.setProperty("stateVar", (this).stateVar); + param.setProperty("text", (this).stateVar); + let extraInfo = ESValue.instantiateEmptyObject(); + extraInfo.setProperty("page", "har1/src/main/ets/components/MainPage"); + let esundefined = ESValue.wrap(undefined); + let blank = (() => {}); + let esblank = ESValue.wrap((blank as object)); + let viewStackProcessor = global.getProperty("ViewStackProcessor"); + let createId = viewStackProcessor.getProperty("AllocateNewElmetIdForNextComponent"); + let elmtId = createId.invoke(); + let har1 = ESValue.load("@normalized:N&entry&com.example.Interop2use1&har1/src/main/ets/components/MainPage&1.0.0"); + let structObject = har1.getProperty("Child1"); + let component = structObject.instantiate(esundefined, param, esundefined, elmtId, esblank, extraInfo); + let create = structObject.getProperty("create"); + create.invoke(component); + return { + component: component, + name: "Child1", + }; +}), ((instance: ESValue) => { + instance.invokeMethod("updateStateVars", ESValue.wrap({stateVar: this.stateVar, text: this.stateVar})); +})); \ No newline at end of file diff --git a/arkui-plugins/test/demo/localtest/build_config_template.json b/arkui-plugins/test/demo/localtest/build_config_template.json index 3855891d1..0d92ceecc 100755 --- a/arkui-plugins/test/demo/localtest/build_config_template.json +++ b/arkui-plugins/test/demo/localtest/build_config_template.json @@ -1,25 +1,45 @@ { - "plugins": { - "ui-syntax-plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/ui-syntax-plugins/index", - "ui_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/ui-plugins/index", - "memo_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/memo-plugins/index" - }, - - "compileFiles": [ - "./demo/localtest/entry/new.ets" - ], - - "packageName" : "entry", - - "buildType": "build", - "buildMode": "Debug", - "moduleRootPath": "./demo/localtest/entry/", - "sourceRoots": ["./"], - - "loaderOutPath": "./dist", - "cachePath": "./dist/cache", - - "buildSdkPath": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/", - - "dependentModuleList": [] + "plugins": { + "ui_syntax_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/ui-syntax-plugins/index", + "ui_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/ui-plugins/index", + "memo_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/memo-plugins/index" + }, + "compileFiles": [ + "./demo/localtest/entry/src/main/ets/pages/new.ets" + ], + "entryFiles": [ + "./demo/localtest/entry/src/main/ets/pages/new.ets" + ], + "buildMode": "Debug", + "projectRootPath": "./demo/localtest/", + "moduleRootPath": "./demo/localtest/entry/", + "cachePath": "./dist/cache", + "loaderOutPath": "./dist", + "compileSdkVersion": 20, + "compatibleSdkVersion": 20, + "bundleName": "com.example.myapplication", + "useNormalizedOHMUrl": true, + "buildType": "build", + "packageName": "entry", + "buildSdkPath": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/", + "sourceRoots": [ + "./" + ], + "moduleType": "shared", + "moduleName": "entry", + "dependentModuleList": [], + "hasMainModule": true, + "buildLoaderJson": "", + "integratedHsp": false, + "allowEmptyBundleName": false, + "declgenV2OutPath": "", + "externalApiPaths": [], + "level": { + "level": 20000, + "levelStr": "INFO", + "colour": "green" + }, + "isBuildConfigModified": false, + "aceModuleJsonPath": "./demo/localtest/entry/src/main/module.json5", + "es2pandaMode": 2 } \ No newline at end of file diff --git a/arkui-plugins/test/demo/localtest/entry/index.ets b/arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/index.ets similarity index 100% rename from arkui-plugins/test/demo/localtest/entry/index.ets rename to arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/index.ets diff --git a/arkui-plugins/test/demo/localtest/entry/new.ets b/arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/new.ets similarity index 36% rename from arkui-plugins/test/demo/localtest/entry/new.ets rename to arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/new.ets index acdb7c3c3..76b815073 100755 --- a/arkui-plugins/test/demo/localtest/entry/new.ets +++ b/arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/new.ets @@ -13,60 +13,62 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins -import { Text, TextAttribute, Column, Component, Button, ButtonAttribute, ClickEvent, UserView } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins -import { State, Link, StateDecoratedVariable, LinkDecoratedVariable,MutableState, stateOf, observableProxy } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { Text, Column, Component, Entry, Button, ClickEvent } from "@ohos.arkui.component" +import { State, Link, Prop } from "@ohos.arkui.stateManagement" import hilog from '@ohos.hilog' -function ArkUICompatible(init:(elmtId: number, instance: ESObject) => void, update: ((elmtId: number, instance: ESObject) => void)) { - -} - +@Entry @Component struct MyStateSample { - @State stateVar: string = "Parent"; - message: string = "var"; + @State stateVar: string = "state var"; + message: string = `click to change state variable, add **`; changeValue() { - this.stateVar+="~"; + this.stateVar+="**" } build() { - Column(undefined) { - Button("ParentChange").backgroundColor("#FFFF00FF") + Column() { + Button("clean variable").onClick((e: ClickEvent) => { this.stateVar = "state var" }) + Text("Hello World").fontSize(20) + Button(this.message).backgroundColor("#FFFF00FF") .onClick((e: ClickEvent) => { hilog.info(0x0000, 'testTag', 'On Click'); - this.changeValue(); + this.changeValue() }) Text(this.stateVar).fontSize(20) - ChildLink({stateVar: this.stateVar, stateVar1: this.stateVar, stateVar2: ""} as __Options_ChildLink) - } + Child({linkVar: this.stateVar, propVar: this.stateVar}) + }.margin(10) } } @Component -struct ChildLink { - @Link stateVar: string = "Child"; - @State stateVar1: string = "Child"; - @Link stateVar2: string = "Child"; - changeValue() { - this.stateVar+="~"; +struct Child { + @Link linkVar: string; + @Prop propVar: string = "Prop"; + + changeValue1() { + this.linkVar+="!!" } - build() { - Button("ChildChange").backgroundColor("#FFFF00FF") - .onClick((e: ClickEvent) => { - hilog.info(0x0000, 'testTag', 'On Click'); - this.changeValue(); - }) - Text(this.stateVar).fontSize(50) + + changeValue2() { + this.propVar+="~~" } -} -export class ComExampleTrivialApplication extends UserView { - getBuilder() { - hilog.info(0x0000, 'testTag', 'getBuilder'); - let wrapper = @memo () => { - hilog.info(0x0000, 'testTag', 'MyStateSample'); - MyStateSample(undefined); + build() { + Column() { + Button(`click to change Link variable, add symbol !!`) + .backgroundColor("#4169E1") + .onClick((e: ClickEvent) => { + hilog.info(0x0000, 'testTag', 'On Click'); + this.changeValue1() + }) + Button(`click to change Prop variable, add symbol ~~`) + .backgroundColor("#3CB371") + .onClick((e: ClickEvent) => { + hilog.info(0x0000, 'testTag', 'On Click'); + this.changeValue2() + }) + Text(`Link variable in child: ${this.linkVar}`).fontSize(30) + Text(`Prop variable in child: ${this.propVar}`).fontSize(30) } - return wrapper; } } \ No newline at end of file diff --git a/arkui-plugins/test/demo/localtest/entry/src/main/module.json5 b/arkui-plugins/test/demo/localtest/entry/src/main/module.json5 new file mode 100644 index 000000000..f2ff2c7f1 --- /dev/null +++ b/arkui-plugins/test/demo/localtest/entry/src/main/module.json5 @@ -0,0 +1,37 @@ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/localtest/entry/src/main/resources/base/profile/main_pages.json b/arkui-plugins/test/demo/localtest/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 000000000..bc9792df1 --- /dev/null +++ b/arkui-plugins/test/demo/localtest/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,6 @@ +{ + "src": [ + "pages/index", + "pages/new" + ] +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/animation/animatable-extend-basic.ets b/arkui-plugins/test/demo/mock/animation/animatable-extend-basic.ets new file mode 100644 index 000000000..91cb598ca --- /dev/null +++ b/arkui-plugins/test/demo/mock/animation/animatable-extend-basic.ets @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Text, Column, Component, Color, Curve } from "@ohos.arkui.component" +import { Entry } from "@ohos.arkui.component" + +@Entry +@Component +struct AnimatablePropertyExample { + build() { + Column() { + Text("AnimatableProperty") + .backgroundColor(Color.Red) + .animation({ duration: 2000, curve: Curve.Ease }) + .fontSize(20) + .animation({ duration: 2000, curve: Curve.Ease }) + .width("100%") + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/block-in-switch-case.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/block-in-switch-case.ets new file mode 100644 index 000000000..77df2cf84 --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/block-in-switch-case.ets @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Text, Column, Component } from "@ohos.arkui.component" + +@Component +struct SwitchCase { + num: int = 2; + + build() { + Column() { + switch (this.num) { + case 0: + break; + case 1: + Text('111'); + case 2: + { + Text('111'); + } + case 3: + { + Text('111'); + } + break; + { + Text('111'); + } + case 4: + { + Text('111'); + break; + } + case 5: + { + Text('111'); + } + break; + case 6: + { + break; + } + case 7: + { + break; + Text('111'); + } + default: + break; + } + Text('hello world') + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/else-if-in-content.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/else-if-in-content.ets new file mode 100644 index 000000000..87e537547 --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/else-if-in-content.ets @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Text, Column, Component } from "@ohos.arkui.component" + +@Component +struct ElseIf { + build() { + Column() { + if (true) { + } else if (false) { + Text('elseIf 1') + } else { + Text('else 1') + } + if (false) { + } else { + if (false) { + Text('elseIf 2') + } else { + Text('else 2') + } + } + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-break-in-nested-content.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-break-in-nested-content.ets new file mode 100644 index 000000000..4f522cc1a --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-break-in-nested-content.ets @@ -0,0 +1,26 @@ +import { Component, Column, Text } from "@ohos.arkui.component"; + +import hilog from "@ohos.hilog"; + +@Component +struct A { + build() { + Column() { + Column() { + Column() { + Column() { + if (false) { + hilog.info(0x0000, "very inside", "1"); + return; + Text("1"); + } + } + } + } + if (true) { + hilog.info(0x0000, "outside column", "2"); + Text("1"); + } + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-else-in-content.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-else-in-content.ets new file mode 100644 index 000000000..c9f98dbe9 --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-else-in-content.ets @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Text, Column, Component } from "@ohos.arkui.component" + +@Component +struct IfElse { + build() { + Column() { + if (true) { + if (false) { + Text('if-if') + } else if (true) { + Text('if-elseIf') + } else { + Text('if-else') + } + } else if (false) { + Text('elseIf') + } else { + Text('else') + return; + Text('after-return') + } + Text('hello world') + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-in-switch-in-content.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-in-switch-in-content.ets new file mode 100644 index 000000000..51b00add0 --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-in-switch-in-content.ets @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Text, Column, Component } from "@ohos.arkui.component" + +@Component +struct IfInSwitch { + num: string = '1'; + + build() { + Column() { + switch (this.num) { + case '-1': { + if (true) { + Text('case 1') + } else if (false) { + Text('case 2') + } else { + Text('case 3') + } + } + case '2': + if (true) { + Text('case 4') + } + } + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.units.d.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/non-builder-within-builder.ets similarity index 58% rename from arkui-plugins/test/local/@ohos.arkui.component.units.d.ets rename to arkui-plugins/test/demo/mock/builder-lambda/condition-scope/non-builder-within-builder.ets index c6a98e628..2579f1ba9 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.units.d.ets +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/non-builder-within-builder.ets @@ -13,17 +13,31 @@ * limitations under the License. */ -import { Resource } from "@ohos.arkui.external.resource" -import { Color } from "@ohos.arkui.component.enums"; +import { Component, Builder } from "@ohos.arkui.component" -export type PX = string; -export type VP = string | number; -export type FP = string; -export type LPX = string; -export type Percentage = string; +@Builder +function TestComponent( + init: TestInitCallback, + update: TestUpdateCallback, +): void {} -export type Dimension = PX | VP | FP | LPX | Percentage | Resource; +type TestInitCallback = () => void; +type TestUpdateCallback = () => void; -export type Length = string | number | Resource; +@Component +struct MyStateSample { + build() { + TestComponent( + () => { + if (true) { -export type ResourceColor = Color | number | string | Resource; \ No newline at end of file + } + }, + () => { + if (false) { + + } + } + ); + } +} diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/switch-case-in-content.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/switch-case-in-content.ets new file mode 100644 index 000000000..8944b5e0a --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/switch-case-in-content.ets @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Text, Column, Component } from "@ohos.arkui.component" + +@Component +struct SwitchCase { + num: string = '1'; + + build() { + Column() { + switch (this.num) { + case '0': + Text('case 0') + case '1': + Text('case 1') + break; + Text('after break') + case '2': + Text('case 2') + return; + Text('after return') + default: + Text('default') + } + Text('hello world') + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/switch-in-if-in-content.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/switch-in-if-in-content.ets new file mode 100644 index 000000000..2e2a0f047 --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/switch-in-if-in-content.ets @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Text, Column, Component } from "@ohos.arkui.component" + +@Component +struct SwitchInIf { + num: string = '1'; + + build() { + Column() { + if (true) { + switch (this.num) { + case '0': + Text('case 0') + } + } + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/with-builder.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/with-builder.ets new file mode 100644 index 000000000..c2a8eaab8 --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/with-builder.ets @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Text, Column, Component, Builder, BuilderParam, WrappedBuilder, wrapBuilder } from "@ohos.arkui.component" + +@Builder +function MyBuilder(): void { + if (true) { + Text('within Builder function'); + } +} + +@Builder +function ParamBuilder(@Builder param: (() => void) = () => { + if (true) { + Text('within Builder parameter') + } +}): void { + param(); +} + +const wBuilder = wrapBuilder(ParamBuilder); + +@Component +struct MyStruct { + @Builder + public myBuilderMethod() { + if (true) { + Text('within Builder method'); + } + } + + build() { + Column() { + wBuilder.builder(@Builder () => { + if (true) { + Text('with Builder lambda'); + } + }); + Child({ + myBuilderParam: () => { + if (true) { + Text('within Builder property') + } + this.myBuilderMethod(); + } + }) + } + } +} + +@Component +struct Child { + @BuilderParam myBuilderParam: () => void = () => { + if (true) { + Text('within BuilderParam property'); + } + } + build() { + if (true) { + Text('within struct build'); + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/simple-component.ets b/arkui-plugins/test/demo/mock/builder-lambda/simple-component.ets index 4f0f3b5cf..79886cd63 100644 --- a/arkui-plugins/test/demo/mock/builder-lambda/simple-component.ets +++ b/arkui-plugins/test/demo/mock/builder-lambda/simple-component.ets @@ -13,8 +13,8 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" -import { Column, UIColumnAttribute } from "arkui.component.column" +import { memo } from "@ohos.arkui.stateManagement" +import { Column, ColumnAttribute } from "arkui.component.column" class MyStateSample { @memo build() { diff --git a/arkui-plugins/test/demo/mock/builder-lambda/style-with-receiver.ets b/arkui-plugins/test/demo/mock/builder-lambda/style-with-receiver.ets index 92d842ff5..a9b81be68 100644 --- a/arkui-plugins/test/demo/mock/builder-lambda/style-with-receiver.ets +++ b/arkui-plugins/test/demo/mock/builder-lambda/style-with-receiver.ets @@ -14,16 +14,16 @@ */ import { memo } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins -import { Text, UITextAttribute, Column, Component} from "@ohos.arkui.component" +import { Text, TextAttribute, Column, Component} from "@ohos.arkui.component" import hilog from '@ohos.hilog' -@memo function cardStyle(this: UITextAttribute, num: number, str: string): this { +@memo function cardStyle(this: TextAttribute, num: number, str: string): this { this.fontSize(num); this.backgroundColor(num); return this; } -@memo function style22(this: UITextAttribute): this { +@memo function style22(this: TextAttribute): this { this.fontWeight(700); return this; } diff --git a/arkui-plugins/test/demo/mock/common-utils/annotation.ets b/arkui-plugins/test/demo/mock/common-utils/annotation.ets new file mode 100644 index 000000000..e8a78a587 --- /dev/null +++ b/arkui-plugins/test/demo/mock/common-utils/annotation.ets @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@Retention({policy:"SOURCE"}) @interface TestAnno {} + +type TestType = number; + +() => {}; + +class A { + prop: number = 1; + + method(arg1: number): void { + const a: number = arg1; + } +} + +interface __A { + prop: number; +} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.d.ets b/arkui-plugins/test/demo/mock/component/declare-component.ets similarity index 64% rename from arkui-plugins/test/local/@ohos.arkui.component.d.ets rename to arkui-plugins/test/demo/mock/component/declare-component.ets index 0f74a9db4..aa0142368 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.d.ets +++ b/arkui-plugins/test/demo/mock/component/declare-component.ets @@ -13,10 +13,14 @@ * limitations under the License. */ -export * from "@ohos.arkui.component.customComponent"; -export * from "@ohos.arkui.component.common"; -export * from "@ohos.arkui.component.column"; -export * from "@ohos.arkui.component.text"; -export * from "@ohos.arkui.component.styledString"; -export * from "@ohos.arkui.component.enums"; -export * from "@ohos.arkui.component.units"; \ No newline at end of file +import { Component, ResourceStr, Builder} from "@ohos.arkui.component" +import { Prop, State } from "@ohos.arkui.stateManagement" + +@Component +export declare struct SwipeRefresher { + @Prop content?: ResourceStr; + @Prop isLoading: boolean; + @State code: number; + + @Builder build(): void; +} diff --git a/arkui-plugins/test/demo/mock/component/for-each.ets b/arkui-plugins/test/demo/mock/component/for-each.ets new file mode 100644 index 000000000..d615581b6 --- /dev/null +++ b/arkui-plugins/test/demo/mock/component/for-each.ets @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Text, WrappedBuilder, Column, ForEach } from "@kit.ArkUI" + +interface Person { + name: string; + age: number +} + +class AB { + per: string = 'hello'; + bar: Array = new Array('xx', 'yy', 'zz') +} + +@Component +struct ImportStruct { + arr: string[] = ['a', 'b', 'c'] + getArray() { + return new Array({ name: 'LiHua', age:25 } as Person, { name: 'Amy', age:18 } as Person) + } + + build() { + Column() { + ForEach(this.arr, (item: string) => { + Text(item) + }) + ForEach(this.getArray(), (item: Person) => { + Text(item.name) + }) + ForEach((new AB()).bar, (item: string) => { + Text(item) + }) + ForEach((new AB()).bar, () => {}) + ForEach(this.getArray(), () => {}) + ForEach(new Array('1', '2'), () => { + ForEach(new Array('1', '2'), (item: string) => { + Text(item) + }) + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/builder-param/optional-builder-param.ets b/arkui-plugins/test/demo/mock/decorators/builder-param/optional-builder-param.ets new file mode 100644 index 000000000..4bc1dca03 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/builder-param/optional-builder-param.ets @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Entry, Builder, BuilderParam, Column, Text, Row } from "@kit.ArkUI" + +@Builder +function showTextBuilder() { + Text('Hello World') +} + +@Component +struct Child { + @BuilderParam customBuilderParam2?: () => void; + @BuilderParam customBuilderParam1: () => void = showTextBuilder; + + build() { + Row() { + if (this.customBuilderParam2) { + (this.customBuilderParam2 as () => void)() + } + if (this.customBuilderParam2) { + (this.customBuilderParam2!)() + } + this.customBuilderParam1() + } + } +} + +@Component +struct Parent { + @Builder componentBuilder() { + Text('Parent builder') + } + + build() { + Column() { + Child({ customBuilderParam2: () => { this.componentBuilder() } }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/computed/computed-in-observedv2-class.ets b/arkui-plugins/test/demo/mock/decorators/computed/computed-in-observedv2-class.ets new file mode 100644 index 000000000..864244caa --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/computed/computed-in-observedv2-class.ets @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Computed, ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class Name { + @Trace firstName: string = 'Hua'; + @Trace lastName: string = 'Li'; + + @Computed + get fullName(): string { + console.info('---------Computed----------'); + return this.firstName + ' ' + this.lastName; + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/computed/computed-in-struct.ets b/arkui-plugins/test/demo/mock/decorators/computed/computed-in-struct.ets new file mode 100644 index 000000000..ec221d404 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/computed/computed-in-struct.ets @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2, Column, Button, Divider, Text } from "@ohos.arkui.component" +import { Computed, Local } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Index { + @Local firstName: string = 'Li'; + @Local lastName: string = 'Hua'; + age: number = 20; + + @Computed + get fullName(): string { + console.info("---------Computed----------"); + return this.firstName + ' ' + this.lastName + this.age; + } + + build() { + Column() { + Text(this.lastName + ' ' + this.firstName) + Text(this.lastName + ' ' + this.firstName) + Divider() + Text(this.fullName) + Text(this.fullName) + Button('changed lastName').onClick((e) => { + this.lastName += 'a'; + }) + Button('changed age').onClick((e) => { + this.age++; + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/computed/computed-no-return-type.ets b/arkui-plugins/test/demo/mock/decorators/computed/computed-no-return-type.ets new file mode 100644 index 000000000..c592a2300 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/computed/computed-no-return-type.ets @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Computed, ObservedV2, Trace } from "@ohos.arkui.stateManagement" +import { ComponentV2, Column, Button, Divider, Text } from "@ohos.arkui.component" +import { Computed, Local } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class Name { + @Trace firstName: string = 'Hua'; + @Trace lastName: string = 'Li'; + age: number = 20; + + @Computed + get fullName() { + if (this.age >= 20) { + return new Array(0, 1, 2); + } + return this.firstName + ' ' + this.lastName; + } +} + +@ComponentV2 +struct Index { + @Local firstName: string = 'Li'; + @Local lastName: string = 'Hua'; + age: number = 20; + + @Computed + get fullName() { + if (this.age >= 20) { + return 500 + } + return this.firstName + ' ' + this.lastName + this.age; + } + + @Computed + get num5() { + return 5; + } + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/computed/static-computed.ets b/arkui-plugins/test/demo/mock/decorators/computed/static-computed.ets new file mode 100644 index 000000000..55efc5b71 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/computed/static-computed.ets @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2 } from "@ohos.arkui.component"; +import { Computed, ObservedV2, Trace, Local } from "@ohos.arkui.stateManagement"; + +@ObservedV2 +class Name { + @Trace static firstName: string = 'Hua'; + @Trace static lastName: string = 'Li'; + + @Computed + static get fullName(): string { + return Name.firstName + ' ' + Name.lastName; + } +} + +@ComponentV2 +struct Parent { + @Local static localVar1: string = 'stateVar1'; + @Local static localVar2: number = 50; + + + @Computed + static get fullName(): string { + return Parent.localVar1; + } + + build() {} +} + +@ComponentV2 +struct Parent2 { + @Computed + static get fullName(): string { + return Name.firstName + ' ' + Name.lastName; + } + + @Computed + static get fullName2(): string { + return Parent.localVar1; + } + + build() {} +} + +@ObservedV2 +class Name2 { + @Computed + static get fullName(): string { + return Name.firstName + ' ' + Name.lastName; + } +} diff --git a/arkui-plugins/test/demo/mock/decorators/custom-dialog/base-custom-dialog.ets b/arkui-plugins/test/demo/mock/decorators/custom-dialog/base-custom-dialog.ets new file mode 100644 index 000000000..e527defd1 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/custom-dialog/base-custom-dialog.ets @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Text, Column, Component, Entry, Button, ClickEvent } from "@ohos.arkui.component" +import { State, Link, Prop } from "@ohos.arkui.stateManagement" +import { + CustomDialog, + CustomDialogController, + DismissDialogAction, + DismissReason, + DialogAlignment, + CustomDialogControllerOptions +} from "@ohos.arkui.component" +import hilog from '@ohos.hilog' + +@CustomDialog +struct CustomDialogExample { + aaController?: CustomDialogController; + @State text: string = 'text'; + cancel: () => void = () => { + } + confirm: () => void = () => { + } + @State hh: string = 'nihao' + build() { + Column() { + Text('CustomDialog One') + .fontSize(30) + .height(100) + Button('Close') + .onClick((e: ClickEvent) => { + if (this.aaController != undefined) { + this.aaController!.close(); + } + }) + .margin(20) + } + } +} + +@Component +struct CustomDialogUser { + dialogController: CustomDialogController | null = new CustomDialogController({ + builder: CustomDialogExample({ + cancel: ()=> { this.onCancel(); }, + confirm: ()=> { this.onAccept(); } + }), + cancel: this.existApp, + autoCancel: true, + alignment: DialogAlignment.Center, + offset: { dx: 0, dy: -20 }, + gridCount: 4, + showInSubWindow: true, + isModal: true, + customStyle: false, + cornerRadius: 10, + focusable: true + }) + + aboutToDisappear() { + this.dialogController = null; + } + + onCancel() { + console.info('Callback when the first button is clicked'); + } + + onAccept() { + console.info('Callback when the second button is clicked'); + } + + existApp() { + console.info('Click the callback in the blank area'); + } + + build() { + Column() { + Button('click me') + .onClick((e: ClickEvent) => { + if (this.dialogController != null) { + this.dialogController!.open(); + } + }).backgroundColor(0x317aff) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/custom-dialog/builder-dialog-options.ets b/arkui-plugins/test/demo/mock/decorators/custom-dialog/builder-dialog-options.ets new file mode 100644 index 000000000..e041a012e --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/custom-dialog/builder-dialog-options.ets @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Text, Column, Component, Builder } from "@ohos.arkui.component" +import { CustomDialog, CustomDialogController, CustomDialogControllerOptions } from "@kit.ArkUI" +import hilog from '@ohos.hilog' + +@Builder function builder1(str: string) {} + +@Builder function builder2() {} + +@Component +struct CustomDialogUser { + dialogController: CustomDialogController | null = new CustomDialogController({ + builder: () => { builder1('nihao') }, + }) + + build() { + Column() {} + } +} + +@Component +struct CustomDialogUser2 { + dialogController: CustomDialogController | null = new CustomDialogController({ + builder: builder2, + }) + + build() { + Column() {} + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/custom-dialog/controller-in-build.ets b/arkui-plugins/test/demo/mock/decorators/custom-dialog/controller-in-build.ets new file mode 100644 index 000000000..c87bc2e13 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/custom-dialog/controller-in-build.ets @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Text, Column, Component, Button, ClickEvent } from "@ohos.arkui.component" +import { State, Link, Prop } from "@ohos.arkui.stateManagement" +import { CustomDialog, CustomDialogController, CustomDialogControllerOptions } from "@kit.ArkUI" +import hilog from '@ohos.hilog' + +@CustomDialog +struct CustomDialogExample { + aaController?: CustomDialogController; + @State text: string = 'text'; + @State hh: string = 'nihao' + build() { + Column() { + Text('CustomDialog One') + } + } +} + +@Component +struct CustomDialogUser { + + build() { + Column() { + Button('click me') + .onClick((e: ClickEvent) => { + let dialogController: CustomDialogController | undefined = new CustomDialogController({ + builder: CustomDialogExample({}) + }) + }).backgroundColor(0x317aff) + } + } +} diff --git a/arkui-plugins/test/demo/mock/decorators/custom-dialog/controller-in-method.ets b/arkui-plugins/test/demo/mock/decorators/custom-dialog/controller-in-method.ets new file mode 100644 index 000000000..677b62982 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/custom-dialog/controller-in-method.ets @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, CustomDialog, CustomDialogController } from "@ohos.arkui.component" +import hilog from '@ohos.hilog' + +@CustomDialog +struct CustomDialogExample { + aaController?: CustomDialogController; + build() {} +} + +@Component +struct CustomDialogUser { + dialogController: CustomDialogController | null = new CustomDialogController({ + builder: CustomDialogExample({}), + }); + + updateController1() { + this.dialogController = new CustomDialogController({ + builder: CustomDialogExample({}), + autoCancel: true, + }); + } + + updateController2() { + let temp = new CustomDialogController({ + builder: CustomDialogExample({}), + autoCancel: true, + }); + this.dialogController = temp; + } + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/custom-dialog/declare-custom-dialog.ets b/arkui-plugins/test/demo/mock/decorators/custom-dialog/declare-custom-dialog.ets new file mode 100644 index 000000000..72cc017a5 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/custom-dialog/declare-custom-dialog.ets @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { State } from "@ohos.arkui.stateManagement" +import { CustomDialog, CustomDialogController, ComponentV2, Component, Builder } from "@ohos.arkui.component" + +@CustomDialog +export declare struct CustomDialogExample { + aaController?: CustomDialogController; + @State text: string; + hh: string + + @Builder build(): void; +} + +@Component +struct CustomDialogUserV1 { + dialogController: CustomDialogController | null = new CustomDialogController({ + builder: CustomDialogExample(), + }) + + build() {} +} + +@ComponentV2 +struct CustomDialogUserV2 { + dialogController: CustomDialogController | null = new CustomDialogController({ + builder: CustomDialogExample(), + }) + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/custom-dialog/extends-dialog-controller.ets b/arkui-plugins/test/demo/mock/decorators/custom-dialog/extends-dialog-controller.ets new file mode 100644 index 000000000..3829a2b74 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/custom-dialog/extends-dialog-controller.ets @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Text, Column, Component, Entry, Button, ClickEvent } from "@ohos.arkui.component" +import { CustomDialog, CustomDialogController, CustomDialogControllerOptions } from "@kit.ArkUI" + +@CustomDialog +struct CustomDialogExample { + aaController?: CustomDialogController; + + build() { + Column() {} + } +} + +class DialogControllerV2 extends CustomDialogController { + options: CustomDialogControllerOptions; + + constructor(options: CustomDialogControllerOptions) { + super(options); + this.options = options; + } +} + +class DialogControllerV3 extends DialogControllerV2 { + options: CustomDialogControllerOptions; + + constructor(options: CustomDialogControllerOptions) { + super(options); + this.options = options; + } +} + +@Component +struct CustomDialogUser { + dialogController: CustomDialogController | null = new CustomDialogController({ + gridCount: 4, + showInSubWindow: true, + builder: CustomDialogExample(), + } as CustomDialogControllerOptions) as DialogControllerV3 + + build() { + Column() { + Button('click me') + .onClick((e: ClickEvent) => { + if (this.dialogController != null) { + this.dialogController!.open(); + } + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/decorator-no-type.ets b/arkui-plugins/test/demo/mock/decorators/decorator-no-type.ets new file mode 100644 index 000000000..65065c3e5 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/decorator-no-type.ets @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, ComponentV2, CustomDialog } from "@ohos.arkui.component" +import { State, Prop, Provide, Event, Local, Param } from "@ohos.arkui.stateManagement" +import { Provider, Consumer, Once, Observed, ObservedV2, Trace, Track } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + + constructor(num: number) { + this.num = num; + } +} + +enum StateType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@ObservedV2 +class OO { + @Trace hi = '150' +} + +@Observed +class RR { + @Track hi = '150' +} + +@Component +struct Parent { + @State stateVar1 = new Per(6); + @Prop stateVar2 = new Array(3, 6, 8); + @Provide stateVar3 = StateType.TYPE3; + stateVar8 = (sr: string) => { + }; + stateVar9 = new Date('2025-4-23'); + @Provide({ alias: 'me0', allowOverride: false }) stateVar11113 = true; + @Provide stateVar11114 = undefined; + @State stateVar11115 = null; + + build() { + } +} + +@ComponentV2 +struct V2Parent { + @Once @Param stateVar4 = new Set(new Array('aa', 'bb')); + @Local stateVar5 = [true, false]; + @Local stateVar6 = new Array(new Per(7), new Per(11)); + stateVar7 = [new Per(7), new Per(11)]; + @Event stateVar8 = (sr: string) => { + }; + @Param stateVar9 = new Date('2025-4-23'); + @Param stateVar10 = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Once @Param stateVar11 = 0.0; + @Provider stateVar12 = new Per(6); + @Consumer stateVar11111 = 'stateVar1'; + @Provider('var') stateVar11188 = ''; + @Consumer('nihao') stateVar11112 = 50; + + build() { + } +} + +@CustomDialog +struct CC { + @Once @Param stateVar4 = new Set(new Array('aa', 'bb')); + @Local stateVar5 = [true, false]; + @Local stateVar6 = new Array(new Per(7), new Per(11)); + stateVar7 = [new Per(7), new Per(11)]; + @Event stateVar8 = (sr: string) => { + }; + @Param stateVar9 = new Date('2025-4-23'); + @Param stateVar10 = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Once @Param stateVar11 = 0.0; + @Provider stateVar12 = new Per(6); + @Consumer stateVar11111 = 'stateVar1'; + @Provider('var') stateVar11188 = ''; + @Consumer('nihao') stateVar11112 = 50; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/event/event-initialize.ets b/arkui-plugins/test/demo/mock/decorators/event/event-initialize.ets new file mode 100644 index 000000000..a596e9324 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/event/event-initialize.ets @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2, Column, Text } from "@ohos.arkui.component" +import { Event, Param, Local } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Child { + @Param index: number = 0; + @Event changeIndex: (val: number) => void; + @Event testEvent: (val: number) => number; + @Event testEvent2: (val: number) => number = (val: number) => { return val }; + + build() { + Column() { + Text(`Child index: ${this.index}`) + .onClick((e) => { + this.changeIndex(20); + console.log(`after changeIndex ${this.index}`); + }) + } + } +} + +@ComponentV2 +struct Index { + @Local index: number = 0; + + build() { + Column() { + Child({ + index: this.index, + changeIndex: (val: number) => { + this.index = val; + console.log(`in changeIndex ${this.index}`); + } + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/local/local-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/local/local-basic-type.ets new file mode 100644 index 000000000..178326815 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/local/local-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2 } from "@ohos.arkui.component" +import { Local } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Parent { + @Local localVar1: string = 'stateVar1'; + @Local localVar2: number = 50; + @Local localVar3: boolean = true; + @Local localVar4: undefined = undefined; + @Local localVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/local/local-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/local/local-complex-type.ets new file mode 100644 index 000000000..7a0d9f40e --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/local/local-complex-type.ets @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2 } from "@ohos.arkui.component" +import { Local } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } +} + +enum StateType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@ComponentV2 +struct Parent { + @Local localVar1: Per = new Per(6); + @Local localVar2: Array = new Array(3,6,8); + @Local localVar3: StateType = StateType.TYPE3; + @Local localVar4: Set = new Set(new Array('aa', 'bb')); + @Local localVar5: boolean[] = [true, false]; + @Local localVar6: Array = new Array(new Per(7), new Per(11)); + @Local localVar7: Per[] = [new Per(7), new Per(11)]; + @Local localVar9: Date = new Date('2025-4-23'); + @Local localVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Local localVar11: string | number = 0.0; + @Local localVar12: Set | Per = new Per(6); + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/local/static-local.ets b/arkui-plugins/test/demo/mock/decorators/local/static-local.ets new file mode 100644 index 000000000..4d84fcbc2 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/local/static-local.ets @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2 } from "@ohos.arkui.component"; +import { Local } from "@ohos.arkui.stateManagement"; + +class ABB {} + +@ComponentV2 +struct Parent { + @Local static localVar1: string = 'stateVar1'; + @Local static localVar2: number = 50; + @Local static localVar3: ABB = new ABB(); + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/localstoragelink/localstoragelink-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/localstoragelink/localstoragelink-complex-type.ets new file mode 100644 index 000000000..4a1099b23 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/localstoragelink/localstoragelink-complex-type.ets @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Entry } from "@ohos.arkui.component" +import { LocalStorageLink } from "@ohos.arkui.stateManagement" + +class Person{ + name: string = '' + constructor(name: string){} +} + +enum Status { + Success = 200, + NotFound = 404, + ServerError = 500 +} + +@Entry +@Component +struct MyStateSample { + @LocalStorageLink('Prop1') arrayA: number[] = [1,2,3]; + @LocalStorageLink('Prop2') objectA: Object = {}; + @LocalStorageLink('Prop3') dateA: Date = new Date('2021-08-08'); + @LocalStorageLink('Prop4') setA: Set = new Set(); + @LocalStorageLink('Prop5') mapA: Map = new Map(); + //@LocalStorageLink('Prop6') unionA: string | undefined = ""; + @LocalStorageLink('Prop7') classA: Person = new Person("John"); + @LocalStorageLink('Prop8') enumA: Status = Status.NotFound; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/localstoragelink/localstoragelink-primitive-type.ets b/arkui-plugins/test/demo/mock/decorators/localstoragelink/localstoragelink-primitive-type.ets new file mode 100644 index 000000000..8bc882e51 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/localstoragelink/localstoragelink-primitive-type.ets @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Entry } from "@ohos.arkui.component" +import { LocalStorageLink } from "@ohos.arkui.stateManagement" + +@Entry +@Component +struct MyStateSample { + @LocalStorageLink('Prop1') numA: number = 33; + @LocalStorageLink('Prop2') stringA: string = 'AA'; + @LocalStorageLink('Prop3') booleanA: boolean = true; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/localstorageprop-ref/localstorageprop-ref-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/localstorageprop-ref/localstorageprop-ref-complex-type.ets new file mode 100644 index 000000000..72cc3f9c7 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/localstorageprop-ref/localstorageprop-ref-complex-type.ets @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { LocalStoragePropRef } from "@ohos.arkui.stateManagement" + +class Person{ + name: string = '' + constructor(name: string){} +} + +enum Status { + Success = 200, + NotFound = 404, + ServerError = 500 +} + +@Component +struct MyStateSample { + @LocalStoragePropRef('Prop1') arrayB: number[] = [1,2,3]; + @LocalStoragePropRef('Prop2') objectB: Object = {}; + @LocalStoragePropRef('Prop3') dateB: Date = new Date('2021-09-09'); + @LocalStoragePropRef('Prop4') setB: Set = new Set(); + @LocalStoragePropRef('Prop5') mapB: Map = new Map(); + @LocalStoragePropRef('Prop7') classB: Person = new Person("Kevin"); + @LocalStoragePropRef('Prop8') enumB: Status = Status.NotFound; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/localstorageprop-ref/localstorageprop-ref-primitive-type.ets b/arkui-plugins/test/demo/mock/decorators/localstorageprop-ref/localstorageprop-ref-primitive-type.ets new file mode 100644 index 000000000..dce193d2f --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/localstorageprop-ref/localstorageprop-ref-primitive-type.ets @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { LocalStoragePropRef } from "@ohos.arkui.stateManagement" + +@Component +struct MyStateSample { + @LocalStoragePropRef('Prop1') numB: number = 43; + @LocalStoragePropRef('Prop2') stringB: string = 'BB'; + @LocalStoragePropRef('Prop3') booleanB: boolean = false; + @LocalStoragePropRef('Prop4') undefinedB: undefined = undefined; + @LocalStoragePropRef('Prop5') nullB: null = null; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.column.d.ets b/arkui-plugins/test/demo/mock/decorators/monitor/enum-monitor-params.ets similarity index 47% rename from arkui-plugins/test/local/@ohos.arkui.component.column.d.ets rename to arkui-plugins/test/demo/mock/decorators/monitor/enum-monitor-params.ets index 915dc5ceb..bef93f0e0 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.column.d.ets +++ b/arkui-plugins/test/demo/mock/decorators/monitor/enum-monitor-params.ets @@ -13,29 +13,37 @@ * limitations under the License. */ -import { memo } from '../stateManagement/runtime'; -import { ComponentBuilder, CommonMethod, PointLightStyle } from './common'; -import { HorizontalAlign, FlexAlign } from './enums'; +import { ComponentV2 } from "@ohos.arkui.component" +import { Monitor, IMonitor, ObservedV2, Trace, Local } from "@ohos.arkui.stateManagement" -export declare interface ColumnOptions { - space?: string | number; +class FFF { + ff: GGG = new GGG() } -export declare interface ColumnAttribute extends CommonMethod { - @memo - alignItems(value: HorizontalAlign): this; - @memo - justifyContent(value: FlexAlign): this; - @memo - pointLight(value: PointLightStyle): this; - @memo - reverse(isReversed?: boolean): this; +class GGG {} + +enum MonitorNames { + name1 = "strArr.0", + name2 = "name", + name3 = MonitorNames.name1, + name4 = "varF.ff", +} + +@ObservedV2 +class Info { + @Trace name: string = "Tom"; + @Trace strArr: string[] = ["North", "east"]; + + @Monitor([MonitorNames.name1, MonitorNames.name2, MonitorNames.name3]) + changeCCC(monitor: IMonitor) {} } -@memo -@ComponentBuilder -export declare function Column ( - options?: ColumnOptions, - @memo - content?: () => void -): ColumnAttribute; \ No newline at end of file +@ComponentV2 +struct Index { + @Local varF: FFF = new FFF() + + @Monitor([MonitorNames.name4]) + changeEEE(monitor: IMonitor) {} + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/monitor/monitor-before-state-variable.ets b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-before-state-variable.ets new file mode 100644 index 000000000..2a0e4d4c6 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-before-state-variable.ets @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2 } from "@ohos.arkui.component" +import { Monitor, Local, IMonitor } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Index { + @Monitor(["message", "name"]) + onStrChange1(monitor: IMonitor) { + monitor.dirty.forEach((path: string) => { + console.info(`${path} changed from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`); + }); + } + @Monitor(["message", "name"]) + onStrChange2(monitor: IMonitor) { + monitor.dirty.forEach((path: string) => { + console.info(`${path} changed from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`); + }); + } + @Monitor(["name"]) + onStrChange3(monitor: IMonitor) { + monitor.dirty.forEach((path: string) => { + console.info(`${path} changed from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`); + }); + } + @Local message: string = "Hello World"; + @Local name: string = "Tom"; + @Local age: number = 24; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/monitor/monitor-in-observedv2-class.ets b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-in-observedv2-class.ets new file mode 100644 index 000000000..a10548d25 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-in-observedv2-class.ets @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2, Column, Button } from "@ohos.arkui.component" +import { Monitor, IMonitor, ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class Info { + @Trace name: string = "Tom"; + @Trace region: string = "North"; + @Trace job: string = "Teacher"; + age: number = 25; + + @Monitor(["name"]) + onNameChange(monitor: IMonitor) { + console.info(`name change from ${monitor.value()?.before} to ${monitor.value()?.now}`); + } + + @Monitor(["age"]) + onAgeChange(monitor: IMonitor) { + console.info(`age change from ${monitor.value()?.before} to ${monitor.value()?.now}`); + } + + @Monitor(["region", "job"]) + onChange(monitor: IMonitor) { + monitor.dirty.forEach((path: string) => { + console.info(`${path} change from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`); + }) + } +} + +@ComponentV2 +struct Index { + info: Info = new Info(); + build() { + Column() { + Button("change name") + .onClick((e) => { + this.info.name = "Jack"; + }) + Button("change age") + .onClick((e) => { + this.info.age = 26; + }) + Button("change region") + .onClick((e) => { + this.info.region = "South"; + }) + Button("change job") + .onClick((e) => { + this.info.job = "Driver"; + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/monitor/monitor-in-struct.ets b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-in-struct.ets new file mode 100644 index 000000000..412af0371 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-in-struct.ets @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2, Column, Button } from "@ohos.arkui.component" +import { Monitor, Local, IMonitor } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Index { + @Local message: string = "Hello World"; + @Local name: string = "Tom"; + @Local age: number = 24; + @Monitor(["message", "name"]) + onStrChange(monitor: IMonitor) { + monitor.dirty.forEach((path: string) => { + console.info(`${path} changed from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`); + }); + } + + build() { + Column() { + Button("change string") + .onClick((e) => { + this.message += "!"; + this.name = "Jack"; + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.customComponent.d.ets b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-params.ets similarity index 34% rename from arkui-plugins/test/local/@ohos.arkui.component.customComponent.d.ets rename to arkui-plugins/test/demo/mock/decorators/monitor/monitor-params.ets index 728c949e7..395c70a69 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.customComponent.d.ets +++ b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-params.ets @@ -13,45 +13,66 @@ * limitations under the License. */ -import { memo } from "@ohos.arkui.stateManagement.runtime"; -import { ComponentBuilder, CommonMethod } from "@ohos.arkui.component.common"; -import { Length, ResourceColor } from "@ohos.arkui.component.units"; - -@Retention({policy: "SOURCE"}) -export declare @interface Component {}; - -@Retention({policy: "SOURCE"}) -export declare @interface Entry { routeName: string }; - -@Retention({policy: "SOURCE"}) -export declare @interface Reusable {}; - -export declare abstract class CustomComponent, T_Options> implements - CommonMethod { - - @memo - @ComponentBuilder - static $_instantiate, S_Options>( - factory: () => S, - initializers?: S_Options, - @memo - content?: () => void - ): S; - - // Life cycle for custom component - aboutToAppear(): void; - aboutToDisappear(): void; - aboutToReuse(): void; - aboutToRecycle(): void; - - @memo - build(): void; - - // Implementation of common method - @memo - width(w: Length): this; - @memo - height(h: Length): this; - @memo - backgroundColor(color: ResourceColor): this; +import { ComponentV2, Column, Button } from "@ohos.arkui.component" +import { Monitor, IMonitor, ObservedV2, Trace, Local } from "@ohos.arkui.stateManagement" + +class AAA { + aaa: BBB = new BBB() +} + +class BBB { + bbb: CCC = new CCC() +} + +class CCC { + ccc: Array = new Array(new DDD(10), new DDD(12), new DDD(16)) +} + +class DDD { + dd: number[] + constructor(dd: number) { + this.dd = []; + for(let i = 4; i < dd; i++) { + this.dd.push(i); + } + } +} + +class EEE { + ee: FFF = new FFF() +} + +class FFF { + ff: GGG = new GGG() +} + +class GGG {} + +@ObservedV2 +class Info { + @Trace name: string = "Tom"; + @Trace strArr: string[] = ["North", "east"]; + @Trace job: AAA = new AAA(); + age: number = 25; + + @Monitor(["name"]) + onNameChange(monitor: IMonitor) {} + + @Monitor(["strArr.0", "age"]) + onAgeChange(monitor: IMonitor) {} + + @Monitor(["job.aaa.bbb.ccc.1.dd.0"]) + onJobChange(monitor: IMonitor) {} +} + +@ComponentV2 +struct Index { + @Local per: EEE = new EEE() + @Local v1: boolean = true + @Local numArr: string[] = ['1','3','5'] + + @Monitor(["per.ee.ff", "v1", "numArr.1"]) + onPerChange(monitor: IMonitor) {} + + build() {} } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-basic.ets b/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-basic.ets index 273a00f46..b13a74436 100644 --- a/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-basic.ets +++ b/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-basic.ets @@ -1,13 +1,39 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component } from "@ohos.arkui.component" import { Observed, ObjectLink } from "@ohos.arkui.stateManagement" @Observed class A {} +@Observed +class B {} + @Component struct MyStateSample { - @ObjectLink objectlinkvar: A; + build() {} +} + +@Component +struct MyStateSample2 { + @ObjectLink objectlinkvar1: A | undefined; + @ObjectLink objectlinkvar2: A | B; + @ObjectLink objectlinkvar3: A | B | null; + build() {} } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-observed.ets b/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-observed.ets index 54dc8f89b..73cc53cc0 100644 --- a/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-observed.ets +++ b/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-observed.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component, Entry, Column, Button, ClickEvent } from "@ohos.arkui.component" import { State, ObjectLink, Observed } from "@ohos.arkui.stateManagement" @@ -10,7 +25,7 @@ class DateClass extends Date { @Observed class NewDate { - public data: DateClass; + public data: DateClass = new DateClass(11); constructor(data: DateClass) { this.data = data; @@ -42,11 +57,11 @@ struct Parent { Child({ label: 'date', data: this.newData.data }) Button(`parent update the new date`) - .onClick(() => { + .onClick((e: ClickEvent) => { this.newData.data = new DateClass('2023-07-07'); }) Button(`ViewB: this.newData = new NewDate(new DateClass('2023-08-20'))`) - .onClick(() => { + .onClick((e: ClickEvent) => { this.newData = new NewDate(new DateClass('2023-08-20')); }) } diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-jsonrename.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-jsonrename.ets new file mode 100644 index 000000000..a9e0cab8c --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-jsonrename.ets @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component } from "@ohos.arkui.component" +import { Observed, Track } from "@ohos.arkui.stateManagement" + +@Retention({policy: "SOURCE"}) +export declare @interface TestDecor {} + +@Observed +class testJsonRename { + var1: number = 1 + @Track var2: number = 2 + @JSONRename('name3') var3: number = 3 + @TestDecor var4: number = 4 + @JSONRename('name5') @Track var5: number = 5 + @JSONRename('name6') @TestDecor var6: number = 6 + @TestDecor @Track var7: number = 7 + @JSONRename('name8') @TestDecor @Track var8: number = 8 +} + +@Component +struct MyStateSample { + build() {} +} diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-jsonstringifyignore.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-jsonstringifyignore.ets new file mode 100644 index 000000000..725a7078e --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-jsonstringifyignore.ets @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component } from "@ohos.arkui.component" +import { Observed, Track } from "@ohos.arkui.stateManagement" + +@Retention({policy: "SOURCE"}) +export declare @interface TestDecor {} + +@Observed +class testJSONStringifyIgnore { + var1: number = 1 + @Track var2: number = 2 + @JSONStringifyIgnore var3: number = 3 + @TestDecor var4: number = 4 + @JSONStringifyIgnore @Track var5: number = 5 + @JSONStringifyIgnore @TestDecor var6: number = 6 + @TestDecor @Track var7: number = 7 + @JSONStringifyIgnore @TestDecor @Track var8: number = 8 +} + +@Component +struct MyStateSample { + build() {} +} diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-only.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-only.ets index 3890ea9ec..b646f91c4 100644 --- a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-only.ets +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-only.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component } from "@ohos.arkui.component" import { Observed } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-class-property.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-class-property.ets index 48a3cd405..10a9ecd57 100644 --- a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-class-property.ets +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-class-property.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component } from "@ohos.arkui.component" import { Observed, Track } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-complex-type.ets index bf59d6d51..a47a08b81 100644 --- a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-complex-type.ets +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-complex-type.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component, Entry } from "@ohos.arkui.component" import { Observed, Track } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-extends.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-extends.ets index 66135926a..a9d102691 100644 --- a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-extends.ets +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-extends.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component } from "@ohos.arkui.component" import { Observed, Track } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-implements.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-implements.ets index 5be8bd871..8342cd8c2 100644 --- a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-implements.ets +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-implements.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component } from "@ohos.arkui.component" import { Observed } from "@ohos.arkui.stateManagement" interface PropInterface { diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track.ets index 3d15c071a..b4023bb2e 100644 --- a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track.ets +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component } from "@ohos.arkui.component" import { Observed, Track } from "@ohos.arkui.stateManagement" @@ -5,6 +20,11 @@ import { Observed, Track } from "@ohos.arkui.stateManagement" class B { propB: number = 1 @Track trackB: number = 2 + @Track newProp: boolean; + + constructor(newProp: boolean) { + this.newProp = newProp; + } } @Component diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/track-only.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/track-only.ets index fea1df86e..08239c86f 100644 --- a/arkui-plugins/test/demo/mock/decorators/observed-track/track-only.ets +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/track-only.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component } from "@ohos.arkui.component" import { Track } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/observedv2-trace/base-observedv2-trace.ets b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/base-observedv2-trace.ets new file mode 100644 index 000000000..89a06a3cb --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/base-observedv2-trace.ets @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class CC { + propB: number = 1 + @Trace traceB: number = 2 +} + +@ObservedV2 +class DD { + propB: number = 1 + @Trace traceE: number = 2 + @Trace tracef: number = 2 + vv: string + constructor(vv1: string) { + this.vv = vv1 + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-serialization.ets b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-serialization.ets new file mode 100644 index 000000000..035c52061 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-serialization.ets @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@Retention({policy: "SOURCE"}) +export declare @interface TestDecor {} + +@ObservedV2 +class testJSONStringifyIgnore { + var1: number = 1 + @Trace var2: number = 2 + @JSONStringifyIgnore var3: number = 3 + @TestDecor var4: number = 4 + @JSONStringifyIgnore @Trace var5: number = 5 + @JSONStringifyIgnore @TestDecor var6: number = 6 + @TestDecor @Trace var7: number = 7 + @JSONStringifyIgnore @TestDecor @Trace var8: number = 8 +} + +@ObservedV2 +class testJsonRename { + var1: number = 1 + @Trace var2: number = 2 + @JSONRename('name3') var3: number = 3 + @TestDecor var4: number = 4 + @JSONRename('name5') @Trace var5: number = 5 + @JSONRename('name6') @TestDecor var6: number = 6 + @TestDecor @Trace var7: number = 7 + @JSONRename('name8') @TestDecor @Trace var8: number = 8 +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-extends.ets b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-extends.ets new file mode 100644 index 000000000..94c66ff97 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-extends.ets @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class A { + propA: number = 1 + @Trace traceA: number = 2 +} + + +class G extends A { + propG: number = 1; +} + +@ObservedV2 +class H extends G { + @Trace propG: number = 1; +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-implements.ets b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-implements.ets new file mode 100644 index 000000000..fdd79e4e5 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-implements.ets @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +interface PropInterface { + propF: number +} + +interface trackInterface { + trackF: number +} + +@ObservedV2 +class F implements PropInterface, trackInterface { + @Trace propF: number = 1 + trackF: number = 2 + @Trace bb: boolean + constructor(bb: boolean) { + this.bb = bb + } +} diff --git a/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-types.ets b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-types.ets new file mode 100644 index 000000000..a5197ef85 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-types.ets @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +class Person{} + +enum Status { + Success = 200, + NotFound = 404, + ServerError = 500 +} + +@ObservedV2 +class mixed1 { + @Trace numA: number = 33; + @Trace stringA: string = 'AA'; + @Trace booleanA: boolean = true; + @Trace arrayA: number[] = [1,2,3]; + @Trace objectA: Object = {}; + @Trace dateA: Date = new Date('2021-08-08'); + @Trace setA: Set = new Set(); + @Trace mapA: Map = new Map(); + @Trace unionA: string | undefined = ""; + @Trace classA: Person = new Person(); + @Trace enumA: Status = Status.NotFound; + + numB: number = 33; + stringB: string = 'AA'; + booleanB: boolean = true; + arrayB: number[] = [1,2,3]; + objectB: Object = {}; + dateB: Date = new Date('2021-08-08'); + setB: Set = new Set(); + mapB: Map = new Map(); + unionB: string | undefined = ""; + classB: Person = new Person(); + enumB: Status = Status.NotFound; +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/observedv2-trace/static-observedv2-trace.ets b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/static-observedv2-trace.ets new file mode 100644 index 000000000..ef145e65f --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/static-observedv2-trace.ets @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class CC { + propB: number = 1 + static propA: number = 1 + @Trace static traceB: number = 2 +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/observedv2-trace/trace-with-constructor.ets b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/trace-with-constructor.ets new file mode 100644 index 000000000..5210335ab --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/trace-with-constructor.ets @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class FF { + propB: number = 1 + @Trace traceE: number = 2 + @Trace vv: string + constructor(vv1: string) { + this.vv = vv1 + } +} diff --git a/arkui-plugins/test/demo/mock/decorators/once/once-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/once/once-basic-type.ets new file mode 100644 index 000000000..dc79a5604 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/once/once-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2 } from "@ohos.arkui.component" +import { Param, Once } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Parent { + @Param @Once onceVar1: string = 'stateVar1'; + @Param @Once onceVar2: number = 50; + @Param @Once onceVar3: boolean = true; + @Param @Once onceVar4: undefined = undefined; + @Param @Once onceVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/once/once-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/once/once-complex-type.ets new file mode 100644 index 000000000..0ce4ad240 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/once/once-complex-type.ets @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2 } from "@ohos.arkui.component" +import { Param, Once } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } +} + +enum StateType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@ComponentV2 +struct Parent { + @Once @Param onceVar1: Per = new Per(6); + @Once @Param onceVar2: Array = new Array(3,6,8); + @Once @Param onceVar3: StateType = StateType.TYPE3; + @Once @Param onceVar4: Set = new Set(new Array('aa', 'bb')); + @Once @Param onceVar5: boolean[] = [true, false]; + @Once @Param onceVar6: Array = new Array(new Per(7), new Per(11)); + @Once @Param onceVar7: Per[] = [new Per(7), new Per(11)]; + @Once @Param onceVar8: (sr: string)=>void = (sr: string)=>{}; + @Once @Param onceVar9: Date = new Date('2025-4-23'); + @Once @Param onceVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Once @Param onceVar11: string | number = 0.0; + @Once @Param onceVar12: Set | Per = new Per(6); + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/once/once-only.ets b/arkui-plugins/test/demo/mock/decorators/once/once-only.ets new file mode 100644 index 000000000..ba4f2892a --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/once/once-only.ets @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2 } from "@ohos.arkui.component" +import { Once } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Child { + @Once onceParamNum: number = 0; + @Once onceVar4: Set = new Set(new Array('aa', 'bb')); + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/once/once-with-require.ets b/arkui-plugins/test/demo/mock/decorators/once/once-with-require.ets new file mode 100644 index 000000000..e346a73da --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/once/once-with-require.ets @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2, Column, Text, Button } from "@ohos.arkui.component" +import { Param, Once, ObservedV2, Trace, Require, Local } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class Info { + @Trace name: string = "info"; +} + +@ComponentV2 +struct Child { + @Param @Once onceParamNum: number = 0; + @Param @Once @Require onceParamInfo: Info; + + build() { + Column() { + Text(`Child onceParamNum: ${this.onceParamNum}`) + Text(`Child onceParamInfo: ${this.onceParamInfo.name}`) + Button("changeOnceParamNum") + .onClick((e) => { + this.onceParamNum++; + }) + } + } +} + +@ComponentV2 +struct Index { + @Local localNum: number = 10; + @Local localInfo: Info = new Info(); + + build() { + Column() { + Text(`Parent localNum: ${this.localNum}`) + Text(`Parent localInfo: ${this.localInfo.name}`) + Child({ + onceParamNum: this.localNum, + onceParamInfo: this.localInfo + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/param/param-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/param/param-basic-type.ets new file mode 100644 index 000000000..a80f8d1d3 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/param/param-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2 } from "@ohos.arkui.component" +import { Param } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Parent { + @Param paramVar1: string = 'stateVar1'; + @Param paramVar2: number = 50; + @Param paramVar3: boolean = true; + @Param paramVar4: undefined = undefined; + @Param paramVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/param/param-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/param/param-complex-type.ets new file mode 100644 index 000000000..52a543a33 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/param/param-complex-type.ets @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2 } from "@ohos.arkui.component" +import { Param } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } +} + +enum StateType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@ComponentV2 +struct Parent { + @Param paramVar1: Per = new Per(6); + @Param paramVar2: Array = new Array(3,6,8); + @Param paramVar3: StateType = StateType.TYPE3; + @Param paramVar4: Set = new Set(new Array('aa', 'bb')); + @Param paramVar5: boolean[] = [true, false]; + @Param paramVar6: Array = new Array(new Per(7), new Per(11)); + @Param paramVar7: Per[] = [new Per(7), new Per(11)]; + @Param paramVar8: (sr: string)=>void = (sr: string)=>{}; + @Param paramVar9: Date = new Date('2025-4-23'); + @Param paramVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Param paramVar11: string | number = 0.0; + @Param paramVar12: Set | Per = new Per(6); + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/param/param-with-require.ets b/arkui-plugins/test/demo/mock/decorators/param/param-with-require.ets new file mode 100644 index 000000000..b6e542ad2 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/param/param-with-require.ets @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2, Column, ForEach, Button, Text } from "@ohos.arkui.component" +import { Param, Require, Local } from "@ohos.arkui.stateManagement" + +class Region { + x: number; + y: number; + constructor(x: number, y: number) { + this.x = x; + this.y = y; + } +} + +class Info { + name: string; + age: number; + region: Region; + constructor(name: string, age: number, x: number, y: number) { + this.name = name; + this.age = age; + this.region = new Region(x, y); + } +} + +@ComponentV2 +struct Index { + @Local infoList: Info[] = [new Info("Alice", 8, 0, 0), new Info("Barry", 10, 1, 20), new Info("Cindy", 18, 24, 40)]; + build() { + Column() { + ForEach(this.infoList, (info: Info) => { + MiddleComponent({ info: info }) + }) + Button("change") + .onClick((e) => { + this.infoList[0] = new Info("Atom", 40, 27, 90); + this.infoList[1].name = "Bob"; + this.infoList[2].region = new Region(7, 9); + }) + } + } +} +@ComponentV2 +struct MiddleComponent { + @Require @Param info: Info; + build() { + Column() { + Text(`name: ${this.info.name}`) + Text(`age: ${this.info.age}`) + SubComponent({ region: this.info.region }) + } + } +} +@ComponentV2 +struct SubComponent { + @Require @Param region: Region; + build() { + Column() { + Text(`region: ${this.region.x}-${this.region.y}`) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-basic-type.ets new file mode 100644 index 000000000..917703d1e --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component } from "@ohos.arkui.component" +import { PropRef } from "@ohos.arkui.stateManagement" + +@Component +struct PropParent { + @PropRef propVar1: string = 'propVar1'; + @PropRef propVar2: number = 50; + @PropRef propVar3: boolean = true; + @PropRef propVar4: undefined = undefined; + @PropRef propVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-complex-type.ets new file mode 100644 index 000000000..419b95b45 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-complex-type.ets @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component } from "@ohos.arkui.component" +import { PropRef } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } +} + +enum PropType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@Component +struct Parent { + @PropRef propVar1: Per = new Per(6); + @PropRef propVar2: Array = new Array(3,6,8); + @PropRef propVar3: PropType = PropType.TYPE3; + @PropRef propVar4: Set = new Set(new Array('aa', 'bb')); + @PropRef propVar5: boolean[] = [true, false]; + @PropRef propVar6: Array = new Array(new Per(7), new Per(11)); + @PropRef propVar7: Per[] = [new Per(7), new Per(11)]; + @PropRef propVar8: (sr: string)=>void = (sr: string)=>{}; + @PropRef propVar9: Date = new Date('2025-4-23'); + @PropRef propVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @PropRef propVar11: string | number = 0.0; + @PropRef propVar12: Set | Per = new Per(6); + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-without-initialization.ets b/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-without-initialization.ets new file mode 100644 index 000000000..66002c9f0 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-without-initialization.ets @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component } from "@ohos.arkui.component" +import { PropRef } from "@ohos.arkui.stateManagement" + +@Component +struct PropParent { + @PropRef propVar1: string; + @PropRef propVar2?: number; + @PropRef propVar3: boolean; + @PropRef propVar4: undefined; + @PropRef propVar5: null; + @PropRef propVar6: Array | null; + @PropRef propVar7: Map | undefined; + + build() { } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/prop-ref/state-to-prop-ref.ets b/arkui-plugins/test/demo/mock/decorators/prop-ref/state-to-prop-ref.ets new file mode 100644 index 000000000..e729a9c7c --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/prop-ref/state-to-prop-ref.ets @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Text, Button, Column, ClickEvent } from "@ohos.arkui.component" +import { PropRef, State } from "@ohos.arkui.stateManagement" + +@Component +struct CountDownComponent { + @PropRef count: number = 0; + costOfOneAttempt: number = 1; + + build() { + Column() { + if (this.count > 0) { + Text('You have'+ this.count + 'Nuggets left') + } else { + Text('Game over!') + } + Button('Try again').onClick((e: ClickEvent) => { + this.count -= this.costOfOneAttempt; + }) + } + } +} + +@Component +struct ParentComponent { + @State countDownStartValue: number = 10; + + build() { + Column() { + Text('Grant' + this.countDownStartValue + 'nuggets to play.') + Button('+1 - Nuggets in New Game').onClick((e: ClickEvent) => { + this.countDownStartValue += 1; + }) + Button('-1 - Nuggets in New Game').onClick((e: ClickEvent) => { + this.countDownStartValue -= 1; + }) + CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provide-and-consume/consume-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/consume-basic-type.ets new file mode 100644 index 000000000..bcc75a5d6 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/consume-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component } from "@ohos.arkui.component" +import { Consume } from "@ohos.arkui.stateManagement" + +@Component +struct PropParent { + @Consume conVar1: string; + @Consume conVar2: number; + @Consume conVar3: boolean; + @Consume conVar4: undefined; + @Consume conVar5: null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provide-and-consume/consume-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/consume-complex-type.ets new file mode 100644 index 000000000..848f1dbf4 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/consume-complex-type.ets @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component } from "@ohos.arkui.component" +import { Consume } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } +} + +enum PropType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@Component +struct Parent { + @Consume conVar1: Per; + @Consume conVar2: Array; + @Consume conVar3: PropType; + @Consume conVar4: Set; + @Consume conVar5: boolean[]; + @Consume conVar6: Array; + @Consume conVar7: Per[]; + @Consume conVar8: (sr: string)=>void; + @Consume conVar9: Date; + @Consume conVar10: Map; + @Consume conVar11: string | number; + @Consume conVar12: Set | Per; + @Consume conVar13: Set | null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-to-consume.ets b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-to-consume.ets new file mode 100644 index 000000000..c0768056a --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-to-consume.ets @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Column, Text } from "@ohos.arkui.component" +import { Consume, Provide } from "@ohos.arkui.stateManagement" + +@Component +struct Child { + @Consume num: number; + @Consume('ss') str: string; + + build() { + Column() { + Text(`Child num: ${this.num}`) + Text(`Child str: ${this.str}`) + } + } +} + +@Component +struct Parent { + @Provide num: number = 10; + @Provide({ alias: 'ss' }) str: string = 'hello'; + + build() { + Column() { + Text(`Parent num: ${this.num}`) + Text(`Parent str: ${this.str}`) + Child() + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/consumer-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/consumer-basic-type.ets new file mode 100644 index 000000000..cedc4f2eb --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/consumer-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2 } from "@ohos.arkui.component" +import { Consumer } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Parent { + @Consumer consumerVar1: string = 'propVar1'; + @Consumer consumerVar2: number = 50; + @Consumer consumerVar3: boolean = true; + @Consumer consumerVar4: undefined = undefined; + @Consumer consumerVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.common.d.ets b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/consumer-complex-type.ets similarity index 38% rename from arkui-plugins/test/local/@ohos.arkui.component.common.d.ets rename to arkui-plugins/test/demo/mock/decorators/provider-and-consumer/consumer-complex-type.ets index 2ff6ea60a..243a52feb 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.common.d.ets +++ b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/consumer-complex-type.ets @@ -13,43 +13,35 @@ * limitations under the License. */ -import { Dimension, Length, ResourceColor } from "@ohos.arkui.component.units"; -import { IlluminatedType } from "@ohos.arkui.component.enums"; -import { memo } from '@ohos.arkui.stateManagement.runtime'; - -@Retention({policy: "SOURCE"}) -export @interface BuilderLambda { - value: string -} - -@Retention({policy: "SOURCE"}) -export declare @interface ComponentBuilder {}; - -@Retention({policy: "SOURCE"}) -export declare @interface BuilderParam {}; - -@Retention({policy: "SOURCE"}) -export declare @interface Builder {}; - -export declare interface LightSource { - positionX: Dimension; - positionY: Dimension; - positionZ: Dimension; - intensity: number; - color?: ResourceColor; +import { ComponentV2 } from "@ohos.arkui.component" +import { Consumer } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } } -export declare interface PointLightStyle { - lightSource?: LightSource; - illuminated?: IlluminatedType; - bloom?: number; +enum StateType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 } -export declare interface CommonMethod { - @memo - width(w: Length): this; - @memo - height(h: Length): this; - @memo - backgroundColor(color: ResourceColor): this; +@ComponentV2 +struct Parent { + @Consumer paramVar1: Per = new Per(6); + @Consumer paramVar2: Array = new Array(3,6,8); + @Consumer paramVar3: StateType = StateType.TYPE3; + @Consumer paramVar4: Set = new Set(new Array('aa', 'bb')); + @Consumer paramVar5: boolean[] = [true, false]; + @Consumer paramVar6: Array = new Array(new Per(7), new Per(11)); + @Consumer paramVar7: Per[] = [new Per(7), new Per(11)]; + @Consumer paramVar9: Date = new Date('2025-4-23'); + @Consumer paramVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Consumer paramVar11: string | number = 0.0; + @Consumer paramVar12: Set | Per = new Per(6); + + build() {} } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-basic-type.ets new file mode 100644 index 000000000..312eed337 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2 } from "@ohos.arkui.component" +import { Provider } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Parent { + @Provider providerVar1: string = 'propVar1'; + @Provider providerVar2: number = 50; + @Provider providerVar3: boolean = true; + @Provider providerVar4: undefined = undefined; + @Provider providerVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-complex-type.ets new file mode 100644 index 000000000..1a6fc86c7 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-complex-type.ets @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2 } from "@ohos.arkui.component" +import { Provider } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } +} + +enum StateType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@ComponentV2 +struct Parent { + @Provider paramVar1: Per = new Per(6); + @Provider paramVar2: Array = new Array(3,6,8); + @Provider paramVar3: StateType = StateType.TYPE3; + @Provider paramVar4: Set = new Set(new Array('aa', 'bb')); + @Provider paramVar5: boolean[] = [true, false]; + @Provider paramVar6: Array = new Array(new Per(7), new Per(11)); + @Provider paramVar7: Per[] = [new Per(7), new Per(11)]; + @Provider paramVar9: Date = new Date('2025-4-23'); + @Provider paramVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Provider paramVar11: string | number = 0.0; + @Provider paramVar12: Set | Per = new Per(6); + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-to-consumer.ets b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-to-consumer.ets new file mode 100644 index 000000000..6bb0cd890 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-to-consumer.ets @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentV2, DragEvent, Button, Column, Text, ForEach, Divider } from "@ohos.arkui.component" +import { Provider, Consumer, Local, ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class User { + @Trace name: string; + @Trace age: number; + + constructor(name: string, age: number) { + this.name = name; + this.age = age; + } +} + +const data: User[] = [new User('Json', 10), new User('Eric', 15)]; + +@ComponentV2 +struct Parent { + @Provider('data') users: User[] = data; + + + build() { + Column() { + Child() + Button('add new user') + .onClick((e) => { + this.users.push(new User('Molly', 18)); + }) + Button('age++') + .onClick((e) => { + this.users[0].age++; + }) + Button('change name') + .onClick((e) => { + this.users[0].name = 'Shelly'; + }) + } + } +} + + +@ComponentV2 +struct Child { + @Consumer('data') users: User[] = []; + + build() { + Column() { + ForEach(this.users, (item: User) => { + Column() { + Text(`name: ${item.name}`).fontSize(30) + Text(`age: ${item.age}`).fontSize(30) + Divider() + } + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/require/basic-require.ets b/arkui-plugins/test/demo/mock/decorators/require/basic-require.ets new file mode 100644 index 000000000..86703f8b5 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/require/basic-require.ets @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, ComponentV2, BuilderParam } from "@ohos.arkui.component" +import { State, Require, Prop, Provide, Param } from "@ohos.arkui.stateManagement" + +@Component +struct MyStateSample { + hello: string = 'hello'; + @State state1: boolean = false; + @Require select100: string; + @Require @State select0: number; + @Require @State select3: number | null; + @Require @State select4: undefined; + @Require @Prop select1: string; + @Require @Provide({ alias: '15' }) select2: string[]; + @Require @Provide({ alias: 't' }) select6: string[] | undefined | string; + @Require @BuilderParam builder: () => void; + + build() {} +} + +@ComponentV2 +struct V2222 { + @Require @Param select1: string; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/resource/resource-in-build.ets b/arkui-plugins/test/demo/mock/decorators/resource/resource-in-build.ets index 49331e647..f77f9a04b 100644 --- a/arkui-plugins/test/demo/mock/decorators/resource/resource-in-build.ets +++ b/arkui-plugins/test/demo/mock/decorators/resource/resource-in-build.ets @@ -13,36 +13,46 @@ * limitations under the License. */ -import { Component, $r, $rawfile, Column, Text, Image, TextInput, Select, SelectOption, Margin, ImageAnimator } from "@ohos.arkui.component" +import { Component, $r, $rawfile, Column, Text, Image, TextInput, Select, SelectOption, Margin, ImageAnimator, Resource } from "@ohos.arkui.component" @Component struct ResourceComponent { str1: string = 'app.media.ri' str2: string = 'app.photo2.png' + numbers: string[] = ['0','1','3','5','8'] + aboutToAppear() { + let arr: Array = new Array() + for (let i = 0; i < 5; i++) { + arr.push($r('app.string.app_name')) + } + for (let item of this.numbers) { + arr.push($r('app.string.app_name')) + } + } build() { Column() { Text($r('app.string.app_name')) - Image($rawfile('app.photo.png')) - TextInput({ text: $r('app.string.input_content') }) + Image($rawfile('app.mock.txt')) + TextInput({ text: $r('app.string.module_desc') }) Text($r(this.str1)) Text($r(this.str2)) Select(new Array( - { value: 'aaa', icon: $r("app.media.selection") }, - { value: 'bbb', icon: $r("app.media.selection") }, - { value: 'ccc', icon: $r("app.media.selection") }, - { value: 'ddd', icon: $r("app.media.selection") } + { value: 'aaa', icon: $r("app.media.background") }, + { value: 'bbb', icon: $r("app.media.background") }, + { value: 'ccc', icon: $r("app.media.background") }, + { value: 'ddd', icon: $r("app.media.background") } )) Image($r('app.media.app_icon')) .margin({ - top: $r('app.float.elements_margin_horizontal_m'), - bottom: $r('app.float.elements_margin_horizontal_l') + top: $r('app.float.page_text_font_size'), + bottom: $r('app.float.page_text_font_size') } as Margin) ImageAnimator().images([ { - src: $r('app.media.aaa') + src: $r('app.media.app_icon') }, { - src: $r('app.media.bbb') + src: $r('app.media.layered_image') }, ]) } diff --git a/arkui-plugins/test/demo/mock/decorators/resource/resource-in-property.ets b/arkui-plugins/test/demo/mock/decorators/resource/resource-in-property.ets index 591311c7d..fca6a78a7 100644 --- a/arkui-plugins/test/demo/mock/decorators/resource/resource-in-property.ets +++ b/arkui-plugins/test/demo/mock/decorators/resource/resource-in-property.ets @@ -20,7 +20,7 @@ let i: Resource = $r('app.string.app_name'); @Component struct ResourceComponent { private str: Resource = $r('app.string.app_name'); - private icon: Resource = $rawfile('app.photo.png'); + private icon: Resource = $rawfile('app.mock.txt'); build() { Column() { Text(this.str) diff --git a/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.ets b/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.ets index 755a647b3..547ed62ea 100644 --- a/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.ets +++ b/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.ets @@ -1,5 +1,20 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component, Reusable} from "@ohos.arkui.component" -import { State, Link } from "@ohos.arkui.stateManagement" +import { State, Prop } from "@ohos.arkui.stateManagement" @Component struct MyStateSample { @@ -11,7 +26,7 @@ struct MyStateSample { @Component @Reusable struct Child { - @Link num: number = 1 + @Prop num: number = 1 @State num1: number = 2 build() { diff --git a/arkui-plugins/test/demo/mock/decorators/reusable/reusable-complex.ets b/arkui-plugins/test/demo/mock/decorators/reusable/reusable-complex.ets index d66fe5451..10bf9f803 100644 --- a/arkui-plugins/test/demo/mock/decorators/reusable/reusable-complex.ets +++ b/arkui-plugins/test/demo/mock/decorators/reusable/reusable-complex.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component, Entry, Reusable, Column, Text, Button, ClickEvent, FontWeight} from "@ohos.arkui.component" import { State } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-appstorage.ets b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-appstorage.ets index 298dc8955..a99565933 100644 --- a/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-appstorage.ets +++ b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-appstorage.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component, Entry, Column, Text, ClickEvent } from "@ohos.arkui.component" import { StorageLink, AppStorage } from "@ohos.arkui.stateManagement" @@ -12,7 +27,6 @@ class Data { AppStorage.setOrCreate('PropA', 47); AppStorage.setOrCreate('PropB', new Data(50)); - @Entry @Component struct Index { diff --git a/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-complex-type.ets index 9aed6364d..08b911396 100644 --- a/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-complex-type.ets +++ b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-complex-type.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component, Entry } from "@ohos.arkui.component" import { StorageLink } from "@ohos.arkui.stateManagement" @@ -20,7 +35,6 @@ struct MyStateSample { @StorageLink('Prop3') dateA: Date = new Date('2021-08-08'); @StorageLink('Prop4') setA: Set = new Set(); @StorageLink('Prop5') mapA: Map = new Map(); - @StorageLink('Prop6') unionA: string | undefined = ""; @StorageLink('Prop7') classA: Person = new Person("John"); @StorageLink('Prop8') enumA: Status = Status.NotFound; diff --git a/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-primitive-type.ets b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-primitive-type.ets index e5afb4d19..a4219a463 100644 --- a/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-primitive-type.ets +++ b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-primitive-type.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component, Entry } from "@ohos.arkui.component" import { StorageLink } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/storageprop-ref/storageprop-ref-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/storageprop-ref/storageprop-ref-complex-type.ets new file mode 100644 index 000000000..1951e8277 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/storageprop-ref/storageprop-ref-complex-type.ets @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { StoragePropRef } from "@ohos.arkui.stateManagement" + +class Person{ + name: string = '' + constructor(name: string){} +} + +enum Status { + Success = 200, + NotFound = 404, + ServerError = 500 +} + +@Component +struct MyStateSample { + @StoragePropRef('Prop1') arrayB: number[] = [1,2,3]; + @StoragePropRef('Prop2') objectB: Object = {}; + @StoragePropRef('Prop3') dateB: Date = new Date('2021-09-09'); + @StoragePropRef('Prop4') setB: Set = new Set(); + @StoragePropRef('Prop5') mapB: Map = new Map(); + @StoragePropRef('Prop7') classB: Person = new Person("Kevin"); + @StoragePropRef('Prop8') enumB: Status = Status.NotFound; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/storageprop-ref/storageprop-ref-primitive-type.ets b/arkui-plugins/test/demo/mock/decorators/storageprop-ref/storageprop-ref-primitive-type.ets new file mode 100644 index 000000000..e84494b66 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/storageprop-ref/storageprop-ref-primitive-type.ets @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { StoragePropRef } from "@ohos.arkui.stateManagement" + +@Component +struct MyStateSample { + @StoragePropRef('Prop1') numB: number = 43; + @StoragePropRef('Prop2') stringB: string = 'BB'; + @StoragePropRef('Prop3') booleanB: boolean = false; + @StoragePropRef('Prop4') undefinedB: undefined = undefined; + @StoragePropRef('Prop5') nullB: null = null; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-appstorage.ets b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-appstorage.ets index bff353a17..dd25eb7fb 100644 --- a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-appstorage.ets +++ b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-appstorage.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component, Entry, Column, Text, ClickEvent } from "@ohos.arkui.component" import { StorageProp, AppStorage } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-complex-type.ets index 5a770cf6e..50c2ea128 100644 --- a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-complex-type.ets +++ b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-complex-type.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component, Entry } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins import { StorageProp } from "@ohos.arkui.stateManagement" @@ -20,7 +35,6 @@ struct MyStateSample { @StorageProp('Prop3') dateB: Date = new Date('2021-09-09'); @StorageProp('Prop4') setB: Set = new Set(); @StorageProp('Prop5') mapB: Map = new Map(); - @StorageProp('Prop6') unionB: string | undefined = ""; @StorageProp('Prop7') classB: Person = new Person("Kevin"); @StorageProp('Prop8') enumB: Status = Status.NotFound; diff --git a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-primitive-type.ets b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-primitive-type.ets index d82a28552..95d4ed58d 100644 --- a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-primitive-type.ets +++ b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-primitive-type.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { Component, Entry } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins import { StorageProp } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets b/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets index 6a85ab00a..4c36212e7 100644 --- a/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets +++ b/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets @@ -1,4 +1,19 @@ -import { Component, Entry } from "@ohos.arkui.component" +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Entry, Column } from "@ohos.arkui.component" import { State, Prop, StorageLink, StorageProp, Link, Watch, ObjectLink, Observed, Track, Provide, Consume } from "@ohos.arkui.stateManagement" @Observed @@ -12,30 +27,32 @@ class A { struct MyStateSample { @State @Watch('stateOnChange') statevar: string = 'Hello World'; @Prop @Watch('propOnChange') propvar: string = 'Hello World'; - // @Link @Watch('linkOnChange') linkvar: string; + @Link @Watch('linkOnChange') linkvar: string; @StorageLink('prop1') @Watch('storageLinkOnChange') storagelinkvar: string = 'Hello World'; @StorageProp('prop2') @Watch('storagePropOnChange') storagepropvar: string = 'Hello World'; - // @ObjectLink @Watch('objectLinkOnChange') objectlinkvar: A; + @ObjectLink @Watch('objectLinkOnChange') objectlinkvar: A; @Provide @Watch('ProvideOnChange') providevar: string = 'Hello World'; stateOnChange(propName: string) {} propOnChange(propName: string) {} - // linkOnChange(propName: string) {} + linkOnChange(propName: string) {} storageLinkOnChange(propName: string) {} storagePropOnChange(propName: string) {} - // objectLinkOnChange(propName: string) {} + objectLinkOnChange(propName: string) {} ProvideOnChange(propName: string) {} build() { - Child() + Column() { + Child() + } } } @Component struct Child { - // @Consume @Watch('ConsumeOnChange') providevar: string; + @Consume @Watch('ConsumeOnChange') providevar: string; - // ConsumeOnChange(propName: string) {} + ConsumeOnChange(propName: string) {} build() {} } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/double-dollar/double-dollar-griditem.ets b/arkui-plugins/test/demo/mock/double-dollar/double-dollar-griditem.ets new file mode 100644 index 000000000..eae3300b8 --- /dev/null +++ b/arkui-plugins/test/demo/mock/double-dollar/double-dollar-griditem.ets @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Text, Entry, Column, Component, $$, Grid, GridItem } from "@ohos.arkui.component" +import { State } from "@ohos.arkui.stateManagement" + +let c: boolean = false + +@Entry +@Component +struct MyStateSample { + @State boo: boolean = true + build() { + Column() { + Grid() { + GridItem() { + Text('nihao') + }.selected($$(this.boo)) + GridItem() { + Text('nihao') + }.selected($$(c)) + } + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.enums.d.ets b/arkui-plugins/test/demo/mock/double-dollar/double-dollar-toggle.ets similarity index 50% rename from arkui-plugins/test/local/@ohos.arkui.component.enums.d.ets rename to arkui-plugins/test/demo/mock/double-dollar/double-dollar-toggle.ets index e72c21ace..20ff29b73 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.enums.d.ets +++ b/arkui-plugins/test/demo/mock/double-dollar/double-dollar-toggle.ets @@ -13,41 +13,25 @@ * limitations under the License. */ -export declare enum FlexAlign { - Start = 0, - Center = 1, - End = 2, - SpaceBetween = 3, - SpaceAround = 4, - SpaceEvenly = 5 -} +import { Text, Column, Component, $$, Toggle, ToggleType } from "@ohos.arkui.component" +import { State } from "@ohos.arkui.stateManagement" -export declare enum HorizontalAlign { - Start = 0, - Center = 1, - End = 2 -} +let c: boolean[] = [true, false, true]; -export declare enum IlluminatedType { - NONE = 0, - BORDER = 1, - CONTENT = 2, - BORDER_CONTENT = 3, - BLOOM_BORDER = 4, - BLOOM_BORDER_CONTENT = 5 +class BooleanClass { + isOn: boolean = true; } -export declare enum Color { - White = 0, - Black = 1, - Blue = 2, - Brown = 3, - Gray = 4, - Green = 5, - Grey = 6, - Orange = 7, - Pink = 8, - Red = 9, - Yellow = 10, - Transparent = 11 +@Component +struct MyStateSample { + @State boo: boolean[] = [true, false, true]; + @State booClass: BooleanClass = new BooleanClass(); + + build() { + Column() { + Toggle({ type: ToggleType.Checkbox, isOn: $$(this.boo[0]) }) + Toggle({ type: ToggleType.Checkbox, isOn: $$(this.booClass.isOn) }) + Toggle({ type: ToggleType.Checkbox, isOn: $$(c[1]) }) + } + } } \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.external.resource.d.ets b/arkui-plugins/test/demo/mock/entry/entry-only.ets similarity index 83% rename from arkui-plugins/test/local/@ohos.arkui.external.resource.d.ets rename to arkui-plugins/test/demo/mock/entry/entry-only.ets index 9cc3b09e0..2d54e1bf2 100644 --- a/arkui-plugins/test/local/@ohos.arkui.external.resource.d.ets +++ b/arkui-plugins/test/demo/mock/entry/entry-only.ets @@ -12,5 +12,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import { Component, Entry } from "@ohos.arkui.component" -export type Resource = boolean; \ No newline at end of file +@Entry() +@Component +struct MyStateSample { + build() {} + +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/entry/localstorage/storage-use-shared-storage-false.ets b/arkui-plugins/test/demo/mock/entry/localstorage/storage-use-shared-storage-false.ets new file mode 100644 index 000000000..2a98d667f --- /dev/null +++ b/arkui-plugins/test/demo/mock/entry/localstorage/storage-use-shared-storage-false.ets @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Entry } from "@ohos.arkui.component" +import { LocalStorage } from "@ohos.arkui.stateManagement" + +const myStorage: ()=>LocalStorage = ()=>new LocalStorage() + +@Entry({storage: 'myStorage', useSharedStorage: false}) +@Component +struct MyStateSample { + build() {} +} diff --git a/arkui-plugins/test/demo/mock/entry/localstorage/storage-use-shared-storage-true.ets b/arkui-plugins/test/demo/mock/entry/localstorage/storage-use-shared-storage-true.ets new file mode 100644 index 000000000..e3c657bd3 --- /dev/null +++ b/arkui-plugins/test/demo/mock/entry/localstorage/storage-use-shared-storage-true.ets @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Entry } from "@ohos.arkui.component" +import { LocalStorage } from "@ohos.arkui.stateManagement" + +const myStorage: ()=>LocalStorage = ()=>new LocalStorage() + +@Entry({storage: 'myStorage', useSharedStorage: true}) +@Component +struct MyStateSample { + build() {} +} diff --git a/arkui-plugins/test/demo/mock/entry/localstorage/storage.ets b/arkui-plugins/test/demo/mock/entry/localstorage/storage.ets new file mode 100644 index 000000000..b1aefaefb --- /dev/null +++ b/arkui-plugins/test/demo/mock/entry/localstorage/storage.ets @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Entry } from "@ohos.arkui.component" +import { LocalStorage } from "@ohos.arkui.stateManagement" + +const myStorage: ()=>LocalStorage = ()=>new LocalStorage() + +@Entry({storage: 'myStorage'}) +@Component +struct MyStateSample { + build() {} + +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/entry/localstorage/use-shared-storage-false.ets b/arkui-plugins/test/demo/mock/entry/localstorage/use-shared-storage-false.ets new file mode 100644 index 000000000..7be065952 --- /dev/null +++ b/arkui-plugins/test/demo/mock/entry/localstorage/use-shared-storage-false.ets @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Entry } from "@ohos.arkui.component" +import { LocalStorage } from "@ohos.arkui.stateManagement" + +const myStorage: ()=>LocalStorage = ()=>new LocalStorage() + +@Entry({useSharedStorage: false}) +@Component +struct MyStateSample { + build() {} +} diff --git a/arkui-plugins/test/demo/mock/entry/localstorage/use-shared-storage-true.ets b/arkui-plugins/test/demo/mock/entry/localstorage/use-shared-storage-true.ets new file mode 100644 index 000000000..f327cf40c --- /dev/null +++ b/arkui-plugins/test/demo/mock/entry/localstorage/use-shared-storage-true.ets @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Entry } from "@ohos.arkui.component" +import { LocalStorage } from "@ohos.arkui.stateManagement" + +const myStorage: ()=>LocalStorage = ()=>new LocalStorage() + +@Entry({useSharedStorage: true}) +@Component +struct MyStateSample { + build() {} +} diff --git a/arkui-plugins/test/demo/mock/entry/route-name/route-name-storage-shared.ets b/arkui-plugins/test/demo/mock/entry/route-name/route-name-storage-shared.ets new file mode 100644 index 000000000..c2d457f4c --- /dev/null +++ b/arkui-plugins/test/demo/mock/entry/route-name/route-name-storage-shared.ets @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Entry } from "@ohos.arkui.component" +import { LocalStorage } from "@ohos.arkui.stateManagement" + +const myStorage: ()=>LocalStorage = ()=>new LocalStorage() +@Entry({routeName: 'MyPage', storage: 'myStorage', useSharedStorage: true}) +@Component +struct MyStateSample { + build() {} + +} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.stateManagement.d.ets b/arkui-plugins/test/demo/mock/entry/route-name/route-name.ets similarity index 79% rename from arkui-plugins/test/local/@ohos.arkui.stateManagement.d.ets rename to arkui-plugins/test/demo/mock/entry/route-name/route-name.ets index 9cb01ec59..3a9fedb06 100644 --- a/arkui-plugins/test/local/@ohos.arkui.stateManagement.d.ets +++ b/arkui-plugins/test/demo/mock/entry/route-name/route-name.ets @@ -12,7 +12,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import { Component, Entry } from "@ohos.arkui.component" -export * from "@ohos.arkui.stateManagement.common"; -export * from "@ohos.arkui.stateManagement.runtime"; -export * from "@ohos.arkui.stateManagement.storage"; +@Entry({routeName: 'MyPage'}) +@Component +struct MyStateSample { + build() {} + +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/imports/import-struct.ets b/arkui-plugins/test/demo/mock/imports/import-struct.ets new file mode 100644 index 000000000..d7cf79b5e --- /dev/null +++ b/arkui-plugins/test/demo/mock/imports/import-struct.ets @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Text } from "@ohos.arkui.component" +import { SimpleStruct } from "./utils/simple-struct" + +@Component +struct ImportStruct { + build() { + SimpleStruct(); + SimpleStruct({ message: "str1" }); + SimpleStruct() { + Text("a"); + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/imports/kit-import.ets b/arkui-plugins/test/demo/mock/imports/kit-import.ets new file mode 100644 index 000000000..5cd7771b2 --- /dev/null +++ b/arkui-plugins/test/demo/mock/imports/kit-import.ets @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Prop, Column, Entry } from "@kit.ArkUI"; +import { Text, Component, ClickEvent } from "@ohos.arkui.component"; +import { State } from "@ohos.arkui.stateManagement"; +import { Button } from "arkui.component.button"; +import hilog from "@ohos.hilog"; + +@Entry +@Component +struct A { + @State a: string = "str"; + @Prop b: string; + + build() { + Column() { + Button("button").onClick((e: ClickEvent) => { }) + Text("text").fontSize(20) + } + } +} diff --git a/arkui-plugins/test/demo/mock/imports/utils/simple-struct.ets b/arkui-plugins/test/demo/mock/imports/utils/simple-struct.ets new file mode 100644 index 000000000..053de876e --- /dev/null +++ b/arkui-plugins/test/demo/mock/imports/utils/simple-struct.ets @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component } from "@ohos.arkui.component" + +@Component +export struct SimpleStruct { + message: string = "str"; + + build() {} +} diff --git a/arkui-plugins/test/demo/mock/memo/functions/argument-call.ets b/arkui-plugins/test/demo/mock/memo/functions/argument-call.ets index 54263a552..1db0074a7 100644 --- a/arkui-plugins/test/demo/mock/memo/functions/argument-call.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/argument-call.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; @memo function memo_arg_call( arg1: number, diff --git a/arkui-plugins/test/local/@ohos.arkui.stateManagement.common.d.ets b/arkui-plugins/test/demo/mock/memo/functions/complex-memo-intrinsic.ets similarity index 33% rename from arkui-plugins/test/local/@ohos.arkui.stateManagement.common.d.ets rename to arkui-plugins/test/demo/mock/memo/functions/complex-memo-intrinsic.ets index 274947957..1ab1a5eba 100644 --- a/arkui-plugins/test/local/@ohos.arkui.stateManagement.common.d.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/complex-memo-intrinsic.ets @@ -13,49 +13,62 @@ * limitations under the License. */ -@Retention({policy: "SOURCE"}) -export declare @interface State {}; +import { memo } from "arkui.stateManagement.runtime"; -@Retention({policy: "SOURCE"}) -export declare @interface Prop {}; +@Retention({policy:"SOURCE"}) @interface memo_intrinsic {} -@Retention({policy: "SOURCE"}) -export declare @interface Link {}; +interface IA { + ccc: boolean; +} -@Retention({policy: "SOURCE"}) -export declare @interface Observed {}; +class A implements IA { + bbb: Map = new Map(); + ddd: Map = new Map(); + ccc: boolean = false; -@Retention({policy: "SOURCE"}) -export declare @interface Track {}; + aaa(value: number): void {} +} -@Retention({policy: "SOURCE"}) -export declare @interface ObjectLink {}; +export type SimpleArray = Array | ReadonlyArray | Readonly>; -@Retention({policy: "SOURCE"}) -export declare @interface StorageProp { value: string }; +@memo_intrinsic +export declare function factory(compute: () => Value): Value; -@Retention({policy: "SOURCE"}) -export declare @interface StorageLink { value: string }; +export function cb(callback?: () => void) { + if (callback) return; +} -@Retention({policy: "SOURCE"}) -export declare @interface LocalStorageProp { value: string }; +@memo_intrinsic +export function impl( + @memo + style: ((attributes: IA) => void) | undefined, + arr: SimpleArray, + err: string = 'error message' +): void { + const s = factory(() => { + return new A(); + }); + s.aaa(arr.length); + style?.(s); + if (!s.bbb.get('some_key')) { + throw new Error(err); + } + if (s.ccc) { + cb(() => + s.ddd.forEach((s: number, t: string) => { + console.log(err); + return; + }) + ); + } else { + return; + } +} -@Retention({policy: "SOURCE"}) -export declare @interface LocalStorageLink { value: string }; - -@Retention({policy: "SOURCE"}) -export declare @interface Provide { value: string }; - -@Retention({policy: "SOURCE"}) -export declare @interface Consume { value: string }; - -@Retention({policy: "SOURCE"}) -export declare @interface Watch { value: string }; - -@Retention({policy: "SOURCE"}) -export declare @interface Require {}; - -export declare class UIUtils { - static getTarget(source: T): T; - static makeObserved(source: T): T; +class Use { + @memo test() { + const style = @memo (attributes: IA) => {}; + const arr = [1, 2, 3, 4]; + impl(style, arr); + } } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/memo/functions/declare-and-call.ets b/arkui-plugins/test/demo/mock/memo/functions/declare-and-call.ets index ac8324d22..9b54856e5 100644 --- a/arkui-plugins/test/demo/mock/memo/functions/declare-and-call.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/declare-and-call.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; @memo declare function funcA(): void; @@ -29,7 +29,26 @@ function funcB(): void { funcA(); } +interface MemoBuilder { + @memo builder: () => void +} + +@memo +function funcWithMemoBuilder(@memo memo_arg: MemoBuilder): void {} + +function funcWithArg(arg: () => void): void {} + +function func(): void {} + @memo () => { funcA(); + funcWithMemoBuilder({ + builder: () => { + funcWithArg(() => { + func(); + return; + }); + } + }); } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/memo/functions/inner-functions.ets b/arkui-plugins/test/demo/mock/memo/functions/inner-functions.ets index 313164b28..40759ede4 100644 --- a/arkui-plugins/test/demo/mock/memo/functions/inner-functions.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/inner-functions.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; @memo function foo() { } diff --git a/arkui-plugins/test/demo/mock/memo/functions/internal-memo-arg.ets b/arkui-plugins/test/demo/mock/memo/functions/internal-memo-arg.ets new file mode 100644 index 000000000..38f146018 --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/functions/internal-memo-arg.ets @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { memo, __memo_context_type, __memo_id_type } from "arkui.stateManagement.runtime"; +import { IncrementalNode } from "@koalaui.runtime.tree.IncrementalNode"; +import { ControlledScope, StateManager } from "@koalaui.runtime.states.State"; + +@Retention({policy:"SOURCE"}) @interface memo_intrinsic {} +@Retention({policy:"SOURCE"}) @interface memo_entry {} + +export declare function __context(): __memo_context_type +export declare function __id(): __memo_id_type + +@memo_entry() export function memoEntry(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: (()=> R)): R { + return entry(); +} + +@memo_entry() export function memoEntry1(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: ((arg: T)=> R), arg: T): R { + return entry(arg); +} + +@memo_entry() export function memoEntry2(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: ((arg1: T1, arg2: T2)=> R), arg1: T1, arg2: T2): R { + return entry(arg1, arg2); +} + +export class MemoCallbackContext { + private readonly context: __memo_context_type; + + private readonly id: __memo_id_type; + + private constructor(context: __memo_context_type, id: __memo_id_type) { + this.context = context; + this.id = id; + } + + @memo() public static Make(): MemoCallbackContext { + return new MemoCallbackContext(__context(), __id()); + } +} + +@memo_intrinsic() export function contextLocalValue(name: string): Value { + return __context().valueBy(name); +} + +@memo_intrinsic() export function contextLocalScope(name: string, value: Value, @memo() content: (()=> void)) { + const scope = __context().scope(__id(), 1); + scope.param(0, value, undefined, name, true); + if (scope.unchanged) { + scope.cached; + } else { + content(); + scope.recache(); + } +} + +@memo_intrinsic() export function NodeAttach(create: (()=> Node), @memo() update: ((node: Node)=> void), reuseKey?: string): void { + const scope = __context().scope(__id(), 0, create, undefined, undefined, undefined, reuseKey); + if (scope.unchanged) { + scope.cached; + } else { + try { + if (!reuseKey) { + update((__context().node as Node)) + } else { + memoEntry(__context(), 0, (() => { + update((__context().node as Node)); + })) + } + } finally { + scope.recache(); + } + } +} + +@memo_intrinsic() export function rememberControlledScope(invalidate: (()=> void)): ControlledScope { + return __context().controlledScope(__id(), invalidate); +} + +@memo() export function Repeat(count: int, @memo() action: ((index: int)=> void)) { + for (let i = 0;((i) < (count));(i++)) { + memoEntry1(__context(), i, action, i); + } +} + +export class CustomComponent { + @memo() public static _instantiateImpl(): void { + const context: StateManager = (__context() as StateManager); + } +} diff --git a/arkui-plugins/test/demo/mock/memo/functions/non-void-return-type.ets b/arkui-plugins/test/demo/mock/memo/functions/non-void-return-type.ets index cc0f993ec..fca54fcc7 100644 --- a/arkui-plugins/test/demo/mock/memo/functions/non-void-return-type.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/non-void-return-type.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; @memo function funcNum(): number { diff --git a/arkui-plugins/test/demo/mock/memo/functions/type-reference.ets b/arkui-plugins/test/demo/mock/memo/functions/type-reference.ets index be715df95..fb4e922ac 100644 --- a/arkui-plugins/test/demo/mock/memo/functions/type-reference.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/type-reference.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; @memo type ItemBuilder = (item: Item) => void; diff --git a/arkui-plugins/test/demo/mock/memo/functions/void-return-type.ets b/arkui-plugins/test/demo/mock/memo/functions/void-return-type.ets index 840c84aaf..5605e2b68 100644 --- a/arkui-plugins/test/demo/mock/memo/functions/void-return-type.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/void-return-type.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; @memo function func(): void { diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/argument-call.ets b/arkui-plugins/test/demo/mock/memo/lambdas/argument-call.ets index def28807a..455fb6843 100644 --- a/arkui-plugins/test/demo/mock/memo/lambdas/argument-call.ets +++ b/arkui-plugins/test/demo/mock/memo/lambdas/argument-call.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; (arg: (()=>void)) => {}(() => {}); diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/function-with-receiver.ets b/arkui-plugins/test/demo/mock/memo/lambdas/function-with-receiver.ets index 36bc7d82f..4ade068c9 100644 --- a/arkui-plugins/test/demo/mock/memo/lambdas/function-with-receiver.ets +++ b/arkui-plugins/test/demo/mock/memo/lambdas/function-with-receiver.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; class B { @memo internal_call(): B { diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/trailing-lambdas.ets b/arkui-plugins/test/demo/mock/memo/lambdas/trailing-lambdas.ets index 08d252f58..1bfdb18b9 100644 --- a/arkui-plugins/test/demo/mock/memo/lambdas/trailing-lambdas.ets +++ b/arkui-plugins/test/demo/mock/memo/lambdas/trailing-lambdas.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; class A { @memo foo(p?: ()=>void): void {} diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/void-lambda.ets b/arkui-plugins/test/demo/mock/memo/lambdas/void-lambda.ets index fb757d8b6..2f5afae38 100644 --- a/arkui-plugins/test/demo/mock/memo/lambdas/void-lambda.ets +++ b/arkui-plugins/test/demo/mock/memo/lambdas/void-lambda.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; @memo (): void => { diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.ets b/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.ets index b54c9c6c2..556aba2b5 100644 --- a/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.ets +++ b/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.ets @@ -13,22 +13,29 @@ * limitations under the License. */ - import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; class Person { constructor() {} } +@memo function fullName(this: Person, @memo arg?: () => void): void { return; } class A {} +@memo type F1 = (this: A, @memo arg?: () => void) => void; + +@memo type F2 = (a: A, @memo arg?: () => void) => void; +@memo function foo(this: A, @memo arg?: () => void): void {} + +@memo function goo(a: A, @memo arg?: () => void): void {} @memo @@ -37,11 +44,7 @@ function goo(a: A, @memo arg?: () => void): void {} x.fullName(() => {}); let f1: F1 = foo; - f1 = goo; - let f2: F2 = goo; - f2 = foo; - f1 = f2; let a = new A(); a.f1(() => {}); diff --git a/arkui-plugins/test/demo/mock/memo/methods/argument-call.ets b/arkui-plugins/test/demo/mock/memo/methods/argument-call.ets index 5f799140f..25903f043 100644 --- a/arkui-plugins/test/demo/mock/memo/methods/argument-call.ets +++ b/arkui-plugins/test/demo/mock/memo/methods/argument-call.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; class Test { @memo lambda_arg(@memo arg: () => void) { diff --git a/arkui-plugins/test/demo/mock/memo/methods/callable.ets b/arkui-plugins/test/demo/mock/memo/methods/callable.ets index 2f4e2a401..bebbb7501 100644 --- a/arkui-plugins/test/demo/mock/memo/methods/callable.ets +++ b/arkui-plugins/test/demo/mock/memo/methods/callable.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; class A { @memo diff --git a/arkui-plugins/test/demo/mock/memo/methods/declare-and-call.ets b/arkui-plugins/test/demo/mock/memo/methods/declare-and-call.ets index e28c85361..121e1568d 100644 --- a/arkui-plugins/test/demo/mock/memo/methods/declare-and-call.ets +++ b/arkui-plugins/test/demo/mock/memo/methods/declare-and-call.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; declare abstract class A { @memo diff --git a/arkui-plugins/test/demo/mock/memo/methods/internal-calls.ets b/arkui-plugins/test/demo/mock/memo/methods/internal-calls.ets index 7ef4cdb0e..5dcd4c292 100644 --- a/arkui-plugins/test/demo/mock/memo/methods/internal-calls.ets +++ b/arkui-plugins/test/demo/mock/memo/methods/internal-calls.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo, __memo_context_type, __memo_id_type } from "arkui.stateManagement.runtime"; export declare function __context(): __memo_context_type export declare function __id(): __memo_id_type diff --git a/arkui-plugins/test/demo/mock/memo/methods/non-void-method.ets b/arkui-plugins/test/demo/mock/memo/methods/non-void-method.ets index 06df30297..4e8053d50 100644 --- a/arkui-plugins/test/demo/mock/memo/methods/non-void-method.ets +++ b/arkui-plugins/test/demo/mock/memo/methods/non-void-method.ets @@ -13,10 +13,11 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo, __memo_context_type, __memo_id_type } from "arkui.stateManagement.runtime"; @Retention({policy:"SOURCE"}) @interface memo_intrinsic {} @Retention({policy:"SOURCE"}) @interface memo_entry {} +@Retention({policy:"SOURCE"}) @interface memo_skip {} export declare function __context(): __memo_context_type export declare function __id(): __memo_id_type @@ -55,6 +56,12 @@ class Test { return entry() } } + + @memo memo_skip_args(arg1: number, @memo_skip arg2: string, @memo_skip arg3: @memo () => void): string { + let a = arg1; + arg3(); + return arg2; + } } class Use { diff --git a/arkui-plugins/test/demo/mock/memo/methods/void-method.ets b/arkui-plugins/test/demo/mock/memo/methods/void-method.ets index ed3339dd1..d2ee07cd7 100644 --- a/arkui-plugins/test/demo/mock/memo/methods/void-method.ets +++ b/arkui-plugins/test/demo/mock/memo/methods/void-method.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; class A { x: int diff --git a/arkui-plugins/test/demo/mock/memo/properties/class-constructor.ets b/arkui-plugins/test/demo/mock/memo/properties/class-constructor.ets index 725d2702b..a349e0450 100644 --- a/arkui-plugins/test/demo/mock/memo/properties/class-constructor.ets +++ b/arkui-plugins/test/demo/mock/memo/properties/class-constructor.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; interface A { @memo a: () => void diff --git a/arkui-plugins/test/demo/mock/memo/properties/class-properties.ets b/arkui-plugins/test/demo/mock/memo/properties/class-properties.ets index 9fe1e5dc4..79c72508b 100644 --- a/arkui-plugins/test/demo/mock/memo/properties/class-properties.ets +++ b/arkui-plugins/test/demo/mock/memo/properties/class-properties.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; class A { arg: () => void diff --git a/arkui-plugins/test/demo/mock/memo/properties/implements.ets b/arkui-plugins/test/demo/mock/memo/properties/implements.ets index ad61b2a7a..cdf8c6644 100644 --- a/arkui-plugins/test/demo/mock/memo/properties/implements.ets +++ b/arkui-plugins/test/demo/mock/memo/properties/implements.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; interface A { @memo prop: (() => void) | undefined diff --git a/arkui-plugins/test/demo/mock/memo/properties/interfaces.ets b/arkui-plugins/test/demo/mock/memo/properties/interfaces.ets index c46a181e5..80a9332fd 100644 --- a/arkui-plugins/test/demo/mock/memo/properties/interfaces.ets +++ b/arkui-plugins/test/demo/mock/memo/properties/interfaces.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; interface A { arg: () => void diff --git a/arkui-plugins/test/demo/mock/resource/ResourceTable.txt b/arkui-plugins/test/demo/mock/resource/ResourceTable.txt new file mode 100644 index 000000000..6c0c36582 --- /dev/null +++ b/arkui-plugins/test/demo/mock/resource/ResourceTable.txt @@ -0,0 +1,13 @@ +string EntryAbility_desc 0x01000002 +string EntryAbility_label 0x01000003 +string app_name 0x01000000 +string module_desc 0x01000004 +color start_window_background 0x01000005 +float page_text_font_size 0x01000006 +media app_icon 0x01000001 +media background 0x01000007 +media foreground 0x01000008 +media layered_image 0x01000009 +media startIcon 0x0100000a +profile backup_config 0x0100000b +profile main_pages 0x0100000c \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/resource/rawfile/mock.txt b/arkui-plugins/test/demo/mock/resource/rawfile/mock.txt new file mode 100644 index 000000000..1eb866e0e --- /dev/null +++ b/arkui-plugins/test/demo/mock/resource/rawfile/mock.txt @@ -0,0 +1 @@ +mocking rawfile \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.styledString.d.ets b/arkui-plugins/test/demo/mock/wrap-builder/builder-in-generic.ets similarity index 41% rename from arkui-plugins/test/local/@ohos.arkui.component.styledString.d.ets rename to arkui-plugins/test/demo/mock/wrap-builder/builder-in-generic.ets index 4a0e94d6f..ab94ff3f3 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.styledString.d.ets +++ b/arkui-plugins/test/demo/mock/wrap-builder/builder-in-generic.ets @@ -13,20 +13,36 @@ * limitations under the License. */ -export declare class StyledString { - constructor(value: string /*| ImageAttachment | CustomSpan, styles?: Array*/); +import { Builder, Text, Color, WrappedBuilder, wrapBuilder, Entry, Component, Row, ForEach } from '@ohos.arkui.component'; +import { State } from '@ohos.arkui.stateManagement'; - readonly length: number; +@Builder +function MyBuilder(value: string, size: number) { + Text(value) + .fontSize(size) +} - getString(): string; - // getStyles(start: number, length: number, styledKey?: StyledStringKey): Array; - equals(other: StyledString): boolean; - subStyledString(start: number, length?: number): StyledString; +@Builder +function YourBuilder(value: string, size: number) { + Text(value) + .fontSize(size) + .fontColor(Color.Pink) +} - static fromHtml(html: string): Promise; - static toHtml(styledString: StyledString): string; - // static marshalling(styledString: StyledString, callback: StyledStringMarshallCallback): ArrayBuffer; - // static unmarshalling(buffer: ArrayBuffer, callback: StyledStringUnmarshallCallback): Promise; - // static marshalling(styledString: StyledString): ArrayBuffer; - // static unmarshalling(buffer: ArrayBuffer): Promise; +let globalBuilder: (@Builder (value: string, size: number) => void) = MyBuilder; +let builderArr: (@Builder (value: string, size: number) => void)[] = [MyBuilder, YourBuilder] + +@Entry +@Component +struct Index { + @State message: string = 'Hello World'; + + build() { + Row() { + globalBuilder(this.message, 50); + ForEach(builderArr, (item: (@Builder (value: string, size: number) => void)) => { + item('Hello World', 30); + }) + }.height('100%') + } } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/wrap-builder/init-with-builder.ets b/arkui-plugins/test/demo/mock/wrap-builder/init-with-builder.ets new file mode 100644 index 000000000..8ec8bd8a6 --- /dev/null +++ b/arkui-plugins/test/demo/mock/wrap-builder/init-with-builder.ets @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Text, WrappedBuilder, wrapBuilder, Builder, Column } from "@kit.ArkUI" + +@Builder +function myBuilder(value: string, size: number) { + Text(value).fontSize(size) +} + +type MyBuilderFuncType = @Builder (value: string, size: number) => void; +let globalBuilder: WrappedBuilder = wrapBuilder(myBuilder); + +@Component +struct ImportStruct { + build() { + Column() { + globalBuilder.builder('hello', 50) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-in-generic.ets b/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-in-generic.ets new file mode 100644 index 000000000..0f82d420d --- /dev/null +++ b/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-in-generic.ets @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Builder, Text, Color, WrappedBuilder, wrapBuilder, Entry, Component, Row, ForEach } from '@ohos.arkui.component'; +import { State } from '@ohos.arkui.stateManagement'; + +@Builder +function MyBuilder(value: string, size: number) { + Text(value) + .fontSize(size) +} + +@Builder +function YourBuilder(value: string, size: number) { + Text(value) + .fontSize(size) + .fontColor(Color.Pink) +} + +let globalBuilder: WrappedBuilder<@Builder (value: string, size: number) => void> = wrapBuilder(MyBuilder); +let builderArr: WrappedBuilder<@Builder (value: string, size: number) => void>[] = [wrapBuilder(MyBuilder), wrapBuilder(YourBuilder)] +let wrappedBuilder1: WrappedBuilder<@Builder (value: string, size: number) => void> = wrapBuilder<@Builder (value: string, size: number) => void>(MyBuilder); +let wrappedBuilder2: WrappedBuilder<@Builder (value: string, size: number) => void> = new WrappedBuilder<@Builder (value: string, size: number) => void>(MyBuilder); + +@Entry +@Component +struct Index { + @State message: string = 'Hello World'; + + build() { + Row() { + globalBuilder.builder(this.message, 50); + ForEach(builderArr, (item: WrappedBuilder<@Builder (value: string, size: number) => void>) => { + item.builder('Hello World', 30); + }) + }.height('100%') + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-in-ui.ets b/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-in-ui.ets new file mode 100644 index 000000000..f92db0bc0 --- /dev/null +++ b/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-in-ui.ets @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Text, WrappedBuilder, wrapBuilder, Builder, Column, ForEach } from "@kit.ArkUI" + +@Builder +function myBuilder(value: string, size: number) { + Text(value).fontSize(size) +} + +@Builder +function yourBuilder(value: string, size: number) { + Text(value).fontSize(size) +} + +type MyBuilderFuncType = @Builder (value: string, size: number) => void; +const globalBuilderArr: WrappedBuilder[] = [wrapBuilder(myBuilder), wrapBuilder(yourBuilder)]; + +@Component +struct ImportStruct { + @Builder testBuilder() { + ForEach(globalBuilderArr, (item: WrappedBuilder) => { + item.builder('hello world', 39) + }) + } + + build() { + Column() { + this.testBuilder() + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-with-lambda.ets b/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-with-lambda.ets new file mode 100644 index 000000000..4604a07a5 --- /dev/null +++ b/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-with-lambda.ets @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Observed, Builder, Entry, Component, State } from "@kit.ArkUI"; +import { WrappedBuilder, wrapBuilder } from "@kit.ArkUI"; +import { Column, Text, Button, ClickEvent } from "@kit.ArkUI"; + +@Observed +class Tmp { + paramA2: string = 'hello'; +} + +@Builder function overBuilder(param: () => Tmp) { + Column() { + Text(`wrapBuildervalue:${param().paramA2}`) + } +} + +type MyBuilderFuncType = @Builder (param: () => Tmp) => void; +const wBuilder: WrappedBuilder = wrapBuilder(overBuilder); + +@Component +struct Parent { + @State label: Tmp = new Tmp(); + build() { + Column() { + wBuilder.builder(() => { return { paramA2: this.label.paramA2 } }) + Button('Click me').onClick((e: ClickEvent) => { + this.label.paramA2 = 'ArkUI'; + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/xcomponent/xcomponent-basic.ets b/arkui-plugins/test/demo/mock/xcomponent/xcomponent-basic.ets deleted file mode 100644 index aa644d5ed..000000000 --- a/arkui-plugins/test/demo/mock/xcomponent/xcomponent-basic.ets +++ /dev/null @@ -1,19 +0,0 @@ -import {Component, Flex, XComponent, FlexDirection, XComponentType, Entry, XComponentController, ItemAlign, FlexAlign, XComponentParameter} from '@ohos.arkui.component' - -@Entry -@Component -struct Index { - myXComponentController: XComponentController = new XComponentController(); - build() { - Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { - XComponent({ - id: 'xComponentId', - type: XComponentType.TEXTURE, - libraryname: 'nativerender', - controller: this.myXComponentController - } as XComponentParameter) - } - .width('100%') - .height('100%') - } -} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.text.d.ets b/arkui-plugins/test/local/@ohos.arkui.component.text.d.ets deleted file mode 100644 index 5ba7ef754..000000000 --- a/arkui-plugins/test/local/@ohos.arkui.component.text.d.ets +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2025 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Resource } from "@ohos.arkui.external.resource"; -import { StyledString } from "@ohos.arkui.component.styledString"; -import { ComponentBuilder, CommonMethod } from "@ohos.arkui.component.common"; -import { ResourceColor, Length } from "@ohos.arkui.component.units"; -import { memo } from '@ohos.arkui.stateManagement.runtime'; - -export declare class TextController { - closeSelectionMenu(): void; - setStyledString(value: StyledString): void; - - // getLayoutManager(): LayoutManager; -} - -export declare interface TextOptions { - controller: TextController; -} - -export declare interface TextAttribute extends CommonMethod { - // @memo - // font(value: Font, options?: FontSettingOptions): this; - @memo - fontColor(value: ResourceColor): this; - @memo - fontSize(value: number | string | Resource): this; - @memo - minFontSize(value: number | string | Resource): this; - @memo - maxFontSize(value: number | string | Resource): this; - @memo - minFontScale(scale: number | Resource): this; - @memo - maxFontScale(scale: number | Resource): this; - // @memo - // fontStyle(value: FontStyle): this; - // @memo - // fontWeight(value: number | FontWeight | string): this; - // @memo - // fontWeight(weight: number | FontWeight | string, options?: FontSettingOptions): this; - // @memo - // lineSpacing(value: LengthMetrics): this; - // @memo - // textAlign(value: TextAlign): this; - @memo - lineHeight(value: number | string | Resource): this; - // @memo - // textOverflow(options: TextOverflowOptions): this; - @memo - fontFamily(value: string | Resource): this; - @memo - maxLines(value: number): this; - // @memo - // decoration(value: DecorationStyleInterface): this; - @memo - letterSpacing(value: number | string): this; - // @memo - // textCase(value: TextCase): this; - @memo - baselineOffset(value: number | string): this; - // @memo - // copyOption(value: CopyOptions): this; - @memo - draggable(value: boolean): this; - // @memo - // textShadow(value: ShadowOptions | Array): this; - // @memo - // heightAdaptivePolicy(value: TextHeightAdaptivePolicy): this; - @memo - textIndent(value: Length): this; - // @memo - // wordBreak(value: WordBreak): this; - // @memo - // lineBreakStrategy(strategy: LineBreakStrategy): this; - @memo - onCopy(callback: (value: string) => void): this; - @memo - selection(selectionStart: number, selectionEnd: number): this; - @memo - caretColor(color: ResourceColor): this; - @memo - selectedBackgroundColor(color: ResourceColor): this; - // @memo - // ellipsisMode(value: EllipsisMode): this; - @memo - enableDataDetector(enable: boolean): this; - // @memo - // dataDetectorConfig(config: TextDataDetectorConfig): this; - // @memo - // bindSelectionMenu(spanType: TextSpanType, content: CustomBuilder, responseType: TextResponseType, - // options?: SelectionMenuOptions): this; - @memo - onTextSelectionChange(callback: (selectionStart: number, selectionEnd: number) => void): this; - @memo - fontFeature(value: string): this; - // @memo - // marqueeOptions(options: Optional): this; - // @memo - // onMarqueeStateChange(callback: Callback): this; - @memo - privacySensitive(supported: boolean): this; - // @memo - // textSelectable(mode: TextSelectableMode): this; - // @memo - // editMenuOptions(editMenu: EditMenuOptions): this; - @memo - halfLeading(halfLeading: boolean): this; - @memo - enableHapticFeedback(isEnabled: boolean): this; -} - -@memo -@ComponentBuilder -export declare function Text ( - value?: string | Resource, - options?: TextOptions, - @memo - content?: () => void -): TextAttribute; diff --git a/arkui-plugins/test/local/@ohos.arkui.stateManagement.runtime.d.ets b/arkui-plugins/test/local/@ohos.arkui.stateManagement.runtime.d.ets deleted file mode 100644 index f70c0cb7f..000000000 --- a/arkui-plugins/test/local/@ohos.arkui.stateManagement.runtime.d.ets +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2025 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { LocalStorage } from "@ohos.arkui.stateManagement.storage" - -// From incremental engine -@Retention({policy: "SOURCE"}) -export declare @interface memo {}; - -export type __memo_context_type = StateContext; -export type __memo_id_type = MemoCallSiteKey; - -export type MemoCallSiteKey = int; - -export declare interface Disposable { - readonly disposed: boolean; - dispose(): void; -} - -export declare interface State { - readonly modified: boolean; - readonly value: T; -} - -export declare interface MutableState extends Disposable, State { - value: T; -} - -export type Equivalent = (oldV: T, newV: T) => boolean; - -export declare interface InternalScope { - readonly unchanged: boolean; - readonly cached: Value; - recache(newValue?: Value): Value; - param(index: int, value: T, equivalent?: Equivalent, name?: string, contextLocal?: boolean): State; -} - -export declare interface StateContext { - scope(id: MemoCallSiteKey, paramCount?: int): InternalScope; -} - -// From Arkoala -export declare function propState(value?: T): MutableState; -export declare function objectLinkState(value?: T): MutableState; -export declare function stateOf(value: T): MutableState; -export declare function contextLocalStateOf(value: T, key: () => T): MutableState; -export declare function contextLocal(value: T): MutableState; -export declare function observableProxy(value: T): T; -export declare function StorageLinkState(storage: LocalStorage, name: string, value: T): MutableState -export declare function AppStorageLinkState(name: string, value: T): MutableState; \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.stateManagement.storage.d.ets b/arkui-plugins/test/local/@ohos.arkui.stateManagement.storage.d.ets deleted file mode 100644 index a7257c839..000000000 --- a/arkui-plugins/test/local/@ohos.arkui.stateManagement.storage.d.ets +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2025 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export declare interface StorageProperty { - key: string; - defaultValue: number | string | boolean | Object; -} - -export type PersistPropsOptions = StorageProperty; - -export declare interface AbstractProperty { - info(): string; - get(): T; - set(newValue: T): void; -} - -export declare interface SubscribedAbstractProperty extends AbstractProperty { - aboutToBeDeleted(): void; -} - -export declare class LocalStorage { - static getShared(): LocalStorage | undefined; - - constructor(initializingProperties?: StorageProperty[]); - - has(propName: string): boolean; - - keys(): IterableIterator; - - size(): int; - - get(propName: string): T | undefined; - - set(propName: string, newValue: T): boolean; - - setOrCreate(propName: string, newValue?: T): boolean; - - ref(propName: string): AbstractProperty | undefined; - - setAndRef(propName: string, defaultValue: T): AbstractProperty; - - link(propName: string): SubscribedAbstractProperty | undefined; - - setAndLink(propName: string, defaultValue: T): SubscribedAbstractProperty; - - prop(propName: string): SubscribedAbstractProperty | undefined; - - setAndProp(propName: string, defaultValue: T): SubscribedAbstractProperty; - - delete(propName: string): boolean; - - clear(): boolean; -} - -export declare class AppStorage { - static has(propName: string): boolean; - - static keys(): IterableIterator; - - static size(): int; - - static get(propName: string): T | undefined; - - static set(propName: string, newValue: T): boolean; - - static setOrCreate(propName: string, newValue?: T): boolean; - - static ref(propName: string): AbstractProperty | undefined; - - static setAndRef(propName: string, defaultValue: T): AbstractProperty; - - static link(propName: string): SubscribedAbstractProperty | undefined; - - static setAndLink(propName: string, defaultValue: T): SubscribedAbstractProperty; - - static prop(propName: string): SubscribedAbstractProperty | undefined; - - static setAndProp(propName: string, defaultValue: T): SubscribedAbstractProperty; - - static delete(propName: string): boolean; - - static clear(): boolean; -} - -export declare class PersistentStorage { - - static persistProp(key: string, defaultValue: T): void; - - static deleteProp(key: string): void; - - static persistProps(props: PersistPropsOptions[]): void; - - static keys(): Array; -} - -export declare interface EnvPropsOptions { - key: string; - defaultValue: number | string | boolean; -} - -export declare class Environment { - static envProp(key: string, value: S): boolean; - - static envProps(props: EnvPropsOptions[]): void; - - static keys(): Array; -} \ No newline at end of file diff --git a/arkui-plugins/test/localtest_config.js b/arkui-plugins/test/localtest_config.js index 87fd2e7ee..00d2cfb51 100644 --- a/arkui-plugins/test/localtest_config.js +++ b/arkui-plugins/test/localtest_config.js @@ -16,6 +16,10 @@ const fs = require('fs'); const path = require('path'); +function changePathToAbsPath(p) { + return path.resolve(p); +} + // 获取当前目录 const currentDirectory = process.cwd(); let workSpace = currentDirectory; @@ -27,25 +31,63 @@ const jsonFilePath = path.join(__dirname, 'demo/localtest/build_config_template. const outJsonFilePath = path.join(__dirname, 'demo/localtest/build_config.json'); try { - // 读取 JSON 文件内容 - const data = fs.readFileSync(jsonFilePath, 'utf8'); - const jsonData = JSON.parse(data); - console.log(jsonData) - // 处理 baseUrl 字段 - if (jsonData.buildSdkPath) { - jsonData.buildSdkPath = jsonData.buildSdkPath.replace(/workspace/g, workSpace); - } - - // 处理 plugins 字段 - if (jsonData.plugins.ui_plugin) { - jsonData.plugins.ui_plugin = jsonData.plugins.ui_plugin.replace(/workspace/g, workSpace); - } - if (jsonData.plugins.memo_plugin) { - jsonData.plugins.memo_plugin = jsonData.plugins.memo_plugin.replace(/workspace/g, workSpace); - } - - // 将修改后的内容写回 JSON 文件 - fs.writeFileSync(outJsonFilePath, JSON.stringify(jsonData, null, 2), 'utf8'); + // 读取 JSON 文件内容 + const data = fs.readFileSync(jsonFilePath, 'utf8'); + const jsonData = JSON.parse(data); + console.log(jsonData) + // 处理 baseUrl 字段 + if (jsonData.buildSdkPath) { + jsonData.buildSdkPath = jsonData.buildSdkPath.replace(/workspace/g, workSpace); + } + + // 处理 plugins 字段 + if (jsonData.plugins.ui_syntax_plugin) { + jsonData.plugins.ui_syntax_plugin = jsonData.plugins.ui_syntax_plugin.replace(/workspace/g, workSpace); + } + if (jsonData.plugins.ui_plugin) { + jsonData.plugins.ui_plugin = jsonData.plugins.ui_plugin.replace(/workspace/g, workSpace); + } + if (jsonData.plugins.memo_plugin) { + jsonData.plugins.memo_plugin = jsonData.plugins.memo_plugin.replace(/workspace/g, workSpace); + } + + // compileFiles + if (jsonData.compileFiles) { + jsonData.compileFiles = jsonData.compileFiles.map((file) => changePathToAbsPath(file)); + } + + // entryFiles + if (jsonData.entryFiles) { + jsonData.entryFiles = jsonData.entryFiles.map((file) => changePathToAbsPath(file)); + } + + // moduleRootPath + if (jsonData.moduleRootPath) { + jsonData.moduleRootPath = changePathToAbsPath(jsonData.moduleRootPath); + } + + // sourceRoots + if (jsonData.sourceRoots) { + jsonData.sourceRoots = jsonData.sourceRoots.map((file) => changePathToAbsPath(file)); + } + + // loaderOutPath + if (jsonData.loaderOutPath) { + jsonData.loaderOutPath = changePathToAbsPath(jsonData.loaderOutPath); + } + + // loaderOutPath + if (jsonData.cachePath) { + jsonData.cachePath = changePathToAbsPath(jsonData.cachePath); + } + + // appModuleJsonPath + if (jsonData.aceModuleJsonPath) { + jsonData.aceModuleJsonPath = changePathToAbsPath(jsonData.aceModuleJsonPath); + } + + // 将修改后的内容写回 JSON 文件 + fs.writeFileSync(outJsonFilePath, JSON.stringify(jsonData, null, 2), 'utf8'); } catch (error) { - console.error('处理 JSON 文件时出错:', error); + console.error('处理 JSON 文件时出错:', error); } \ No newline at end of file diff --git a/arkui-plugins/test/package.json b/arkui-plugins/test/package.json index 07123fbd7..46947c865 100644 --- a/arkui-plugins/test/package.json +++ b/arkui-plugins/test/package.json @@ -1,7 +1,6 @@ { "name": "arkui-plugins-test", "version": "1.0.0", - "description": "", "private": true, "scripts": { "compile:ohos": "node $INIT_CWD/../../../../arkcompiler/ets_frontend/ets2panda/driver/build-system/dist/entry.js ./demo/hello_world/build_config.json", @@ -10,7 +9,15 @@ "clean:localtest": "rm -rf dist", "clean:test": "rm -rf generated", "clean:all": "npm run clean:localtest && npm run clean:test", + "mem": "rm -rf dist && node localtest_config.js && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib LD_BIND_NOW=1 /usr/local/valgrind-3.25.1/bin/valgrind --tool=massif --pages-as-heap=yes --heap=yes --alloc-fn='libes2panda_public.so*' --alloc-fn='*es2panda.node*' node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", + "local": "rm -rf dist && node localtest_config.js && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", + "inspect": "rm -rf dist && node localtest_config.js && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib node --inspect-brk --expose_gc --no-turbo-inlining $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", + "clinic_mem": "rm -rf dist && node localtest_config.js && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib clinic heapprofiler --node-arguments='--heapsnapshot-on-signal' -- node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", + "mem1": "rm -rf dist && node localtest_config.js && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib clinic flame --mem --collect-only -- node --expose_gc --heap-prof --trace-gc --track-heap-objects $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", + "mem2": "rm -rf dist && node localtest_config.js && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib clinic flame --mem-prof -- node --no-node-snapshot --no-deprecation $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", + "valgrind": "rm -rf dist && node localtest_config.js && npm run compile:plugins && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib LD_BIND_NOW=1 /usr/local/valgrind-3.25.1/bin/valgrind --tool=massif --pages-as-heap=yes --heap=yes --alloc-fn='libes2panda_public.so*' --alloc-fn='*es2panda.node*' node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", "test": "npm run clean:all && npm run compile:plugins && cd .. && npm run test", + "test:gdb": "npm run clean:all && npm run compile:plugins && cd .. && npm run test:gdb", "localtest": "rm -rf dist && node localtest_config.js && npm run compile:plugins && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", "localtest_gdb": "LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib gdb --args node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", "localtest_decl": "rm -rf dist && node localtest_decl_config.js && npm run compile:plugins && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_decl_config.json", diff --git a/arkui-plugins/test/test.log b/arkui-plugins/test/test.log deleted file mode 100644 index 984a29adf..000000000 --- a/arkui-plugins/test/test.log +++ /dev/null @@ -1,372 +0,0 @@ - -> build_system_test@1.0.0 compile:ohos_sdk -> node /home/wuhaibin/newcode/oh/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/hello_world/build_config.json - -[ - '/home/wuhaibin/.nvm/versions/node/v23.8.0/bin/node', - '/home/wuhaibin/newcode/oh/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js', - './demo/hello_world/build_config.json' -] -Updated PATH: /home/wuhaibin/newcode/oh/developtools/ace_ets2bundle/arkui-plugins/test/node_modules/.bin:/home/wuhaibin/newcode/oh/developtools/ace_ets2bundle/arkui-plugins/node_modules/.bin:/home/wuhaibin/newcode/oh/developtools/ace_ets2bundle/node_modules/.bin:/home/wuhaibin/newcode/oh/developtools/node_modules/.bin:/home/wuhaibin/newcode/oh/node_modules/.bin:/home/wuhaibin/newcode/node_modules/.bin:/home/wuhaibin/node_modules/.bin:/home/node_modules/.bin:/node_modules/.bin:/home/wuhaibin/.nvm/versions/node/v23.8.0/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin:/home/wuhaibin/.local/bin:/home/wuhaibin/bin:/home/wuhaibin/.nvm/versions/node/v23.8.0/bin:/home/wuhaibin/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib:/mnt/c/WINDOWS/system32:/mnt/c/WINDOWS:/mnt/c/WINDOWS/System32/Wbem:/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/:/mnt/c/WINDOWS/System32/OpenSSH/:/mnt/c/Users/wuhaibin/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/wuhaibin/AppData/Local/Programs/Microsoft VS Code/bin:/snap/bin:/home/wuhaibin/newcode/oh/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib -Loaded plugin: ui-plugin { uiTransform: [Function: uiTransform] } [Function: uiTransform] -Loaded plugin: memo-plugin { unmemoizeTransform: [Function: unmemoizeTransform] } [Function: unmemoizeTransform] -ets2pandaCmd: _ --extension ets --arktsconfig /home/wuhaibin/newcode/oh/developtools/ace_ets2bundle/arkui-plugins/test/dist/cache/entry/arktsconfig.json --output /home/wuhaibin/newcode/oh/developtools/ace_ets2bundle/arkui-plugins/test/dist/cache/entry/a.abc --debug-info ./demo/hello_world/entry/a.ets -[TS WRAPPER] CREATE CONFIG -InitModule: es2panda - -[TS WRAPPER] PROCEED TO STATE: 1 -es2panda proceedToState parsed -[TS WRAPPER] GET AST FROM CONTEXT -executing plugin: ui-plugin -[UI PLUGIN] AFTER PARSED ENTER -[AFTER PARSED SCRIPT]: -import { StructBase } from "@koalaui.arkts-arkui.StructBase"; - -import { Text as Text } from "@koalaui.arkts-arkui.Text"; - -import { Column as Column } from "@koalaui.arkts-arkui.Column"; - -import { Button as Button } from "@koalaui.arkts-arkui.Button"; - -import { Component as Component, StorageLink as StorageLink, State as State } from "@koalaui.arkts-arkui.Common"; - -import { UserView as UserView, UserViewBuilder as UserViewBuilder } from "@koalaui.arkts-arkui.UserView"; - -import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "@ohos.arkui.StateManagement.runtime"; - -import { memo as memo } from "@ohos.arkui.StateManagement.runtime"; - -function isTrue(): string { - return "aa"; -} - -final class MyStateSample extends StructBase { - public aaa: string = isTrue(); - - public build() { - Column(){ - Text("Hello World!"); - Text((this).aaa); - Button("change"); - }; - } - - public constructor() {} - -} - -class ComExampleTrivialApplication extends UserView { - public getBuilder(): UserViewBuilder { - let wrapper = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { - MyStateSample.instantiateImpl(undefined, ((): MyStateSample => new MyStateSample()), ({} as __Options_MyStateSample), undefined); - }); - return wrapper; - } - - public constructor() {} - -} - -interface __Options_MyStateSample { - -} - - -[UI PLUGIN] AFTER PARSED EXIT -plugin parsed finished -[TS WRAPPER] GET AST FROM CONTEXT -[TS WRAPPER] DESTROY AND RECREATE -[TS WRAPPER] PROCEED TO STATE: 4 -es2panda proceedToState checked -[TS WRAPPER] GET AST FROM CONTEXT -executing plugin: ui-plugin -[UI PLUGIN] AFTER CHECKED ENTER -[AFTER STRUCT SCRIPT] script: -import { StructBase as StructBase } from "@koalaui.arkts-arkui.StructBase"; - -import { Text as Text } from "@koalaui.arkts-arkui.Text"; - -import { Column as Column } from "@koalaui.arkts-arkui.Column"; - -import { Button as Button } from "@koalaui.arkts-arkui.Button"; - -import { Component as Component, StorageLink as StorageLink, State as State } from "@koalaui.arkts-arkui.Common"; - -import { UserView as UserView, UserViewBuilder as UserViewBuilder } from "@koalaui.arkts-arkui.UserView"; - -import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "@ohos.arkui.StateManagement.runtime"; - -import { memo as memo } from "@ohos.arkui.StateManagement.runtime"; - -abstract class ETSGLOBAL { - public static main() {} - - public static _$init$_() {} - - public static isTrue(): string { - return "aa"; - } - - -} - -class MyStateSample extends StructBase { - @memo()public __initializeStruct(initializers?: __Options_MyStateSample, @memo()content?: (()=> void)): void {} - - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} - - public aaa: string = ETSGLOBAL.isTrue(); - - @memo()protected _build(@memo()style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo()content: (()=> void) | undefined, initializers?: __Options_MyStateSample): void { - Column.instantiateImpl(((instance: Column): Column => { - return instance; - }), ((): Column => { - return new Column(); - }), (() => { - Text.instantiateImpl(((instance: Text): Text => { - return instance; - }), ((): Text => { - return new Text(); - }), "Hello World!") - Text.instantiateImpl(((instance: Text): Text => { - return instance; - }), ((): Text => { - return new Text(); - }), (this).aaa) - Button.instantiateImpl(((instance: Button): Button => { - return instance; - }), ((): Button => { - return new Button(); - }), "change") - })); - } - - public constructor() {} - -} - -class ComExampleTrivialApplication extends UserView { - public getBuilder(): UserViewBuilder { - let wrapper = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { - MyStateSample.instantiateImpl(undefined, ((): MyStateSample => { - return new MyStateSample(); - }), ({} as __Options_MyStateSample), undefined); - }); - return wrapper; - } - - public constructor() {} - -} - -interface __Options_MyStateSample { - -} - - -[UI PLUGIN] AFTER CHECKED EXIT -executing plugin: memo-plugin -[MEMO PLUGIN] AFTER CHECKED ENTER -[BEFORE MEMO SCRIPT] script: -import { StructBase as StructBase } from "@koalaui.arkts-arkui.StructBase"; - -import { Text as Text } from "@koalaui.arkts-arkui.Text"; - -import { Column as Column } from "@koalaui.arkts-arkui.Column"; - -import { Button as Button } from "@koalaui.arkts-arkui.Button"; - -import { Component as Component, StorageLink as StorageLink, State as State } from "@koalaui.arkts-arkui.Common"; - -import { UserView as UserView, UserViewBuilder as UserViewBuilder } from "@koalaui.arkts-arkui.UserView"; - -import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "@ohos.arkui.StateManagement.runtime"; - -import { memo as memo } from "@ohos.arkui.StateManagement.runtime"; - -abstract class ETSGLOBAL { - public static main() {} - - public static _$init$_() {} - - public static isTrue(): string { - return "aa"; - } - - -} - -class MyStateSample extends StructBase { - @memo()public __initializeStruct(initializers?: __Options_MyStateSample, @memo()content?: (()=> void)): void {} - - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} - - public aaa: string = ETSGLOBAL.isTrue(); - - @memo()protected _build(@memo()style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo()content: (()=> void) | undefined, initializers?: __Options_MyStateSample): void { - Column.instantiateImpl(((instance: Column): Column => { - return instance; - }), ((): Column => { - return new Column(); - }), (() => { - Text.instantiateImpl(((instance: Text): Text => { - return instance; - }), ((): Text => { - return new Text(); - }), "Hello World!") - Text.instantiateImpl(((instance: Text): Text => { - return instance; - }), ((): Text => { - return new Text(); - }), (this).aaa) - Button.instantiateImpl(((instance: Button): Button => { - return instance; - }), ((): Button => { - return new Button(); - }), "change") - })); - } - - public constructor() {} - -} - -class ComExampleTrivialApplication extends UserView { - public getBuilder(): UserViewBuilder { - let wrapper = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { - MyStateSample.instantiateImpl(undefined, ((): MyStateSample => { - return new MyStateSample(); - }), ({} as __Options_MyStateSample), undefined); - }); - return wrapper; - } - - public constructor() {} - -} - -interface __Options_MyStateSample { - -} - - -[AFTER MEMO SCRIPT] script: -import { StructBase as StructBase } from "@koalaui.arkts-arkui.StructBase"; - -import { Text as Text } from "@koalaui.arkts-arkui.Text"; - -import { Column as Column } from "@koalaui.arkts-arkui.Column"; - -import { Button as Button } from "@koalaui.arkts-arkui.Button"; - -import { Component as Component, StorageLink as StorageLink, State as State } from "@koalaui.arkts-arkui.Common"; - -import { UserView as UserView, UserViewBuilder as UserViewBuilder } from "@koalaui.arkts-arkui.UserView"; - -import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "@ohos.arkui.StateManagement.runtime"; - -import { memo as memo } from "@ohos.arkui.StateManagement.runtime"; - -abstract class ETSGLOBAL { - public static main() {} - - public static _$init$_() {} - - public static isTrue(): string { - return "aa"; - } - - -} - -class MyStateSample extends StructBase { - public __initializeStruct(__memo_context: __memo_context_type, __memo_id: __memo_id_type, initializers?: __Options_MyStateSample, content?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { - const __memo_scope = __memo_context.scope(((__memo_id) + (168924120)), 2); - const __memo_parameter_initializers = __memo_scope.param(0, initializers), __memo_parameter_content = __memo_scope.param(1, content); - if (__memo_scope.unchanged) { - __memo_scope.recache(__memo_scope.cached) - return; - } - { - __memo_scope.recache() - return; - } - } - - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} - - public aaa: string = ETSGLOBAL.isTrue(); - - protected _build(__memo_context: __memo_context_type, __memo_id: __memo_id_type, style: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: MyStateSample)=> MyStateSample) | undefined, content: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined, initializers?: __Options_MyStateSample): void { - const __memo_scope = __memo_context.scope(((__memo_id) + (168198604)), 3); - const __memo_parameter_style = __memo_scope.param(0, style), __memo_parameter_content = __memo_scope.param(1, content), __memo_parameter_initializers = __memo_scope.param(2, initializers); - if (__memo_scope.unchanged) { - __memo_scope.recache(__memo_scope.cached) - return; - } - Column.instantiateImpl(__memo_context, ((__memo_id) + (229216764)), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: Column): Column => { - return instance; - }), ((): Column => { - return new Column(); - }), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { - const __memo_scope = __memo_context.scope(((__memo_id) + (131080140)), 0); - if (__memo_scope.unchanged) { - __memo_scope.recache(__memo_scope.cached) - return; - } - Text.instantiateImpl(__memo_context, ((__memo_id) + (122349231)), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: Text): Text => { - return instance; - }), ((): Text => { - return new Text(); - }), "Hello World!") - Text.instantiateImpl(__memo_context, ((__memo_id) + (259830593)), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: Text): Text => { - return instance; - }), ((): Text => { - return new Text(); - }), (this).aaa) - Button.instantiateImpl(__memo_context, ((__memo_id) + (23671947)), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: Button): Button => { - return instance; - }), ((): Button => { - return new Button(); - }), "change") - { - __memo_scope.recache() - return; - } - })); - { - __memo_scope.recache() - return; - } - } - - public constructor() {} - -} - -class ComExampleTrivialApplication extends UserView { - public getBuilder(): UserViewBuilder { - let wrapper = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { - MyStateSample.instantiateImpl(__memo_context, ((__memo_id) + (44218244)), undefined, ((): MyStateSample => { - return new MyStateSample(); - }), ({} as __Options_MyStateSample), undefined); - }); - return wrapper; - } - - public constructor() {} - -} - -interface __Options_MyStateSample { - -} - - -[MEMO PLUGIN] AFTER CHECKED EXIT -plugin checked finished -[TS WRAPPER] GET AST FROM CONTEXT -[TS WRAPPER] DESTROY AND RECREATE -[TS WRAPPER] PROCEED TO STATE: 7 -es2panda bin generated -"/home/wuhaibin/newcode/oh/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/bin/ark_link" --output "/home/wuhaibin/newcode/oh/developtools/ace_ets2bundle/arkui-plugins/test/dist/modules_static.abc" -- @"dist/cache/fileInfo.txt" diff --git a/arkui-plugins/test/ut/common/annotation.test.ts b/arkui-plugins/test/ut/common/annotation.test.ts index cf9f7d191..663454255 100644 --- a/arkui-plugins/test/ut/common/annotation.test.ts +++ b/arkui-plugins/test/ut/common/annotation.test.ts @@ -13,24 +13,210 @@ * limitations under the License. */ -import { PluginTestContext, PluginTester } from '../../utils/plugin-tester'; -import { annotation } from '../../../common/arkts-utils'; import * as arkts from '@koalaui/libarkts'; +import path from 'path'; +import { PluginTester } from '../../utils/plugin-tester'; +import { BuildConfig, PluginTestContext } from '../../utils/shared-types'; +import { mockBuildConfig } from '../../utils/artkts-config'; +import { parseDumpSrc } from '../../utils/parse-string'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../utils/path-config'; +import { annotation } from '../../../common/arkts-utils'; +import { PluginContext, Plugins } from '../../../common/plugin-context'; +import { AbstractVisitor } from '../../../common/abstract-visitor'; +import { ProgramVisitor } from '../../../common/program-visitor'; +import { EXTERNAL_SOURCE_PREFIX_NAMES } from '../../../common/predefines'; + +const COMMON_UTILS_DIR_PATH: string = 'common-utils'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, COMMON_UTILS_DIR_PATH, 'annotation.ets')]; + +const pluginTester = new PluginTester('test arkts-utils', buildConfig); -const pluginTester = new PluginTester('test arkts-utils'); +type AnnotationAstNode = + | arkts.ClassDefinition + | arkts.ClassProperty + | arkts.ETSParameterExpression + | arkts.ArrowFunctionExpression + | arkts.MethodDefinition + | arkts.VariableDeclaration + | arkts.TSInterfaceDeclaration + | arkts.TSTypeAliasDeclaration; + +class AnnotationVisitor extends AbstractVisitor { + isRemover: boolean; + + constructor(isRemover?: boolean) { + super(); + this.isRemover = !!isRemover; + } + + private testAnnotation(): arkts.AnnotationUsage { + return annotation('TestAnno'); + } + + addTestAnnotation(node: AnnotationAstNode): void { + if (arkts.isEtsParameterExpression(node)) { + node.annotations = [this.testAnnotation()]; + } else if (arkts.isMethodDefinition(node)) { + node.scriptFunction.setAnnotations([this.testAnnotation()]); + node.setOverloads( + node.overloads.map((ov) => { + if (this.isAnnotationNode(ov)) { + this.addTestAnnotation(ov); + } + return ov; + }) + ); + } else { + node.setAnnotations([this.testAnnotation()]); + } + } + + removeTestAnnotation(node: AnnotationAstNode): void { + if (arkts.isEtsParameterExpression(node)) { + node.annotations = []; + } else if (arkts.isMethodDefinition(node)) { + node.scriptFunction.setAnnotations([]); + node.setOverloads( + node.overloads.map((ov) => { + if (this.isAnnotationNode(ov)) { + this.removeTestAnnotation(ov); + } + return ov; + }) + ); + } else { + node.setAnnotations([]); + } + } + + isAnnotationNode(node: arkts.AstNode): node is AnnotationAstNode { + return ( + arkts.isClassDefinition(node) || + arkts.isClassProperty(node) || + arkts.isMethodDefinition(node) || + arkts.isEtsParameterExpression(node) || + arkts.isArrowFunctionExpression(node) || + arkts.isMethodDefinition(node) || + arkts.isVariableDeclaration(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node) + ); + } + + visitor(node: arkts.AstNode): arkts.AstNode { + if (this.isAnnotationNode(node)) { + if (this.isRemover) { + this.removeTestAnnotation(node); + } else { + this.addTestAnnotation(node); + } + } + return this.visitEachChild(node); + } +} + +function addAnnotationTransform(this: PluginContext): arkts.EtsScript | undefined { + let script: arkts.EtsScript | undefined; + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; + if (!!contextPtr) { + let program = arkts.getOrUpdateGlobalContext(contextPtr).program; + const annotationAdder = new AnnotationVisitor(); + const programVisitor = new ProgramVisitor({ + pluginName: addAnnotationTransform.name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_ERROR, // ignored + visitors: [annotationAdder], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this, + }); + program = programVisitor.programVisitor(program); + script = program.astNode; + return script; + } + return script; +} + +function removeAnnotationTransform(this: PluginContext): arkts.EtsScript | undefined { + let script: arkts.EtsScript | undefined; + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; + if (!!contextPtr) { + let program = arkts.getOrUpdateGlobalContext(contextPtr).program; + const annotationAdder = new AnnotationVisitor(true); + const programVisitor = new ProgramVisitor({ + pluginName: addAnnotationTransform.name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_ERROR, // ignored + visitors: [annotationAdder], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this, + }); + program = programVisitor.programVisitor(program); + script = program.astNode; + return script; + } + return script; +} + +const addAnnotation: Plugins = { + name: 'add-annotation', + parsed: addAnnotationTransform, + checked: addAnnotationTransform, +}; + +const removeAnnotation: Plugins = { + name: 'remove-annotation', + parsed: removeAnnotationTransform, + checked: removeAnnotationTransform, +}; + +const expectedParseSnapshot: string = ` +@Retention({policy:\"SOURCE\"}) @interface TestAnno {} +@TestAnno() type TestType = number; +@TestAnno() (() => {}) +@TestAnno() class A { + @TestAnno() public prop: number = 1; + @TestAnno() public method(@TestAnno() arg1: number): void { + @TestAnno() const a: number = arg1; + } + @TestAnno() public constructor() {} +} +@TestAnno() interface __A { + @TestAnno() prop: number; +} +`; + +function testParseAnnotation(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParseSnapshot)); +} + +const expectedCheckSnapshot: string = ` +@TestAnno() function main() {} +@TestAnno() (() => {}); +@Retention({policy:\"SOURCE\"}) @interface TestAnno {} +@TestAnno() type TestType = number; +@TestAnno() class A { + @TestAnno() public prop: number = 1; + @TestAnno() public method(@TestAnno() arg1: number): void { + @TestAnno() const a: number = arg1; + } + @TestAnno() public constructor() {} +} +@TestAnno() interface __A { + @TestAnno() set prop(prop: number) + @TestAnno() get prop(): number +} +`; -function testAnnotation(this: PluginTestContext): void { - const anno: arkts.AnnotationUsage = annotation('State'); - expect(arkts.isAnnotationUsage(anno)).toBeTruthy(); - expect(anno.dumpSrc()).toBe('@State() '); +function testCheckAnnotation(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckSnapshot)); } pluginTester.run( 'annotation', - [], + [addAnnotation, removeAnnotation], { - parsed: [testAnnotation], - checked: [testAnnotation], + 'parsed:add-annotation': [testParseAnnotation], + 'checked:add-annotation': [testCheckAnnotation], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/argument-call.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/argument-call.test.ts index a3d7d2909..1506b9ca1 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/argument-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/argument-call.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -28,9 +29,10 @@ buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUN const pluginTester = new PluginTester('test memo function', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@functions.OptionalParametersAnnotation({minArgCount:3}) function memo_arg_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, arg2: ((x: number)=> number), @memo() arg3: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number), arg4?: ((x: number)=> number), @memo() arg5?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number)): void { +@memo() function memo_arg_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, arg2: ((x: number)=> number), @memo() arg3: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number), arg4?: ((x: number)=> number), @memo() arg5?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 5); const __memo_parameter_arg1 = __memo_scope.param(0, arg1), __memo_parameter_arg2 = __memo_scope.param(1, arg2), __memo_parameter_arg3 = __memo_scope.param(2, arg3), __memo_parameter_arg4 = __memo_scope.param(3, arg4), __memo_parameter_arg5 = __memo_scope.param(4, arg5); if (__memo_scope.unchanged) { @@ -48,7 +50,7 @@ function main() {} return; } } -@functions.OptionalParametersAnnotation({minArgCount:1}) function memo_arg_call_with_lowering(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, arg4?: ((x: number)=> number), @memo() arg5?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number)): void { +@memo() function memo_arg_call_with_lowering(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, arg4?: ((x: number)=> number), @memo() arg5?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 3); const __memo_parameter_arg1 = __memo_scope.param(0, arg1), __memo_parameter_arg4 = __memo_scope.param(1, arg4), __memo_parameter_arg5 = __memo_scope.param(2, arg5); if (__memo_scope.unchanged) { @@ -68,9 +70,9 @@ function main() {} return; } } -@functions.OptionalParametersAnnotation({minArgCount:0}) function args_with_default_values(__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: int, @memo() gensym%%_?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int), gensym%%_?: int, arg4?: int): void { +@memo() function args_with_default_values(__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: int, @memo() gensym%%_?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int), gensym%%_?: int, arg4?: int): void { let arg1: int = (((gensym%%_) !== (undefined)) ? gensym%%_ : (10 as int)); - let arg2: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type): int => { + let arg2: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; @@ -99,7 +101,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform argument calls in functions', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/complex-memo-intrinsic.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/complex-memo-intrinsic.test.ts new file mode 100644 index 000000000..ba06b5787 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/complex-memo-intrinsic.test.ts @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig, mockProjectConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { ProjectConfig } from '../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'memo/functions'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'complex-memo-intrinsic.ets'), +]; + +const projectConfig: ProjectConfig = mockProjectConfig(); +projectConfig.frameworkMode = 'frameworkMode'; + +const pluginTester = new PluginTester('test complex memo intrinsic function', buildConfig, projectConfig); + +const expectedScript: string = ` + +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +function main() {} + + +@memo_intrinsic() export function factory(__memo_context: __memo_context_type, __memo_id: __memo_id_type, compute: (()=> Value)): Value + +export function cb(callback?: (()=> void)) { + if (callback) { + return; + } +} + +@memo_intrinsic() export function impl(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() style: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type, attributes: IA)=> void) | undefined), arr: SimpleArray, gensym%%_1?: string): void { + let err: string = (((gensym%%_1) !== (undefined)) ? gensym%%_1 : ("error message" as string)); + const s = factory(__memo_context, ((__memo_id) + (90010973)), (() => { + return new A(); + })); + s.aaa(arr.length); + ({let gensym%%_68 = style; + (((gensym%%_68) == (null)) ? undefined : gensym%%_68(__memo_context, ((__memo_id) + (222201816)), s))}); + if (!(s.bbb.get("some_key"))) { + throw new Error(err); + } + if (s.ccc) { + cb((() => { + return s.ddd.forEach(((s: number, t: string) => { + console.log(err); + return; + })); + })); + } else { + return; + } +} + +@Retention({policy:"SOURCE"}) @interface memo_intrinsic {} + +interface IA { + set ccc(ccc: boolean) + + get ccc(): boolean + +} + +class A implements IA { + public bbb: Map = new Map(); + + public ddd: Map = new Map(); + + public aaa(value: number): void {} + + public constructor() {} + + private _$property$_ccc: boolean = false; + + set ccc(_$property$_ccc: boolean) { + this._$property$_ccc = _$property$_ccc; + return; + } + + public get ccc(): boolean { + return this._$property$_ccc; + } + +} + +export type SimpleArray = (Array | ReadonlyArray | Readonly>); + +class Use { + @memo() public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (228150357)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + const style = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, attributes: IA) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (237001330)), 1); + const __memo_parameter_attributes = __memo_scope.param(0, attributes); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + }); + const arr = [1, 2, 3, 4]; + impl(__memo_context, ((__memo_id) + (158199735)), style, arr); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform complex @memo_intrinsic calls in functions', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/declare-and-call.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/declare-and-call.test.ts index db15946ae..99fba4a09 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/declare-and-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/declare-and-call.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -30,9 +31,11 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo function', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() function funcA(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void +@memo() function funcB(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -43,22 +46,52 @@ function main() {} __memo_scope.recache(); return; } -}); -@memo() function funcA(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void -function funcB(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { +} +@memo() function funcWithMemoBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() memo_arg: MemoBuilder): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_memo_arg = __memo_scope.param(0, memo_arg); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +} +function funcWithArg(arg: (()=> void)): void {} +function func(): void {} +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } funcA(__memo_context, ((__memo_id) + ())); + funcWithMemoBuilder(__memo_context, ((__memo_id) + ()), { + builder: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + funcWithArg((() => { + func(); + return; + })); + { + __memo_scope.recache(); + return; + } + }), + }); { __memo_scope.recache(); return; } -} +}); class A { - public foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -72,6 +105,10 @@ class A { } public constructor() {} } +interface MemoBuilder { + @memo() set builder(builder: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) + @memo() get builder(): ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) +} `; function testMemoTransformer(this: PluginTestContext): void { @@ -80,7 +117,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform declare functions and calls', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/inner-functions.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/inner-functions.test.ts index 8e5494643..a73b950b6 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/inner-functions.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/inner-functions.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -30,9 +31,10 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo function', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -function foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { +@memo() function foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -43,13 +45,13 @@ function foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type): vo return; } } -function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { +@memo() function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - const qux = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + const qux = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -68,14 +70,14 @@ function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type): vo } } class A { - public goo(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public goo(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { - __memo_scope.cached; - return; + __memo_scope.cached; + return; } let func = (() => {}); - let func2 = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + let func2 = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -102,7 +104,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform inner functions', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/internal-memo-arg.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/internal-memo-arg.test.ts new file mode 100644 index 000000000..02fc30648 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/internal-memo-arg.test.ts @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig, mockProjectConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { ProjectConfig } from '../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'memo/functions'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'internal-memo-arg.ets'), +]; + +const projectConfig: ProjectConfig = mockProjectConfig(); +projectConfig.frameworkMode = 'frameworkMode'; + +const pluginTester = new PluginTester('test internal memo argument calls', buildConfig, projectConfig); + +const expectedScript: string = ` + +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; + +import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; + +import { IncrementalNode as IncrementalNode } from "@koalaui.runtime.tree.IncrementalNode"; + +import { ControlledScope as ControlledScope, StateManager as StateManager } from "@koalaui.runtime.states.State"; + +function main() {} + + +export function __context(): __memo_context_type + +export function __id(): __memo_id_type + +@memo_entry() export function memoEntry(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> R)): R { + return entry(__memo_context, ((__memo_id) + (75311131))); +} + +@memo_entry() export function memoEntry1(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: T)=> R), arg: T): R { + return entry(__memo_context, ((__memo_id) + (168506859)), arg); +} + +@memo_entry() export function memoEntry2(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: T1, arg2: T2)=> R), arg1: T1, arg2: T2): R { + return entry(__memo_context, ((__memo_id) + (76962895)), arg1, arg2); +} + +@memo_intrinsic() export function contextLocalValue(__memo_context: __memo_context_type, __memo_id: __memo_id_type, name: string): Value { + return __memo_context.valueBy(name); +} + +@memo_intrinsic() export function contextLocalScope(__memo_context: __memo_context_type, __memo_id: __memo_id_type, name: string, value: Value, @memo() content: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { + const scope = __memo_context.scope(__memo_id, 1); + scope.param(0, value, undefined, name, true); + if (scope.unchanged) { + scope.cached; + } else { + content(__memo_context, ((__memo_id) + (2633070))); + scope.recache(); + } +} + +@memo_intrinsic() export function NodeAttach(__memo_context: __memo_context_type, __memo_id: __memo_id_type, create: (()=> Node), @memo() update: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, node: Node)=> void), reuseKey?: string): void { + const scope = __memo_context.scope(__memo_id, 0, create, undefined, undefined, undefined, reuseKey); + if (scope.unchanged) { + scope.cached; + } else { + try { + if (!reuseKey) { + update(__memo_context, ((__memo_id) + (6025780)), (__memo_context.node as Node)); + } else { + memoEntry(__memo_context, 0, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (31840240)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + update(__memo_context, ((__memo_id) + (253864074)), (__memo_context.node as Node)); + { + __memo_scope.recache(); + return; + } + })); + } + } finally { + scope.recache(); + } + } +} + +@memo_intrinsic() export function rememberControlledScope(__memo_context: __memo_context_type, __memo_id: __memo_id_type, invalidate: (()=> void)): ControlledScope { + return __memo_context.controlledScope(__memo_id, invalidate); +} + +@memo() export function Repeat(__memo_context: __memo_context_type, __memo_id: __memo_id_type, count: int, @memo() action: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, index: int)=> void)) { + const __memo_scope = __memo_context.scope(((__memo_id) + (200707415)), 2); + const __memo_parameter_count = __memo_scope.param(0, count), __memo_parameter_action = __memo_scope.param(1, action); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + for (let i = 0;((i) < (__memo_parameter_count.value));(i++)) { + memoEntry1(__memo_context, i, __memo_parameter_action.value, i); + } + { + __memo_scope.recache(); + return; + } +} + + +@Retention({policy:"SOURCE"}) @interface memo_intrinsic {} + +@Retention({policy:"SOURCE"}) @interface memo_entry {} + +export class MemoCallbackContext { + private readonly context: __memo_context_type; + + private readonly id: __memo_id_type; + + private constructor(context: __memo_context_type, id: __memo_id_type) { + this.context = context; + this.id = id; + } + + @memo() public static Make(__memo_context: __memo_context_type, __memo_id: __memo_id_type): MemoCallbackContext { + const __memo_scope = __memo_context.scope(((__memo_id) + (41727473)), 0); + if (__memo_scope.unchanged) { + return __memo_scope.cached; + } + return __memo_scope.recache(new MemoCallbackContext(__memo_context, __memo_id)); + } + +} + +export class CustomComponent { + @memo() public static _instantiateImpl(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + const __memo_scope = __memo_context.scope(((__memo_id) + (214802466)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + const context: StateManager = (__memo_context as StateManager); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform internal __context() and __id() calls in functions', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/non-void-return-type.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/non-void-return-type.test.ts index 19b061ad6..c59453f5f 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/non-void-return-type.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/non-void-return-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -30,30 +31,31 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo function', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -function funcNum(__memo_context: __memo_context_type, __memo_id: __memo_id_type): number { +@memo() function funcNum(__memo_context: __memo_context_type, __memo_id: __memo_id_type): number { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(1); } -function funcStr(__memo_context: __memo_context_type, __memo_id: __memo_id_type): string { +@memo() function funcStr(__memo_context: __memo_context_type, __memo_id: __memo_id_type): string { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(\"1\"); } -function funcBool(__memo_context: __memo_context_type, __memo_id: __memo_id_type): boolean { +@memo() function funcBool(__memo_context: __memo_context_type, __memo_id: __memo_id_type): boolean { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(false); } -function funcA(__memo_context: __memo_context_type, __memo_id: __memo_id_type): A { +@memo() function funcA(__memo_context: __memo_context_type, __memo_id: __memo_id_type): A { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; @@ -62,21 +64,21 @@ function funcA(__memo_context: __memo_context_type, __memo_id: __memo_id_type): str: \"1\", }); } -function funcB(__memo_context: __memo_context_type, __memo_id: __memo_id_type): B { +@memo() function funcB(__memo_context: __memo_context_type, __memo_id: __memo_id_type): B { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(((str: string) => {})); } -function funcC(__memo_context: __memo_context_type, __memo_id: __memo_id_type): C { +@memo() function funcC(__memo_context: __memo_context_type, __memo_id: __memo_id_type): C { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(new C(\"1\")); } -function funcD(__memo_context: __memo_context_type, __memo_id: __memo_id_type): (()=> void) { +@memo() function funcD(__memo_context: __memo_context_type, __memo_id: __memo_id_type): (()=> void) { const __memo_scope = __memo_context.scope<(()=> void)>(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; @@ -102,7 +104,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform functions with non-void return type', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/type-reference.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/type-reference.test.ts index 7db7cd676..eca62211f 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/type-reference.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/type-reference.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -30,15 +31,27 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo function', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() function A(__memo_context: __memo_context_type, __memo_id: __memo_id_type): Attribute -function func(__memo_context: __memo_context_type, __memo_id: __memo_id_type): ItemBuilder { +@memo() export function A(__memo_context: __memo_context_type, __memo_id: __memo_id_type): Attribute +@memo() function func(__memo_context: __memo_context_type, __memo_id: __memo_id_type): ItemBuilder { const __memo_scope = __memo_context.scope>(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } - return __memo_scope.recache(((__memo_context: __memo_context_type, __memo_id: __memo_id_type, item: Item): void => {})); + return __memo_scope.recache(((__memo_context: __memo_context_type, __memo_id: __memo_id_type, item: Item): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_item = __memo_scope.param(0, item); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + })); } @memo() type ItemBuilder = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, item: Item)=> void); interface Item { @@ -49,13 +62,13 @@ interface Attribute { @memo() each(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() itemGenerator: ItemBuilder): Attribute } class B { - public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - A(__memo_context, ((__memo_id) + ())).each(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, ri: Item): void => { + A(__memo_context, ((__memo_id) + ())).each(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, ri: Item) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_ri = __memo_scope.param(0, ri); if (__memo_scope.unchanged) { @@ -82,7 +95,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform functions with type reference', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/void-return-type.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/void-return-type.test.ts index 08c0eb3bd..fd2f3cb88 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/void-return-type.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/void-return-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -30,9 +31,10 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo function', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -function func(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { +@memo() function func(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -51,7 +53,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform functions with void return type', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/argument-call.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/argument-call.test.ts index f23a8d252..18b3a26e3 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/argument-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/argument-call.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const LAMBDA_DIR_PATH: string = 'memo/lambdas'; @@ -28,14 +29,25 @@ buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, LAM const pluginTester = new PluginTester('test memo lambda', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} ((arg: (()=> void)) => {})((() => {})); -((arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => {})(@memo() (() => {})); +((arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => {})(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +})); ((gensym%%_1?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => { - let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_1) !== (undefined)) ? gensym%%_1 : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_1) !== (undefined)) ? gensym%%_1 : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + (201676739)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -46,7 +58,7 @@ function main() {} return; } }) as @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))); -})(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +})(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + (209782503)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -58,14 +70,14 @@ function main() {} } })); -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void => { - let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => { + let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -86,7 +98,7 @@ function main() {} __memo_scope.recache(); return; } - })(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + })(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -103,13 +115,13 @@ function main() {} } }); -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - let goo = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: string): void => { + let goo = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: string) => { let name: string = (((gensym%%_) !== (undefined)) ? gensym%%_ : (\"old\" as string)); const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_name = __memo_scope.param(0, name); @@ -131,7 +143,7 @@ function main() {} (() => { let foo = ((gensym%%_?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => { - let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -143,7 +155,7 @@ function main() {} } }) as @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))); }); - foo(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + foo(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -163,7 +175,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform argument calls in lambdas', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/function-with-receiver.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/function-with-receiver.test.ts index be488294a..60d5c5985 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/function-with-receiver.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/function-with-receiver.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; import { Plugins } from '../../../../common/plugin-context'; import { uiTransform } from '../../../../ui-plugins'; @@ -31,19 +32,18 @@ buildConfig.compileFiles = [ const parsedTransform: Plugins = { name: 'state-complex-type', - parsed: uiTransform().parsed + parsed: uiTransform().parsed, }; const pluginTester = new PluginTester('test memo lambda', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; -import { memo as memo } from "@ohos.arkui.stateManagement"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { memo as memo } from "arkui.stateManagement.runtime"; function main() {} -function foo1(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string): void { +@memo() function foo1(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string): void { const __memo_scope = __memo_context.scope(((__memo_id) + (38567515)), 2); const __memo_parameter_this = __memo_scope.param(0, this), __memo_parameter_str = __memo_scope.param(1, str); if (__memo_scope.unchanged) { @@ -57,7 +57,7 @@ function foo1(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id } } -function foo2(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string): B { +@memo() function foo2(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string): B { const __memo_scope = __memo_context.scope(((__memo_id) + (167482260)), 2); const __memo_parameter_this = __memo_scope.param(0, this), __memo_parameter_str = __memo_scope.param(1, str); if (__memo_scope.unchanged) { @@ -68,7 +68,7 @@ function foo2(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id } class B { - public internal_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type): B { + @memo() public internal_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type): B { const __memo_scope = __memo_context.scope(((__memo_id) + (146437675)), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; @@ -87,7 +87,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform lambdas about function with receiver feature', - [parsedTransform, memoNoRecheck], + [parsedTransform, beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/trailing-lambdas.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/trailing-lambdas.test.ts index f023fef13..47a14d954 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/trailing-lambdas.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/trailing-lambdas.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const LAMBDA_DIR_PATH: string = 'memo/lambdas'; @@ -30,9 +31,35 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo lambda', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type, f?: (()=> void)): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_f = __memo_scope.param(0, f); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +} +function par(f?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} +@memo() function kar(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() f?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_f = __memo_scope.param(0, f); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +} +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -42,7 +69,7 @@ function main() {} a.foo(__memo_context, ((__memo_id) + ()), (() => { console.log(); })); - a.goo(((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + a.goo(((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -54,7 +81,7 @@ function main() {} return; } })); - a.koo(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + a.koo(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -69,7 +96,7 @@ function main() {} bar(__memo_context, ((__memo_id) + ()), (() => { console.log(); })); - par(((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + par(((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -81,7 +108,7 @@ function main() {} return; } })); - kar(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + kar(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -98,33 +125,8 @@ function main() {} return; } }); -@functions.OptionalParametersAnnotation({minArgCount:0}) function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type, f?: (()=> void)): void { - const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); - const __memo_parameter_f = __memo_scope.param(0, f); - if (__memo_scope.unchanged) { - __memo_scope.cached; - return; - } - { - __memo_scope.recache(); - return; - } -} -@functions.OptionalParametersAnnotation({minArgCount:0}) function par(f?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} -@functions.OptionalParametersAnnotation({minArgCount:0}) function kar(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() f?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { - const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); - const __memo_parameter_f = __memo_scope.param(0, f); - if (__memo_scope.unchanged) { - __memo_scope.cached; - return; - } - { - __memo_scope.recache(); - return; - } -} class A { - @functions.OptionalParametersAnnotation({minArgCount:0}) public foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, p?: (()=> void)): void { + @memo() public foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, p?: (()=> void)): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_p = __memo_scope.param(0, p); if (__memo_scope.unchanged) { @@ -136,8 +138,8 @@ class A { return; } } - @functions.OptionalParametersAnnotation({minArgCount:0}) public goo(@memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} - @functions.OptionalParametersAnnotation({minArgCount:0}) public koo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + public goo(@memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} + @memo() public koo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_p = __memo_scope.param(0, p); if (__memo_scope.unchanged) { @@ -159,7 +161,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform trailing lambdas', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/void-lambda.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/void-lambda.test.ts index 59b48a024..37b1c1365 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/void-lambda.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/void-lambda.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const LAMBDA_DIR_PATH: string = 'memo/lambdas'; @@ -30,7 +31,8 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo lambda', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); @@ -63,7 +65,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform lambdas with void return type', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/with-receiver.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/with-receiver.test.ts index 4f6e3fa92..1cb3c4d17 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/with-receiver.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/with-receiver.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const LAMBDA_DIR_PATH: string = 'memo/lambdas'; @@ -30,16 +31,58 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo lambda', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; + function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + +@memo() function fullName(this: Person, __memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_this = __memo_scope.param(0, this), __memo_parameter_arg = __memo_scope.param(1, arg); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +} + +@memo() function foo(this: A, __memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_this = __memo_scope.param(0, this), __memo_parameter_arg = __memo_scope.param(1, arg); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +} + +@memo() function goo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, a: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_a = __memo_scope.param(0, a), __memo_parameter_arg = __memo_scope.param(1, arg); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +} + +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } let x = new Person(); - fullName(x, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + fullName(x, __memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -51,12 +94,9 @@ function main() {} } })); let f1: F1 = foo; - f1 = goo; let f2: F2 = goo; - f2 = foo; - f1 = f2; let a = new A(); - f1(a, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + f1(a, __memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -67,7 +107,7 @@ function main() {} return; } })); - f1(a, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + f1(a, __memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -78,7 +118,7 @@ function main() {} return; } })); - f2(a, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + f2(__memo_context, ((__memo_id) + ()), a, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -94,19 +134,17 @@ function main() {} return; } }); -@functions.OptionalParametersAnnotation({minArgCount:1}) function fullName(this: Person, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { - return; -} -@functions.OptionalParametersAnnotation({minArgCount:1}) function foo(this: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} -@functions.OptionalParametersAnnotation({minArgCount:1}) function goo(a: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} + class Person { public constructor() {} } + class A { public constructor() {} } -type F1 = ((this: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void); -type F2 = ((a: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void); + +@memo() type F1 = ((this: A, __memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void); +@memo() type F2 = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, a: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void); `; function testMemoTransformer(this: PluginTestContext): void { @@ -115,7 +153,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform lambdas with receiver', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/argument-call.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/argument-call.test.ts index de72519e7..f56a4c7cf 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/argument-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/argument-call.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const METHOD_DIR_PATH: string = 'memo/methods'; @@ -28,10 +29,11 @@ buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, MET const pluginTester = new PluginTester('test memo method', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} class Test { - public lambda_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + @memo() public lambda_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -43,7 +45,7 @@ class Test { return; } } - public lambda_arg_with_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string)=> string)): void { + @memo() public lambda_arg_with_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string)=> string)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -55,7 +57,7 @@ class Test { return; } } - public memo_content(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() content: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + @memo() public memo_content(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() content: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_content = __memo_scope.param(0, content); if (__memo_scope.unchanged) { @@ -68,7 +70,7 @@ class Test { return; } } - public compute_test(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg1: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined, arg2: (()=> void) | undefined, content: (()=> void) | undefined): void { + @memo() public compute_test(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg1: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined), arg2: ((()=> void) | undefined), content: ((()=> void) | undefined)): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 3); const __memo_parameter_arg1 = __memo_scope.param(0, arg1), __memo_parameter_arg2 = __memo_scope.param(1, arg2), __memo_parameter_content = __memo_scope.param(2, content); if (__memo_scope.unchanged) { @@ -83,7 +85,7 @@ class Test { public constructor() {} } class Use { - public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -109,7 +111,7 @@ class Use { } return __memo_scope.recache(__memo_parameter_value.value); })); - test.compute_test(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + test.compute_test(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -135,7 +137,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform argument calls in methods', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/callable.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/callable.test.ts index 7657e1dd0..c611a8e99 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/callable.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/callable.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const METHOD_DIR_PATH: string = 'memo/methods'; @@ -28,16 +29,17 @@ buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, MET const pluginTester = new PluginTester('test memo method', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } A.$_invoke(__memo_context, ((__memo_id) + ())); - B.$_invoke(((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + B.$_invoke(((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -48,7 +50,7 @@ function main() {} return; } })); - let x: C | D = C.$_instantiate(__memo_context, ((__memo_id) + ()), (() => { + let x: (C | D) = C.$_instantiate(__memo_context, ((__memo_id) + ()), (() => { return new C(); })); x = D.$_instantiate((() => { @@ -60,7 +62,7 @@ function main() {} } }); class A { - public static $_invoke(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public static $_invoke(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -74,11 +76,11 @@ class A { public constructor() {} } class B { - @functions.OptionalParametersAnnotation({minArgCount:0}) public static $_invoke(@memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} + public static $_invoke(@memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} public constructor() {} } class C { - public static $_instantiate(__memo_context: __memo_context_type, __memo_id: __memo_id_type, factory: (()=> C)): C { + @memo() public static $_instantiate(__memo_context: __memo_context_type, __memo_id: __memo_id_type, factory: (()=> C)): C { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_factory = __memo_scope.param(0, factory); if (__memo_scope.unchanged) { @@ -89,7 +91,7 @@ class C { public constructor() {} } class D { - @functions.OptionalParametersAnnotation({minArgCount:1}) public static $_instantiate(factory: (()=> D), @memo() content?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): D { + public static $_instantiate(factory: (()=> D), @memo() content?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): D { return factory(); } public constructor() {} @@ -102,7 +104,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform callable class', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/declare-and-call.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/declare-and-call.test.ts index 0a669b4c0..d1e6012ea 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/declare-and-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/declare-and-call.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const METHOD_DIR_PATH: string = 'memo/methods'; @@ -30,9 +31,10 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo method', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -48,11 +50,11 @@ function main() {} }); declare abstract class A { @memo() public x(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void - public test_signature(@memo() arg1: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void), @memo() arg2: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined, @memo() arg3: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined | ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int) | undefined, @memo() x: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, y: ((z: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void))=> void)): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) + public test_signature(@memo() arg1: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void), @memo() arg2: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined), @memo() arg3: ((((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) | (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int) | undefined)), @memo() x: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, y: ((z: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void))=> void)): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) public constructor() {} } class AA extends A { - public x(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public x(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -73,7 +75,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform declare methods and calls', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/internal-calls.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/internal-calls.test.ts index 11dae0f7e..caf2b1b9f 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/internal-calls.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/internal-calls.test.ts @@ -14,27 +14,33 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig, mockProjectConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { ProjectConfig } from '../../../../common/plugin-context'; const METHOD_DIR_PATH: string = 'memo/methods'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, METHOD_DIR_PATH, 'internal-calls.ets')]; -const pluginTester = new PluginTester('test memo method', buildConfig); +const projectConfig: ProjectConfig = mockProjectConfig(); +projectConfig.frameworkMode = 'frameworkMode'; + +const pluginTester = new PluginTester('test memo method', buildConfig, projectConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; function main() {} -function __context(): __memo_context_type -function __id(): __memo_id_type +export function __context(): __memo_context_type +export function __id(): __memo_id_type type MemoType = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); class Test { - public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -45,7 +51,7 @@ class Test { return; } } - public internal_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public internal_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -57,21 +63,21 @@ class Test { return; } } - public method_with_internals(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public method_with_internals(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - __context(); - __id(); + __memo_context; + __memo_id; { __memo_scope.recache(); return; } } public memo_lambda() { - @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -83,7 +89,7 @@ class Test { } }); } - public memo_variables(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public memo_variables(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -118,7 +124,7 @@ class Test { return; } } - @functions.OptionalParametersAnnotation({minArgCount:0}) public args_with_default_values(__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_1?: int, gensym%%_2?: (()=> int), gensym%%_3?: int, arg4?: int): void { + @memo() public args_with_default_values(__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_1?: int, gensym%%_2?: (()=> int), gensym%%_3?: int, arg4?: int): void { let arg1: int = (((gensym%%_1) !== (undefined)) ? gensym%%_1 : (10 as int)); let arg2: (()=> int) = (((gensym%%_2) !== (undefined)) ? gensym%%_2 : ((() => { return 20; @@ -137,7 +143,7 @@ class Test { return; } } - @functions.OptionalParametersAnnotation({minArgCount:0}) public optional_args(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1?: int, arg2?: (()=> int)): void { + @memo() public optional_args(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1?: int, arg2?: (()=> int)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); const __memo_parameter_arg1 = __memo_scope.param(0, arg1), __memo_parameter_arg2 = __memo_scope.param(1, arg2); if (__memo_scope.unchanged) { @@ -153,7 +159,7 @@ class Test { return; } } - public type_alias(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: MemoType): void { + @memo() public type_alias(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: MemoType) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -176,7 +182,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform inner calls in methods', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/non-void-method.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/non-void-method.test.ts index a8ddef791..8957ca871 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/non-void-method.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/non-void-method.test.ts @@ -14,28 +14,35 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig, mockProjectConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { ProjectConfig } from '../../../../common/plugin-context'; const METHOD_DIR_PATH: string = 'memo/methods'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, METHOD_DIR_PATH, 'non-void-method.ets')]; -const pluginTester = new PluginTester('test memo method', buildConfig); +const projectConfig: ProjectConfig = mockProjectConfig(); +projectConfig.frameworkMode = 'frameworkMode'; + +const pluginTester = new PluginTester('test memo method', buildConfig, projectConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; function main() {} -function __context(): __memo_context_type -function __id(): __memo_id_type +export function __context(): __memo_context_type +export function __id(): __memo_id_type @Retention({policy:"SOURCE"}) @interface memo_intrinsic {} @Retention({policy:"SOURCE"}) @interface memo_entry {} +@Retention({policy:"SOURCE"}) @interface memo_skip {} class Test { - public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -46,7 +53,7 @@ class Test { return; } } - public string_method_with_return(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string): string { + @memo() public string_method_with_return(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string): string { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -54,7 +61,7 @@ class Test { } return __memo_scope.recache(__memo_parameter_arg.value); } - public method_with_type_parameter(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: T): T { + @memo() public method_with_type_parameter(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: T): T { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -62,19 +69,19 @@ class Test { } return __memo_scope.recache(__memo_parameter_arg.value); } - public intrinsic_method(): int { + @memo_intrinsic() public intrinsic_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): int { return 0; } - public intrinsic_method_with_this(): int { + @memo_intrinsic() public intrinsic_method_with_this(__memo_context: __memo_context_type, __memo_id: __memo_id_type): int { this.void_method(__memo_context, ((__memo_id) + ())); return 0; } - public memoEntry(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> R)): R { + @memo_entry() public memoEntry(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> R)): R { const getContext = (() => { - return __context(); + return __memo_context; }); const getId = (() => { - return __id(); + return __memo_id; }); { const __memo_context = getContext(); @@ -82,10 +89,20 @@ class Test { return entry(__memo_context, ((__memo_id) + ())); } } + @memo() public memo_skip_args(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, @memo_skip() arg2: string, @memo_skip() arg3: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): string { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_arg1 = __memo_scope.param(0, arg1); + if (__memo_scope.unchanged) { + return __memo_scope.cached; + } + let a = __memo_parameter_arg1.value; + arg3(__memo_context, ((__memo_id) + ())); + return __memo_scope.recache(arg2); + } public constructor() {} } class Use { - public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -109,7 +126,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform methods with non-void return type', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/void-method.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/void-method.test.ts index 953742b8a..41e73d5bb 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/void-method.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/void-method.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const METHOD_DIR_PATH: string = 'memo/methods'; @@ -30,7 +31,8 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo method', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} class A { public x: int; @@ -38,7 +40,7 @@ class A { public constructor() {} } class Test { - public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -49,7 +51,7 @@ class Test { return; } } - public a_method_with_implicit_return_type(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public a_method_with_implicit_return_type(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -60,7 +62,7 @@ class Test { return; } } - public void_method_with_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string): void { + @memo() public void_method_with_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -72,7 +74,7 @@ class Test { return; } } - public void_method_with_return(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string): void { + @memo() public void_method_with_return(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -84,7 +86,7 @@ class Test { return; } } - public static static_method_with_type_parameter(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: T): void { + @memo() public static static_method_with_type_parameter(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: T): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -96,7 +98,7 @@ class Test { return; } } - public obj_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: A): void { + @memo() public obj_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: A) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -111,7 +113,7 @@ class Test { public constructor() {} } class Use { - public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -141,7 +143,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform methods with void return type', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/property-declarations/class-constructor.test.ts b/arkui-plugins/test/ut/memo-plugins/property-declarations/class-constructor.test.ts index 28671e700..b3be707ba 100644 --- a/arkui-plugins/test/ut/memo-plugins/property-declarations/class-constructor.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/property-declarations/class-constructor.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const PROPERTY_DIR_PATH: string = 'memo/properties'; @@ -30,16 +31,17 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo property', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } let a = new AA({ - a: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + a: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -57,19 +59,19 @@ function main() {} } }); interface A { - @memo() set a(a: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void + @memo() set a(a: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) @memo() get a(): ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) } class AA { - @memo() public a: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined; + @memo() public a: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined); constructor() { this(undefined); } - public constructor(arg: A | undefined) { + public constructor(arg: (A | undefined)) { this.a = ({let gensym%%_ = arg; (((gensym%%_) == (null)) ? undefined : gensym%%_.a)}); } - public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -91,7 +93,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform properties in class constructor', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/property-declarations/class-properties.test.ts b/arkui-plugins/test/ut/memo-plugins/property-declarations/class-properties.test.ts index c98720039..1a97511af 100644 --- a/arkui-plugins/test/ut/memo-plugins/property-declarations/class-properties.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/property-declarations/class-properties.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const PROPERTY_DIR_PATH: string = 'memo/properties'; @@ -30,13 +31,14 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo property', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "@ohos.arkui.stateManagement"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} class A { public arg: (()=> void); @memo() public memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); - @memo() public memo_optional_arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined; - @memo() public memo_union_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + @memo() public memo_optional_arg?: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined); + @memo() public memo_union_arg: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -50,7 +52,7 @@ class A { public arg_memo_type: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); public constructor() { this.arg = (() => {}); - this.memo_arg = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + this.memo_arg = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -61,7 +63,7 @@ class A { return; } }); - this.arg_memo_type = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + this.arg_memo_type = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -73,7 +75,7 @@ class A { } }); } - public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -96,7 +98,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform properties in class', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/property-declarations/interfaces.test.ts b/arkui-plugins/test/ut/memo-plugins/property-declarations/interfaces.test.ts index 99053d0ef..a28664fde 100644 --- a/arkui-plugins/test/ut/memo-plugins/property-declarations/interfaces.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/property-declarations/interfaces.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const PROPERTY_DIR_PATH: string = 'memo/properties'; @@ -28,9 +29,10 @@ buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, PRO const pluginTester = new PluginTester('test memo property', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -38,7 +40,7 @@ function main() {} } let a: A = { arg: (() => {}), - memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -49,7 +51,7 @@ function main() {} return; } }), - memo_union_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + memo_union_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -60,7 +62,7 @@ function main() {} return; } }), - arg_memo_type: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + arg_memo_type: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -80,12 +82,12 @@ function main() {} interface A { set arg(arg: (()=> void)) get arg(): (()=> void) - @memo() set memo_arg(memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void + @memo() set memo_arg(memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) @memo() get memo_arg(): ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) - @memo() set memo_optional_arg(memo_optional_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined): void - @memo() get memo_optional_arg(): ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined - @memo() set memo_union_arg(memo_union_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined): void - @memo() get memo_union_arg(): ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined + @memo() set memo_optional_arg(memo_optional_arg: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)) + @memo() get memo_optional_arg(): (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) + @memo() set memo_union_arg(memo_union_arg: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)) + @memo() get memo_union_arg(): (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) set arg_memo_type(arg_memo_type: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) get arg_memo_type(): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) } @@ -97,7 +99,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform interface properties', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/ui-plugins/xcomponent/xcomponent-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/animation/animatable-extend-basic.test.ts similarity index 35% rename from arkui-plugins/test/ut/ui-plugins/xcomponent/xcomponent-basic.test.ts rename to arkui-plugins/test/ut/ui-plugins/animation/animatable-extend-basic.test.ts index fc5ee09c6..f921f5f4a 100644 --- a/arkui-plugins/test/ut/ui-plugins/xcomponent/xcomponent-basic.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/animation/animatable-extend-basic.test.ts @@ -1,3 +1,4 @@ + /* * Copyright (c) 2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,82 +15,81 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { uiNoRecheck } from '../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; import { uiTransform } from '../../../../ui-plugins'; import { Plugins } from '../../../../common/plugin-context'; -const XCOMPONENT_DIR_PATH: string = 'xcomponent'; +const ANIMATION_DIR_PATH: string = 'animation'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, XCOMPONENT_DIR_PATH, 'xcomponent-basic.ets'), + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, ANIMATION_DIR_PATH, 'animatable-extend-basic.ets'), ]; -const xcomponentTransform: Plugins = { - name: 'xcomponent', +const animatableExtendTransform: Plugins = { + name: 'animatable-extend', parsed: uiTransform().parsed, -} +}; -const pluginTester = new PluginTester('test basic XComponent transform', buildConfig); +const pluginTester = new PluginTester('test basic animatableExtend transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { UIXComponentAttribute as UIXComponentAttribute } from "@ohos.arkui.component"; +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { NavInterface as NavInterface } from "arkui.UserView"; -import { UIFlexAttribute as UIFlexAttribute } from "@ohos.arkui.component"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; -import { Component as Component, Flex as Flex, XComponent as XComponent, FlexDirection as FlexDirection, XComponentType as XComponentType, Entry as Entry, XComponentController as XComponentController, ItemAlign as ItemAlign, FlexAlign as FlexAlign, XComponentParameter as XComponentParameter } from "@ohos.arkui.component"; +import { Text as Text, Column as Column, Component as Component, Color as Color, Curve as Curve } from "@ohos.arkui.component"; -function main() {} +import { Entry as Entry } from "@ohos.arkui.component"; +function main() {} +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../animation/animatable-extend-basic", + pageFullPath: "test/demo/mock/animation/animatable-extend-basic", + integratedHsp: "false", + } as NavInterface)); -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class Index extends CustomComponent { - public __initializeStruct(initializers: __Options_Index | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_myXComponentController = ((({let gensym___221905990 = initializers; - (((gensym___221905990) == (null)) ? undefined : gensym___221905990.myXComponentController)})) ?? (new XComponentController())); - } +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct AnimatablePropertyExample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_AnimatablePropertyExample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_Index | undefined): void {} + public __updateStruct(initializers: (__Options_AnimatablePropertyExample | undefined)): void {} - private __backing_myXComponentController?: XComponentController; - - public get myXComponentController(): XComponentController { - return (this.__backing_myXComponentController as XComponentController); - } - - public set myXComponentController(value: XComponentController) { - this.__backing_myXComponentController = value; - } - - @memo() public _build(@memo() style: ((instance: Index)=> Index) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Index | undefined): void { - Flex(@memo() ((instance: UIFlexAttribute): void => { - instance.width("100%").height("100%"); - return; - }), { - direction: FlexDirection.Column, - alignItems: ItemAlign.Center, - justifyContent: FlexAlign.Start, - }, (() => { - XComponent(undefined, ({ - id: "xComponentId", - type: XComponentType.TEXTURE, - libraryname: "nativerender", - controller: this.myXComponentController, - } as XComponentParameter), "", undefined); + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { + instance.animationStart({ + duration: 2000, + curve: Curve.Ease, + }).backgroundColor(Color.Red).animationStop({ + duration: 2000, + curve: Curve.Ease, + }).animationStart({ + duration: 2000, + curve: Curve.Ease, + }).fontSize(20).animationStop({ + duration: 2000, + curve: Curve.Ease, + }).width("100%"); + return; + }), "AnimatableProperty", undefined, undefined); })); } @@ -97,17 +97,14 @@ function main() {} } -interface __Options_Index { - set myXComponentController(myXComponentController: XComponentController | undefined) - - get myXComponentController(): XComponentController | undefined +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_AnimatablePropertyExample { } class __EntryWrapper extends EntryPoint { @memo() public entry(): void { - Index._instantiateImpl(undefined, (() => { - return new Index(); + AnimatablePropertyExample._instantiateImpl(undefined, (() => { + return new AnimatablePropertyExample(); }), undefined, undefined, undefined); } @@ -116,17 +113,23 @@ class __EntryWrapper extends EntryPoint { } `; -function testXComponentTransformer(this: PluginTestContext): void { +const expectedHeader = + ` + __createOrSetAnimatableProperty(functionName: string, value: number | AnimatableArithmetic, callback: ((value: number | AnimatableArithmetic)=> void)): void + `; + +function testAnimatableExtendTransformer(this: PluginTestContext): void { expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); } pluginTester.run( - 'test basic XComponent transform', - [xcomponentTransform, uiNoRecheck], + 'test basic animation transform', + [animatableExtendTransform, uiNoRecheck, recheck], { - checked: [testXComponentTransformer], + checked: [testAnimatableExtendTransformer], }, { stopAfter: 'checked', + tracing: { externalSourceNames: ['arkui.component.common'] }, } ); diff --git a/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts index dd4678db6..51d4f1b76 100644 --- a/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { uiNoRecheck } from '../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; import { uiTransform } from '../../../../ui-plugins'; import { Plugins } from '../../../../common/plugin-context'; @@ -32,23 +33,23 @@ buildConfig.compileFiles = [ const animationTransform: Plugins = { name: 'animation', parsed: uiTransform().parsed, -} +}; const pluginTester = new PluginTester('test basic animation transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { NavInterface as NavInterface } from "arkui.UserView"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Text as Text, Column as Column, Component as Component, Color as Color, Curve as Curve } from "@ohos.arkui.component"; @@ -57,16 +58,22 @@ import { Entry as Entry } from "@ohos.arkui.component"; function main() {} +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../animation/animation-basic", + pageFullPath: "test/demo/mock/animation/animation-basic", + integratedHsp: "false", + } as NavInterface)); - -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class AnimatablePropertyExample extends CustomComponent { - public __initializeStruct(initializers: __Options_AnimatablePropertyExample | undefined, @memo() content: (()=> void) | undefined): void {} +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct AnimatablePropertyExample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_AnimatablePropertyExample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_AnimatablePropertyExample | undefined): void {} + public __updateStruct(initializers: (__Options_AnimatablePropertyExample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: AnimatablePropertyExample)=> AnimatablePropertyExample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_AnimatablePropertyExample | undefined): void { - Column(undefined, undefined, (() => { - Text(@memo() ((instance: UITextAttribute): void => { + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { instance.animationStart({ duration: 2000, curve: Curve.Ease, @@ -89,7 +96,7 @@ function main() {} } -interface __Options_AnimatablePropertyExample { +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_AnimatablePropertyExample { } @@ -105,17 +112,24 @@ class __EntryWrapper extends EntryPoint { } `; +const expectedHeader = + ` + animationStart(value: AnimateParam | undefined): this + animationStop(value: AnimateParam | undefined): this + `; + function testAnimationTransformer(this: PluginTestContext): void { expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); } pluginTester.run( 'test basic animation transform', - [animationTransform, uiNoRecheck], + [animationTransform, uiNoRecheck, recheck], { checked: [testAnimationTransformer], }, { stopAfter: 'checked', + tracing: { externalSourceNames: ['arkui.component.common'] }, } ); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/block-in-switch-case.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/block-in-switch-case.test.ts new file mode 100644 index 000000000..b38eeb4a6 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/block-in-switch-case.test.ts @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'block-in-switch-case.ets'), +]; + +const pluginTester = new PluginTester('test block statement in switch case', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ColumnAttribute as ColumnAttribute } from "arkui.component.column"; +import { ConditionScope as ConditionScope } from "arkui.component.builder"; +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { TextAttribute as TextAttribute } from "arkui.component.text"; +import { TextImpl as TextImpl } from "arkui.component.text"; +import { ColumnImpl as ColumnImpl } from "arkui.component.column"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Text as Text, Column as Column, Component as Component } from "@ohos.arkui.component"; + +function main() {} + +@Component() final struct SwitchCase extends CustomComponent { + public __initializeStruct(initializers: (__Options_SwitchCase | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = ((({let gensym___83257243 = initializers; + (((gensym___83257243) == (null)) ? undefined : gensym___83257243.num)})) ?? (2)); + } + + public __updateStruct(initializers: (__Options_SwitchCase | undefined)): void {} + + private __backing_num?: int; + + public get num(): int { + return (this.__backing_num as int); + } + + public set num(value: int) { + this.__backing_num = value; + } + + @memo() public build() { + ColumnImpl(@memo() ((instance: ColumnAttribute): void => { + instance.setColumnOptions(undefined).applyAttributesFinish(); + return; + }), @memo() (() => { + ConditionScope(@memo() (() => { + switch (this.num) { + case 0: { + break; + } + case 1: { + ConditionBranch(@memo() (() => { + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("111", undefined).applyAttributesFinish(); + return; + }), undefined); + })); + } + case 2: { + ConditionBranch(@memo() (() => { + { + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("111", undefined).applyAttributesFinish(); + return; + }), undefined); + } + })); + } + case 3: { + ConditionBranch(@memo() (() => { + { + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("111", undefined).applyAttributesFinish(); + return; + }), undefined); + } + })); + break; + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("111", undefined).applyAttributesFinish(); + return; + }), undefined); + } + case 4: { + ConditionBranch(@memo() (() => { + { + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("111", undefined).applyAttributesFinish(); + return; + }), undefined); + return; + } + })); + break; + } + case 5: { + ConditionBranch(@memo() (() => { + { + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("111", undefined).applyAttributesFinish(); + return; + }), undefined); + } + })); + break; + } + case 6: { + ConditionBranch(@memo() (() => { + { + return; + } + })); + break; + } + case 7: { + ConditionBranch(@memo() (() => { + { + return; + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("111", undefined).applyAttributesFinish(); + return; + }), undefined); + } + })); + break; + } + default: { + break; + } + } + })); + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("hello world", undefined).applyAttributesFinish(); + return; + }), undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_SwitchCase { + set num(num: (int | undefined)) + + get num(): (int | undefined) + +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { ColumnAttribute as ColumnAttribute } from "arkui.component.column"; +import { ConditionScope as ConditionScope } from "arkui.component.builder"; +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { TextAttribute as TextAttribute } from "arkui.component.text"; +import { TextImpl as TextImpl } from "arkui.component.text"; +import { ColumnImpl as ColumnImpl } from "arkui.component.column"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Text as Text, Column as Column, Component as Component } from "@ohos.arkui.component"; + +function main() {} + +@Component() final struct SwitchCase extends CustomComponent { + public __initializeStruct(initializers: (__Options_SwitchCase | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_num = ((({let gensym___83257243 = initializers; + (((gensym___83257243) == (null)) ? undefined : gensym___83257243.num)})) ?? (2)); + } + + public __updateStruct(initializers: (__Options_SwitchCase | undefined)): void {} + + private __backing_num?: int; + + public get num(): int { + return (this.__backing_num as int); + } + + public set num(value: int) { + this.__backing_num = value; + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (261239291)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ColumnImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: ColumnAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setColumnOptions(undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (147868395)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + (186113)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (186336799)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + switch (this.num) { + case 0: { + break; + } + case 1: { + ConditionBranch(__memo_context, ((__memo_id) + (27357263)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (78642435)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("111", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + { + __memo_scope.recache(); + return; + } + })); + } + case 2: { + ConditionBranch(__memo_context, ((__memo_id) + (220977109)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (91459184)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("111", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + } + { + __memo_scope.recache(); + return; + } + })); + } + case 3: { + ConditionBranch(__memo_context, ((__memo_id) + (143235624)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (214575380)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("111", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + } + { + __memo_scope.recache(); + return; + } + })); + break; + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("111", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + } + case 4: { + ConditionBranch(__memo_context, ((__memo_id) + (7513933)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (235688754)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("111", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + return; + } + })); + break; + } + case 5: { + ConditionBranch(__memo_context, ((__memo_id) + (58475451)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (257250368)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("111", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + } + { + __memo_scope.recache(); + return; + } + })); + break; + } + case 6: { + ConditionBranch(__memo_context, ((__memo_id) + (60292460)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (21099142)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + return; + } + })); + break; + } + case 7: { + ConditionBranch(__memo_context, ((__memo_id) + (34940192)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (15961624)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + return; + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("111", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + } + { + __memo_scope.recache(); + return; + } + })); + break; + } + default: { + break; + } + } + { + __memo_scope.recache(); + return; + } + })); + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("hello world", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} + +@Component() export interface __Options_SwitchCase { + set num(num: (int | undefined)) + + get num(): (int | undefined) + +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test block statement in switch case', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/else-if-in-content.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/else-if-in-content.test.ts new file mode 100644 index 000000000..3465c1c59 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/else-if-in-content.test.ts @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'else-if-in-content.ets'), +]; + +const pluginTester = new PluginTester('test else-if conditions in builder lambda call', buildConfig); + +const parsedTransform: Plugins = { + name: 'else-if', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct ElseIf extends CustomComponent { + public __initializeStruct(initializers: (__Options_ElseIf | undefined), @memo() content: ((()=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_ElseIf | undefined)): void {} + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => {})); + } else { + if (false) { + ConditionBranch(@memo() (() => { + Text(undefined, \"elseIf 1\", undefined, undefined); + })); + } else { + ConditionBranch(@memo() (() => { + Text(undefined, \"else 1\", undefined, undefined); + })); + } + } + })); + ConditionScope(@memo() (() => { + if (false) { + ConditionBranch(@memo() (() => {})); + } else { + ConditionBranch(@memo() (() => { + ConditionScope(@memo() (() => { + if (false) { + ConditionBranch(@memo() (() => { + Text(undefined, \"elseIf 2\", undefined, undefined); + })); + } else { + ConditionBranch(@memo() (() => { + Text(undefined, \"else 2\", undefined, undefined); + })); + } + })); + })); + } + })); + })); + } + public constructor() {} +} +@Component() export interface __Options_ElseIf { +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct ElseIf extends CustomComponent { + public __initializeStruct(initializers: (__Options_ElseIf | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_ElseIf | undefined)): void {} + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + })); + } else { + if (false) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"elseIf 1\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } else { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"else 1\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + } + { + __memo_scope.recache(); + return; + } + })); + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (false) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + })); + } else { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (false) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"elseIf 2\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } else { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"else 2\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() export interface __Options_ElseIf { +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test else-if condition branch', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-break-in-nested-content.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-break-in-nested-content.test.ts new file mode 100644 index 000000000..d84aeb785 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-break-in-nested-content.test.ts @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'if-break-in-nested-content.ets'), +]; + +const pluginTester = new PluginTester('test if condition branch break statements in nested builder lambda call\'s trailing lambda', buildConfig); + +const parsedTransform: Plugins = { + name: 'if-else', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Component as Component, Column as Column, Text as Text } from \"@ohos.arkui.component\"; +import hilog from \"@ohos.hilog\"; +function main() {} +@Component() final struct A extends CustomComponent { + public __initializeStruct(initializers: (__Options_A | undefined), @memo() content: ((()=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_A | undefined)): void {} + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + if (false) { + ConditionBranch(@memo() (() => { + hilog.info(0x0000, \"very inside\", \"1\"); + })); + return; + Text(undefined, \"1\", undefined, undefined); + } + })); + })); + })); + })); + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + hilog.info(0x0000, \"outside column\", \"2\"); + Text(undefined, \"1\", undefined, undefined); + })); + } + })); + })); + } + public constructor() {} +} +@Component() export interface __Options_A { +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Component as Component, Column as Column, Text as Text } from \"@ohos.arkui.component\"; +import hilog from \"@ohos.hilog\"; +function main() {} +@Component() final struct A extends CustomComponent { + public __initializeStruct(initializers: (__Options_A | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_A | undefined)): void {} + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (false) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + hilog.info(0x0000, \"very inside\", \"1\"); + { + __memo_scope.recache(); + return; + } + })); + return; + Text(__memo_context, ((__memo_id) + ()), undefined, \"1\", undefined, undefined); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + hilog.info(0x0000, \"outside column\", \"2\"); + Text(__memo_context, ((__memo_id) + ()), undefined, \"1\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() export interface __Options_A { +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test if condition branch break statements', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-else-in-content.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-else-in-content.test.ts new file mode 100644 index 000000000..98209c11a --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-else-in-content.test.ts @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'if-else-in-content.ets'), +]; + +const pluginTester = new PluginTester('test if-else conditions in builder lambda call', buildConfig); + +const parsedTransform: Plugins = { + name: 'if-else', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct IfElse extends CustomComponent { + public __initializeStruct(initializers: (__Options_IfElse | undefined), @memo() content: ((()=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_IfElse | undefined)): void {} + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + ConditionScope(@memo() (() => { + if (false) { + ConditionBranch(@memo() (() => { + Text(undefined, \"if-if\", undefined, undefined); + })); + } else { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"if-elseIf\", undefined, undefined); + })); + } else { + ConditionBranch(@memo() (() => { + Text(undefined, \"if-else\", undefined, undefined); + })); + } + } + })); + })); + } else { + if (false) { + ConditionBranch(@memo() (() => { + Text(undefined, \"elseIf\", undefined, undefined); + })); + } else { + ConditionBranch(@memo() (() => { + Text(undefined, \"else\", undefined, undefined); + })); + return; + Text(undefined, \"after-return\", undefined, undefined); + } + } + })); + Text(undefined, \"hello world\", undefined, undefined); + })); + } + public constructor() {} +} +@Component() export interface __Options_IfElse { +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct IfElse extends CustomComponent { + public __initializeStruct(initializers: (__Options_IfElse | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_IfElse | undefined)): void {} + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (false) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"if-if\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } else { + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"if-elseIf\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } else { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"if-else\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + } else { + if (false) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"elseIf\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } else { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"else\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + return; + Text(__memo_context, ((__memo_id) + ()), undefined, \"after-return\", undefined, undefined); + } + } + { + __memo_scope.recache(); + return; + } + })); + Text(__memo_context, ((__memo_id) + ()), undefined, \"hello world\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() export interface __Options_IfElse { +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test if-else', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-in-switch-in-content.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-in-switch-in-content.test.ts new file mode 100644 index 000000000..66e215a5b --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-in-switch-in-content.test.ts @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'if-in-switch-in-content.ets'), +]; + +const pluginTester = new PluginTester('test if conditions in switch-case in builder lambda call', buildConfig); + +const parsedTransform: Plugins = { + name: 'if-in-switch', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct IfInSwitch extends CustomComponent { + public __initializeStruct(initializers: (__Options_IfInSwitch | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.num)})) ?? (\"1\")); + } + public __updateStruct(initializers: (__Options_IfInSwitch | undefined)): void {} + private __backing_num?: string; + public get num(): string { + return (this.__backing_num as string); + } + public set num(value: string) { + this.__backing_num = value; + } + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + switch (this.num) { + case \"-1\": { + ConditionBranch(@memo() (() => { + { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 1\", undefined, undefined); + })); + } else { + if (false) { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 2\", undefined, undefined); + })); + } else { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 3\", undefined, undefined); + })); + } + } + })); + } + })); + } + case \"2\": { + ConditionBranch(@memo() (() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 4\", undefined, undefined); + })); + } + })); + })); + } + } + })); + })); + } + public constructor() {} +} +@Component() export interface __Options_IfInSwitch { + set num(num: (string | undefined)) + get num(): (string | undefined) +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct IfInSwitch extends CustomComponent { + public __initializeStruct(initializers: (__Options_IfInSwitch | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_num = ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.num)})) ?? (\"1\")); + } + public __updateStruct(initializers: (__Options_IfInSwitch | undefined)): void {} + private __backing_num?: string; + public get num(): string { + return (this.__backing_num as string); + } + public set num(value: string) { + this.__backing_num = value; + } + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + switch (this.num) { + case \"-1\": { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 1\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } else { + if (false) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 2\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } else { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 3\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + } + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + } + case \"2\": { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 4\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + } + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() export interface __Options_IfInSwitch { + set num(num: (string | undefined)) + get num(): (string | undefined) +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test if conditions in switch-case', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/non-builder-within-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/non-builder-within-builder.test.ts new file mode 100644 index 000000000..7bfebe64e --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/non-builder-within-builder.test.ts @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'non-builder-within-builder.ets'), +]; + +const pluginTester = new PluginTester( + 'test no conditionScope in non-@Builder function within @Builder function', + buildConfig +); + +const parsedTransform: Plugins = { + name: 'with-no-condition', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Component as Component, Builder as Builder } from \"@ohos.arkui.component\"; +function main() {} +@memo() function TestComponent(init: TestInitCallback, update: TestUpdateCallback): void {} +type TestInitCallback = (()=> void); +type TestUpdateCallback = (()=> void); +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + @memo() public build() { + TestComponent((() => { + if (true) { + } + }), (() => { + if (false) { + } + })); + } + public constructor() {} +} +@Component() export interface __Options_MyStateSample { +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +pluginTester.run( + 'test no conditionScope in non-@Builder function', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/switch-case-in-content.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/switch-case-in-content.test.ts new file mode 100644 index 000000000..5f4ac9e90 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/switch-case-in-content.test.ts @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'switch-case-in-content.ets'), +]; + +const pluginTester = new PluginTester('test switch-case conditions in builder lambda call', buildConfig); + +const parsedTransform: Plugins = { + name: 'switch-case', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct SwitchCase extends CustomComponent { + public __initializeStruct(initializers: (__Options_SwitchCase | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.num)})) ?? (\"1\")); + } + public __updateStruct(initializers: (__Options_SwitchCase | undefined)): void {} + private __backing_num?: string; + public get num(): string { + return (this.__backing_num as string); + } + public set num(value: string) { + this.__backing_num = value; + } + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + switch (this.num) { + case \"0\": { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 0\", undefined, undefined); + })); + } + case \"1\": { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 1\", undefined, undefined); + })); + break; + Text(undefined, \"after break\", undefined, undefined); + } + case \"2\": { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 2\", undefined, undefined); + })); + return; + Text(undefined, \"after return\", undefined, undefined); + } + default: { + ConditionBranch(@memo() (() => { + Text(undefined, \"default\", undefined, undefined); + })); + } + } + })); + Text(undefined, \"hello world\", undefined, undefined); + })); + } + public constructor() {} +} +@Component() export interface __Options_SwitchCase { + set num(num: (string | undefined)) + get num(): (string | undefined) +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct SwitchCase extends CustomComponent { + public __initializeStruct(initializers: (__Options_SwitchCase | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_num = ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.num)})) ?? (\"1\")); + } + public __updateStruct(initializers: (__Options_SwitchCase | undefined)): void {} + private __backing_num?: string; + public get num(): string { + return (this.__backing_num as string); + } + public set num(value: string) { + this.__backing_num = value; + } + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + switch (this.num) { + case \"0\": { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 0\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + case \"1\": { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 1\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + break; + Text(__memo_context, ((__memo_id) + ()), undefined, \"after break\", undefined, undefined); + } + case \"2\": { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 2\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + return; + Text(__memo_context, ((__memo_id) + ()), undefined, \"after return\", undefined, undefined); + } + default: { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"default\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + } + { + __memo_scope.recache(); + return; + } + })); + Text(__memo_context, ((__memo_id) + ()), undefined, \"hello world\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() export interface __Options_SwitchCase { + set num(num: (string | undefined)) + get num(): (string | undefined) +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test switch-case', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/switch-in-if-in-content.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/switch-in-if-in-content.test.ts new file mode 100644 index 000000000..588d255e3 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/switch-in-if-in-content.test.ts @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'switch-in-if-in-content.ets'), +]; + +const pluginTester = new PluginTester('test switch-statement in if conditions in builder lambda call', buildConfig); + +const parsedTransform: Plugins = { + name: 'switch-case', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct SwitchInIf extends CustomComponent { + public __initializeStruct(initializers: (__Options_SwitchInIf | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.num)})) ?? (\"1\")); + } + public __updateStruct(initializers: (__Options_SwitchInIf | undefined)): void {} + private __backing_num?: string; + public get num(): string { + return (this.__backing_num as string); + } + public set num(value: string) { + this.__backing_num = value; + } + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + ConditionScope(@memo() (() => { + switch (this.num) { + case \"0\": { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 0\", undefined, undefined); + })); + } + } + })); + })); + } + })); + })); + } + public constructor() {} +} +@Component() export interface __Options_SwitchInIf { + set num(num: (string | undefined)) + get num(): (string | undefined) +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct SwitchInIf extends CustomComponent { + public __initializeStruct(initializers: (__Options_SwitchInIf | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_num = ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.num)})) ?? (\"1\")); + } + public __updateStruct(initializers: (__Options_SwitchInIf | undefined)): void {} + private __backing_num?: string; + public get num(): string { + return (this.__backing_num as string); + } + public set num(value: string) { + this.__backing_num = value; + } + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + switch (this.num) { + case \"0\": { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 0\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() export interface __Options_SwitchInIf { + set num(num: (string | undefined)) + get num(): (string | undefined) +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test switch-statement in if', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/with-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/with-builder.test.ts new file mode 100644 index 000000000..570c82a4b --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/with-builder.test.ts @@ -0,0 +1,498 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'with-builder.ets'), +]; + +const pluginTester = new PluginTester('test conditionScope within @Builder or @BuilderParam', buildConfig); + +const parsedTransform: Plugins = { + name: 'with-builder', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component, Builder as Builder, BuilderParam as BuilderParam, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder } from \"@ohos.arkui.component\"; +const wBuilder = wrapBuilder(ParamBuilder); +function main() {} +@memo() function MyBuilder(): void { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"within Builder function\", undefined, undefined); + })); + } + })); +} +@memo() function ParamBuilder(@Builder() @memo() gensym%%_?: (()=> void)): void { + let param: (()=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : ((() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"within Builder parameter\", undefined, undefined); + })); + } + })); + }) as (()=> void))); + param(); +} +wBuilder = wrapBuilder(ParamBuilder); +@Component() final struct MyStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStruct | undefined), @memo() content: ((()=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_MyStruct | undefined)): void {} + @memo() public myBuilderMethod() { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"within Builder method\", undefined, undefined); + })); + } + })); + } + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + wBuilder.builder(@Builder() (() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"with Builder lambda\", undefined, undefined); + })); + } + })); + })); + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + myBuilderParam: @memo() (() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"within Builder property\", undefined, undefined); + })); + } + })); + this.myBuilderMethod(); + }), + }, undefined, undefined); + })); + } + public constructor() {} +} +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_myBuilderParam = ((((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.myBuilderParam)})) ?? (content))) ?? ((() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"within BuilderParam property\", undefined, undefined); + })); + } + })); + }))) + } + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + private __backing_myBuilderParam?: @memo() (()=> void); + public get myBuilderParam(): @memo() (()=> void) { + return this.__backing_myBuilderParam!; + } + public set myBuilderParam(value: @memo() (()=> void)) { + this.__backing_myBuilderParam = value; + } + @memo() public build() { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, "within struct build", undefined, undefined); + })); + } + })); + } + public constructor() {} +} +@Component() export interface __Options_MyStruct { +} +@Component() export interface __Options_Child { + set myBuilderParam(myBuilderParam: (@memo() (()=> void) | undefined)) + get myBuilderParam(): (@memo() (()=> void) | undefined) +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component, Builder as Builder, BuilderParam as BuilderParam, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder } from \"@ohos.arkui.component\"; +const wBuilder = wrapBuilder(ParamBuilder); +function main() {} +@memo() function MyBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"within Builder function\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } +} +@memo() function ParamBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @Builder() @memo() gensym%%_?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + let param: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"within Builder parameter\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + }) as ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))); + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_param = __memo_scope.param(0, param); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_param.value(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } +} +wBuilder = wrapBuilder(ParamBuilder); +@Component() final struct MyStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStruct | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_MyStruct | undefined)): void {} + @memo() public myBuilderMethod(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"within Builder method\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + wBuilder.builder(__memo_context, ((__memo_id) + ()), @Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"with Builder lambda\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + Child._instantiateImpl(__memo_context, ((__memo_id) + ()), undefined, (() => { + return new Child(); + }), { + myBuilderParam: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"within Builder property\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + this.myBuilderMethod(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } + }), + }, undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_myBuilderParam = ((((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.myBuilderParam)})) ?? (content))) ?? (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"within BuilderParam property\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + }))) + } + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + private __backing_myBuilderParam?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); + public get myBuilderParam(): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) { + return this.__backing_myBuilderParam!; + } + public set myBuilderParam(value: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { + this.__backing_myBuilderParam = value; + } + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, "within struct build", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() export interface __Options_MyStruct { +} +@Component() export interface __Options_Child { + set myBuilderParam(myBuilderParam: (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)) + get myBuilderParam(): (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test conditionScope within @Builder or @BuilderParam', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/custom-component/custom-component-call.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/custom-component/custom-component-call.test.ts index b9a0d5943..3215144c7 100644 --- a/arkui-plugins/test/ut/ui-plugins/builder-lambda/custom-component/custom-component-call.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/custom-component/custom-component-call.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -27,55 +28,67 @@ const CUSTOM_COMPONENT_DIR_PATH: string = 'custom-component'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, CUSTOM_COMPONENT_DIR_PATH, 'custom-component-call.ets'), + path.resolve( + getRootPath(), + MOCK_ENTRY_DIR_PATH, + BUILDER_LAMBDA_DIR_PATH, + CUSTOM_COMPONENT_DIR_PATH, + 'custom-component-call.ets' + ), ]; const pluginTester = new PluginTester('test custom component call transformation', buildConfig); const parsedTransform: Plugins = { name: 'custom-component-call', - parsed: uiTransform().parsed + parsed: uiTransform().parsed, }; const expectedParsedScript: string = ` -import { __memo_id_type as __memo_id_type } from "@ohos.arkui.stateManagement"; -import { __memo_context_type as __memo_context_type } from "@ohos.arkui.stateManagement"; -import { memo as memo } from "@ohos.arkui.stateManagement"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { CustomComponent as CustomComponent } from "@ohos.arkui.component"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Text as Text, Column as Column, Component as Component, Builder as Builder, BuilderParam as BuilderParam } from "@ohos.arkui.component"; -@Component() final class CustomContainer extends CustomComponent { +@Component() final struct CustomContainer extends CustomComponent { @Builder() public closerBuilder() {} - @BuilderParam() public closer: (()=> void) = (this).closerBuilder; + + @BuilderParam() public closer: (()=> void) = this.closerBuilder; + public build() {} + public constructor() {} + } -@Component() final class CustomContainerUser extends CustomComponent { +@Component() final struct CustomContainerUser extends CustomComponent { public build() { - Column(undefined){ - CustomContainer(undefined){ - Column(undefined){ - Text("hello", undefined); + Column(){ + CustomContainer(){ + Column(){ + Text("hello"); }; }; - CustomContainer(({} as __Options_CustomContainer)){ - Column(undefined){}; + CustomContainer({}){ + Column(){}; }; CustomContainer(undefined){}; CustomContainer(); }; } + public constructor() {} + } -interface __Options_CustomContainer { - closer?: @memo() (()=> void); +@Component() export interface __Options_CustomContainer { + @BuilderParam() closer?: (()=> void); + } -interface __Options_CustomContainerUser { +@Component() export interface __Options_CustomContainerUser { + } `; @@ -84,68 +97,85 @@ function testParedTransformer(this: PluginTestContext): void { } const expectedBuilderLambdaScript: string = ` -import { __memo_id_type as __memo_id_type } from "@ohos.arkui.stateManagement"; -import { __memo_context_type as __memo_context_type } from "@ohos.arkui.stateManagement"; -import { memo as memo } from "@ohos.arkui.stateManagement"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { CustomComponent as CustomComponent } from "@ohos.arkui.component"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Text as Text, Column as Column, Component as Component, Builder as Builder, BuilderParam as BuilderParam } from "@ohos.arkui.component"; function main() {} -@Component({freezeWhenInactive:false}) final class CustomContainer extends CustomComponent { - public __initializeStruct(initializers: __Options_CustomContainer | undefined, @memo() content: (()=> void) | undefined): void { - (this).__backing_closer = ((((({let gensym___38813563 = initializers; - (((gensym___38813563) == (null)) ? undefined : gensym___38813563.closer)})) ?? (content))) ?? ((this).closerBuilder)) + + +@Component() final struct CustomContainer extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomContainer | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_closer = ((((({let gensym___38813563 = initializers; + (((gensym___38813563) == (null)) ? undefined : gensym___38813563.closer)})) ?? (content))) ?? (this.closerBuilder)) } - public __updateStruct(initializers: __Options_CustomContainer | undefined): void {} + + public __updateStruct(initializers: (__Options_CustomContainer | undefined)): void {} + private __backing_closer?: @memo() (()=> void); + public get closer(): @memo() (()=> void) { - return (this).__backing_closer!; + return this.__backing_closer!; } - public set closer(@memo() value: (()=> void)) { - (this).__backing_closer = value; + + public set closer(value: @memo() (()=> void)) { + this.__backing_closer = value; } + @memo() public closerBuilder() {} - @memo() public _build(@memo() style: ((instance: CustomContainer)=> CustomContainer) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_CustomContainer | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -@Component({freezeWhenInactive:false}) final class CustomContainerUser extends CustomComponent { - public __initializeStruct(initializers: __Options_CustomContainerUser | undefined, @memo() content: (()=> void) | undefined): void {} - public __updateStruct(initializers: __Options_CustomContainerUser | undefined): void {} - @memo() public _build(@memo() style: ((instance: CustomContainerUser)=> CustomContainerUser) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_CustomContainerUser | undefined): void { - Column(undefined, undefined, (() => { +@Component() final struct CustomContainerUser extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomContainerUser | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_CustomContainerUser | undefined)): void {} + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { CustomContainer._instantiateImpl(undefined, (() => { return new CustomContainer(); - }), undefined, (() => { - Column(undefined, undefined, (() => { + }), undefined, undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => { Text(undefined, "hello", undefined, undefined); })); - }), undefined); + })); CustomContainer._instantiateImpl(undefined, (() => { return new CustomContainer(); - }), ({} as __Options_CustomContainer), (() => { - Column(undefined, undefined, (() => {})); - }), undefined); + }), {}, undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => {})); + })); CustomContainer._instantiateImpl(undefined, (() => { return new CustomContainer(); - }), undefined, (() => {}), undefined); + }), undefined, undefined, @memo() (() => {})); CustomContainer._instantiateImpl(undefined, (() => { return new CustomContainer(); }), undefined, undefined, undefined); })); } + public constructor() {} + } -interface __Options_CustomContainer { - set closer(closer: @memo() (()=> void) | undefined) - get closer(): @memo() (()=> void) | undefined +@Component() export interface __Options_CustomContainer { + set closer(closer: (@memo() (()=> void) | undefined)) + + get closer(): (@memo() (()=> void) | undefined) + } -interface __Options_CustomContainerUser { +@Component() export interface __Options_CustomContainerUser { + } `; @@ -155,10 +185,10 @@ function testCustomComponentTransformer(this: PluginTestContext): void { pluginTester.run( 'test custom component call transformation', - [parsedTransform, recheck, uiNoRecheck], + [parsedTransform, recheck, uiNoRecheck, recheck], { parsed: [testParedTransformer], - 'checked:builder-lambda-no-recheck': [testCustomComponentTransformer], + 'checked:ui-no-recheck': [testCustomComponentTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/simple-component.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/simple-component.test.ts index 37b4605ad..e8e1ace16 100644 --- a/arkui-plugins/test/ut/ui-plugins/builder-lambda/simple-component.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/simple-component.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { builderLambdaNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, builderLambdaNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda'; @@ -31,12 +32,13 @@ const pluginTester = new PluginTester('test builder-lambda simple component', bu function testBuilderLambdaTransformer(this: PluginTestContext): void { const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; -import { Column as Column, UIColumnAttribute as UIColumnAttribute } from \"arkui.component.column\"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { memo as memo } from \"@ohos.arkui.stateManagement\"; +import { Column as Column, ColumnAttribute as ColumnAttribute } from \"arkui.component.column\"; function main() {} class MyStateSample { @memo() public build() { - Column(undefined, undefined, (() => {})); + Column(undefined, undefined, @memo() (() => {})); } public constructor() {} } @@ -46,17 +48,19 @@ class MyStateSample { function testMemoTransformer(this: PluginTestContext): void { const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; -import { Column as Column, UIColumnAttribute as UIColumnAttribute } from \"arkui.component.column\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { memo as memo } from \"@ohos.arkui.stateManagement\"; +import { Column as Column, ColumnAttribute as ColumnAttribute } from \"arkui.component.column\"; function main() {} class MyStateSample { - public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + (263357132)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - Column(__memo_context, ((__memo_id) + (65509320)), undefined, undefined, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + Column(__memo_context, ((__memo_id) + (65509320)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + (147296800)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -80,7 +84,7 @@ class MyStateSample { pluginTester.run( 'transform simple component', - [builderLambdaNoRecheck, recheck, memoNoRecheck], + [builderLambdaNoRecheck, beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:builder-lambda-no-recheck': [testBuilderLambdaTransformer], 'checked:memo-no-recheck': [testMemoTransformer], diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/style-with-receiver.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/style-with-receiver.test.ts index 1c64f1cdb..e55e43771 100644 --- a/arkui-plugins/test/ut/ui-plugins/builder-lambda/style-with-receiver.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/style-with-receiver.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { builderLambdaNoRecheck, structNoRecheck, uiNoRecheck } from '../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; import { uiTransform } from '../../../../ui-plugins'; import { Plugins } from '../../../../common/plugin-context'; @@ -33,44 +34,51 @@ const pluginTester = new PluginTester('test function with receiver style transfo const parsedTransform: Plugins = { name: 'style-with-receiver', - parsed: uiTransform().parsed + parsed: uiTransform().parsed, }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { memo as memo } from "@ohos.arkui.stateManagement"; -import { Text as Text, UITextAttribute as UITextAttribute, Column as Column, Component as Component } from "@ohos.arkui.component"; + +import { Text as Text, TextAttribute as TextAttribute, Column as Column, Component as Component } from "@ohos.arkui.component"; + import hilog from "@ohos.hilog"; function main() {} -@memo() function cardStyle(this: UITextAttribute, num: number, str: string): UITextAttribute { + +@memo() function cardStyle(this: TextAttribute, num: number, str: string): TextAttribute { this.fontSize(num); this.backgroundColor(num); return this; } -@memo() function style22(this: UITextAttribute): UITextAttribute { +@memo() function style22(this: TextAttribute): TextAttribute { this.fontWeight(700); return this; } -@Component({freezeWhenInactive:false}) final class MM extends CustomComponent { - public __initializeStruct(initializers: __Options_MM | undefined, @memo() content: (()=> void) | undefined): void {} + +@Component() final struct MM extends CustomComponent { + public __initializeStruct(initializers: (__Options_MM | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MM | undefined): void {} + public __updateStruct(initializers: (__Options_MM | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MM)=> MM) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MM | undefined): void { - Column(undefined, undefined, (() => { - Text(@memo() ((instance: UITextAttribute): void => { + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { style22(cardStyle(instance.height(200).fontColor("#000000"), 600, "#eeeeee").fontSize(60).fontWeight(400)).width(900); return; }), "hello world", undefined, undefined); - Text(@memo() ((instance: UITextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { cardStyle(instance, 600, "#eeeeee"); return; }), "hello world", undefined, undefined); @@ -81,7 +89,7 @@ function main() {} } -interface __Options_MM { +@Component() export interface __Options_MM { } `; @@ -92,9 +100,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test function with receiver style transformstion', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/component/declare-component.test.ts b/arkui-plugins/test/ut/ui-plugins/component/declare-component.test.ts new file mode 100644 index 000000000..6261ad5d5 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/component/declare-component.test.ts @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const COMPONENT_DIR_PATH: string = 'component'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, COMPONENT_DIR_PATH, 'declare-component.ets'), +]; + +const pluginTester = new PluginTester('test declare component transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'declare-component', + parsed: uiTransform().parsed +}; + +const expectedParsedcript: string = ` +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, ResourceStr as ResourceStr, Builder as Builder } from "@ohos.arkui.component"; + +import { Prop as Prop, State as State } from "@ohos.arkui.stateManagement"; + +@Component() export declare final struct SwipeRefresher extends CustomComponent { + @Prop() public content?: (ResourceStr | undefined); + + @Prop() public isLoading: boolean; + + @State() public code: number; + + @Builder() public build(): void + + public constructor() {} + + public static _buildCompatibleNode(options: __Options_SwipeRefresher): void + +} + +@Component() export declare interface __Options_SwipeRefresher { + content?: (ResourceStr | undefined); + @Prop() __backing_content?: (ResourceStr | undefined); + isLoading?: boolean; + @Prop() __backing_isLoading?: boolean; + code?: number; + @State() __backing_code?: number; + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedcript)); +} + +const expectedCheckedScript: string = ` +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, ResourceStr as ResourceStr, Builder as Builder } from "@ohos.arkui.component"; + +import { Prop as Prop, State as State } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Component() export declare final struct SwipeRefresher extends CustomComponent { + @Prop() public content?: (ResourceStr | undefined); + + @Prop() public isLoading: boolean; + + @State() public code: number; + + @Builder() @memo() public build(): void + + public constructor() {} + + public static _buildCompatibleNode(options: __Options_SwipeRefresher): void + +} + +@Component() export declare interface __Options_SwipeRefresher { + set content(content: ((ResourceStr | undefined) | undefined)) + + get content(): ((ResourceStr | undefined) | undefined) + set __backing_content(__backing_content: (IPropDecoratedVariable<(ResourceStr | undefined)> | undefined)) + + get __backing_content(): (IPropDecoratedVariable<(ResourceStr | undefined)> | undefined) + set isLoading(isLoading: (boolean | undefined)) + + get isLoading(): (boolean | undefined) + set __backing_isLoading(__backing_isLoading: (IPropDecoratedVariable | undefined)) + + get __backing_isLoading(): (IPropDecoratedVariable | undefined) + set code(code: (number | undefined)) + + get code(): (number | undefined) + set __backing_code(__backing_code: (IStateDecoratedVariable | undefined)) + + get __backing_code(): (IStateDecoratedVariable | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test declare component transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/component/for-each.test.ts b/arkui-plugins/test/ut/ui-plugins/component/for-each.test.ts new file mode 100644 index 000000000..52e9fd7b7 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/component/for-each.test.ts @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const COMPONENT_DIR_PATH: string = 'component'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, COMPONENT_DIR_PATH, 'for-each.ets'), +]; + +const pluginTester = new PluginTester('test ForEach component transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'for-each', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text, WrappedBuilder as WrappedBuilder, Column as Column, ForEach as ForEach } from "@kit.ArkUI"; + +function main() {} + +interface Person { + set name(name: string) + + get name(): string + set age(age: number) + + get age(): number + +} + +class AB { + public per: string = "hello"; + + public bar: Array = new Array("xx", "yy", "zz"); + + public constructor() {} + +} + +@Component() final struct ImportStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_ImportStruct | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_arr = ((({let gensym___244068973 = initializers; + (((gensym___244068973) == (null)) ? undefined : gensym___244068973.arr)})) ?? (["a", "b", "c"])); + } + + public __updateStruct(initializers: (__Options_ImportStruct | undefined)): void {} + + private __backing_arr?: Array; + + public get arr(): Array { + return (this.__backing_arr as Array); + } + + public set arr(value: Array) { + this.__backing_arr = value; + } + + public getArray() { + return new Array(({ + name: "LiHua", + age: 25, + } as Person), ({ + name: "Amy", + age: 18, + } as Person)); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ForEach(((): Array => { + return this.arr; + }), ((item: string) => { + Text(undefined, item, undefined, undefined); + })); + ForEach(((): Array => { + return this.getArray(); + }), ((item: Person) => { + Text(undefined, item.name, undefined, undefined); + })); + ForEach(((): Array => { + return new AB().bar; + }), ((item: string) => { + Text(undefined, item, undefined, undefined); + })); + ForEach(((): Array => { + return new AB().bar; + }), (() => {})); + ForEach(((): Array => { + return this.getArray(); + }), (() => {})); + ForEach(((): Array => { + return new Array("1", "2"); + }), (() => { + ForEach(((): Array => { + return new Array("1", "2"); + }), ((item: string) => { + Text(undefined, item, undefined, undefined); + })); + })); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_ImportStruct { + set arr(arr: (Array | undefined)) + + get arr(): (Array | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test ForEach component transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/builder-param-passing.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/builder-param-passing.test.ts index ba63b9e2d..26590b874 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/builder-param-passing.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/builder-param-passing.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -33,99 +34,254 @@ const pluginTester = new PluginTester('test builder param variable passing', bui const parsedTransform: Plugins = { name: 'builder-param-passing', - parsed: uiTransform().parsed + parsed: uiTransform().parsed, }; -const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "@ohos.arkui.stateManagement"; -import { __memo_context_type as __memo_context_type } from "@ohos.arkui.stateManagement"; -import { memo as memo } from "@ohos.arkui.stateManagement"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; -import { CustomComponent as CustomComponent } from "@ohos.arkui.component"; +const expectedAfterUIScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, Entry as Entry, Builder as Builder, BuilderParam as BuilderParam, Column as Column, Text as Text } from "@ohos.arkui.component"; function main() {} -@Component({freezeWhenInactive:false}) final class Child extends CustomComponent { - public __initializeStruct(initializers: __Options_Child | undefined, @memo() content: (()=> void) | undefined): void { - (this).__backing_customBuilderParam = ((((({let gensym___169376706 = initializers; - (((gensym___169376706) == (null)) ? undefined : gensym___169376706.customBuilderParam)})) ?? (content))) ?? ((this).customBuilder)) +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_customBuilderParam = ((((({let gensym___169376706 = initializers; + (((gensym___169376706) == (null)) ? undefined : gensym___169376706.customBuilderParam)})) ?? (content))) ?? (this.customBuilder)) } - public __updateStruct(initializers: __Options_Child | undefined): void {} + public __updateStruct(initializers: (__Options_Child | undefined)): void {} private __backing_customBuilderParam?: @memo() (()=> void); public get customBuilderParam(): @memo() (()=> void) { - return (this).__backing_customBuilderParam!; + return this.__backing_customBuilderParam!; } - public set customBuilderParam(@memo() value: (()=> void)) { - (this).__backing_customBuilderParam = value; + public set customBuilderParam(value: @memo() (()=> void)) { + this.__backing_customBuilderParam = value; } @memo() public customBuilder() {} - @memo() public _build(@memo() style: ((instance: Child)=> Child) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Child | undefined): void { - (this).customBuilderParam(); + @memo() public build() { + this.customBuilderParam(); } public constructor() {} } -@Component({freezeWhenInactive:false}) final class Parent extends CustomComponent { - public __initializeStruct(initializers: __Options_Parent | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_Parent | undefined): void {} + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} @memo() public componentBuilder() { Text(undefined, "Parent builder", undefined, undefined); } - @memo() public _build(@memo() style: ((instance: Parent)=> Parent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parent | undefined): void { - Column(undefined, undefined, (() => { + @memo() public build() { + Column(undefined, undefined, @memo() (() => { Child._instantiateImpl(undefined, (() => { return new Child(); - }), ({ - customBuilderParam: (this).componentBuilder, - } as __Options_Child), undefined, undefined); + }), { + customBuilderParam: this.componentBuilder, + }, undefined, undefined); Child._instantiateImpl(undefined, (() => { return new Child(); - }), ({ + }), { customBuilderParam: @memo() (() => { - (this).componentBuilder(); + this.componentBuilder(); }), - } as __Options_Child), undefined, undefined); + }, undefined, undefined); Child._instantiateImpl(undefined, (() => { return new Child(); - }), undefined, (() => { + }), undefined, undefined, @memo() (() => { Text(undefined, "Parent builder", undefined, undefined); - }), undefined); + })); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_Child { + set customBuilderParam(customBuilderParam: (@memo() (()=> void) | undefined)) + + get customBuilderParam(): (@memo() (()=> void) | undefined) + +} + +@Component() export interface __Options_Parent { + +} +`; + +const expectedAfterMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, Entry as Entry, Builder as Builder, BuilderParam as BuilderParam, Column as Column, Text as Text } from "@ohos.arkui.component"; + +function main() {} + +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_customBuilderParam = ((((({let gensym___169376706 = initializers; + (((gensym___169376706) == (null)) ? undefined : gensym___169376706.customBuilderParam)})) ?? (content))) ?? (this.customBuilder)) + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_customBuilderParam?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); + + public get customBuilderParam(): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) { + return this.__backing_customBuilderParam!; + } + + public set customBuilderParam(value: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { + this.__backing_customBuilderParam = value; + } + + @memo() public customBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (252759234)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (209256344)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + this.customBuilderParam(__memo_context, ((__memo_id) + (175145513))); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + @memo() public componentBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (219399173)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (137225318)), undefined, "Parent builder", undefined, undefined); + { + __memo_scope.recache(); + return; + } + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (135515930)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + (136716185)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (54078781)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Child._instantiateImpl(__memo_context, ((__memo_id) + (213104625)), undefined, (() => { + return new Child(); + }), { + customBuilderParam: this.componentBuilder, + }, undefined, undefined); + Child._instantiateImpl(__memo_context, ((__memo_id) + (218979098)), undefined, (() => { + return new Child(); + }), { + customBuilderParam: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (76711614)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + this.componentBuilder(__memo_context, ((__memo_id) + (46726221))); + { + __memo_scope.recache(); + return; + } + }), + }, undefined, undefined); + Child._instantiateImpl(__memo_context, ((__memo_id) + (213687742)), undefined, (() => { + return new Child(); + }), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (192802443)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (223657391)), undefined, "Parent builder", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } })); + { + __memo_scope.recache(); + return; + } } + public constructor() {} + } -interface __Options_Child { - set customBuilderParam(customBuilderParam: @memo() (()=> void) | undefined) - get customBuilderParam(): @memo() (()=> void) | undefined +@Component() export interface __Options_Child { + set customBuilderParam(customBuilderParam: (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)) + + get customBuilderParam(): (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) + } -interface __Options_Parent { +@Component() export interface __Options_Parent { + } `; function testCheckedTransformer(this: PluginTestContext): void { - expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedAfterUIScript)); +} + +function testMemoCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedAfterMemoScript)); } pluginTester.run( 'test builder param variable passing', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], { 'checked:ui-no-recheck': [testCheckedTransformer], + 'checked:memo-no-recheck': [testMemoCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/init-with-local-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/init-with-local-builder.test.ts index 21e7f2bb4..b480c1154 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/init-with-local-builder.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/init-with-local-builder.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -36,63 +37,172 @@ const parsedTransform: Plugins = { parsed: uiTransform().parsed }; -const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +const expectedAfterUIScript: string = ` import { memo as memo } from "arkui.stateManagement.runtime"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, Builder as Builder, BuilderParam as BuilderParam } from "@ohos.arkui.component"; - function main() {} -@Component({freezeWhenInactive:false}) final class Child extends CustomComponent { - public __initializeStruct(initializers: __Options_Child | undefined, @memo() content: (()=> void) | undefined): void { +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { this.__backing_customBuilderParam = ((((({let gensym___169376706 = initializers; (((gensym___169376706) == (null)) ? undefined : gensym___169376706.customBuilderParam)})) ?? (content))) ?? (this.doNothingBuilder)) this.__backing_customBuilderParam2 = ((((({let gensym___14041256 = initializers; (((gensym___14041256) == (null)) ? undefined : gensym___14041256.customBuilderParam2)})) ?? (content))) ?? (this.doNothingBuilder2)) } - public __updateStruct(initializers: __Options_Child | undefined): void {} + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + private __backing_customBuilderParam?: @memo() (()=> void); + public get customBuilderParam(): @memo() (()=> void) { return this.__backing_customBuilderParam!; } - public set customBuilderParam(@memo() value: (()=> void)) { + + public set customBuilderParam(value: @memo() (()=> void)) { this.__backing_customBuilderParam = value; } + private __backing_customBuilderParam2?: @memo() ((str: string)=> void); + public get customBuilderParam2(): @memo() ((str: string)=> void) { return this.__backing_customBuilderParam2!; } - public set customBuilderParam2(@memo() value: ((str: string)=> void)) { + + public set customBuilderParam2(value: @memo() ((str: string)=> void)) { this.__backing_customBuilderParam2 = value; } + @memo() public doNothingBuilder() {} + @memo() public doNothingBuilder2(str: string) {} - @memo() public _build(@memo() style: ((instance: Child)=> Child) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Child | undefined): void { + + @memo() public build() { this.customBuilderParam(); this.customBuilderParam2("hello"); } + + public constructor() {} + +} + +@Component() export interface __Options_Child { + set customBuilderParam(customBuilderParam: (@memo() (()=> void) | undefined)) + + get customBuilderParam(): (@memo() (()=> void) | undefined) + set customBuilderParam2(customBuilderParam2: (@memo() ((str: string)=> void) | undefined)) + + get customBuilderParam2(): (@memo() ((str: string)=> void) | undefined) + +} +`; + +const expectedAfterMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, Builder as Builder, BuilderParam as BuilderParam } from "@ohos.arkui.component"; + +function main() {} + +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_customBuilderParam = ((((({let gensym___169376706 = initializers; + (((gensym___169376706) == (null)) ? undefined : gensym___169376706.customBuilderParam)})) ?? (content))) ?? (this.doNothingBuilder)) + this.__backing_customBuilderParam2 = ((((({let gensym___14041256 = initializers; + (((gensym___14041256) == (null)) ? undefined : gensym___14041256.customBuilderParam2)})) ?? (content))) ?? (this.doNothingBuilder2)) + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_customBuilderParam?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); + + public get customBuilderParam(): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) { + return this.__backing_customBuilderParam!; + } + + public set customBuilderParam(value: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { + this.__backing_customBuilderParam = value; + } + + private __backing_customBuilderParam2?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string)=> void); + + public get customBuilderParam2(): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string)=> void) { + return this.__backing_customBuilderParam2!; + } + + public set customBuilderParam2(value: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string)=> void)) { + this.__backing_customBuilderParam2 = value; + } + + @memo() public doNothingBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (174403279)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + + @memo() public doNothingBuilder2(__memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string) { + const __memo_scope = __memo_context.scope(((__memo_id) + (76253767)), 1); + const __memo_parameter_str = __memo_scope.param(0, str); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (179390036)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + this.customBuilderParam(__memo_context, ((__memo_id) + (241913892))); + this.customBuilderParam2(__memo_context, ((__memo_id) + (137225318)), "hello"); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} + } -interface __Options_Child { - set customBuilderParam(customBuilderParam: @memo() (()=> void) | undefined) - get customBuilderParam(): @memo() (()=> void) | undefined - set customBuilderParam2(customBuilderParam2: @memo() ((str: string)=> void) | undefined) - get customBuilderParam2(): @memo() ((str: string)=> void) | undefined +@Component() export interface __Options_Child { + set customBuilderParam(customBuilderParam: (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)) + + get customBuilderParam(): (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) + set customBuilderParam2(customBuilderParam2: (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string)=> void) | undefined)) + + get customBuilderParam2(): (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string)=> void) | undefined) + } `; function testCheckedTransformer(this: PluginTestContext): void { - expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedAfterUIScript)); +} + +function testAfterMemoCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedAfterMemoScript)); } pluginTester.run( 'test builder param init with local builder', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], { 'checked:ui-no-recheck': [testCheckedTransformer], + 'checked:memo-no-recheck': [testAfterMemoCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/optional-builder-param.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/optional-builder-param.test.ts new file mode 100644 index 000000000..68b761ca7 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/optional-builder-param.test.ts @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'decorators/builder-param'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'optional-builder-param.ets'), +]; + +const pluginTester = new PluginTester('test optional builder param', buildConfig); + +const parsedTransform: Plugins = { + name: 'optional-builder-param', + parsed: uiTransform().parsed +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from "arkui.component.builder"; +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, Entry as Entry, Builder as Builder, BuilderParam as BuilderParam, Column as Column, Text as Text, Row as Row } from "@kit.ArkUI"; + +function main() {} + +@memo() function showTextBuilder() { + Text(undefined, "Hello World", undefined, undefined); +} + +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_customBuilderParam2 = ((((({let gensym___103851375 = initializers; + (((gensym___103851375) == (null)) ? undefined : gensym___103851375.customBuilderParam2)})) ?? (content))) ?? (undefined)) + this.__backing_customBuilderParam1 = ((((({let gensym___20169645 = initializers; + (((gensym___20169645) == (null)) ? undefined : gensym___20169645.customBuilderParam1)})) ?? (content))) ?? (showTextBuilder)) + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_customBuilderParam2?: ((()=> void) | undefined); + + public get customBuilderParam2(): (@memo() (()=> void) | undefined) { + return this.__backing_customBuilderParam2; + } + + public set customBuilderParam2(value: (@memo() (()=> void) | undefined)) { + this.__backing_customBuilderParam2 = value; + } + + private __backing_customBuilderParam1?: @memo() (()=> void); + + public get customBuilderParam1(): @memo() (()=> void) { + return this.__backing_customBuilderParam1!; + } + + public set customBuilderParam1(value: @memo() (()=> void)) { + this.__backing_customBuilderParam1 = value; + } + + @memo() public build() { + Row(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + if (this.customBuilderParam2) { + ConditionBranch(@memo() (() => { + (this.customBuilderParam2 as (()=> void))(); + })); + } + })); + ConditionScope(@memo() (() => { + if (this.customBuilderParam2) { + ConditionBranch(@memo() (() => { + this.customBuilderParam2!(); + })); + } + })); + this.customBuilderParam1(); + })); + } + + public constructor() {} + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + @memo() public componentBuilder() { + Text(undefined, "Parent builder", undefined, undefined); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + customBuilderParam2: @memo() (() => { + this.componentBuilder(); + }), + }, undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_Child { + set customBuilderParam2(customBuilderParam2: (((()=> void) | undefined) | undefined)) + + get customBuilderParam2(): (((()=> void) | undefined) | undefined) + set customBuilderParam1(customBuilderParam1: (@memo() (()=> void) | undefined)) + + get customBuilderParam1(): (@memo() (()=> void) | undefined) + +} + +@Component() export interface __Options_Parent { + +} +`; + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { ConditionScope as ConditionScope } from "arkui.component.builder"; +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, Entry as Entry, Builder as Builder, BuilderParam as BuilderParam, Column as Column, Text as Text, Row as Row } from "@kit.ArkUI"; + +function main() {} + +@memo() function showTextBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (183537441)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (47330804)), undefined, "Hello World", undefined, undefined); + { + __memo_scope.recache(); + return; + } +} + +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_customBuilderParam2 = ((((({let gensym___103851375 = initializers; + (((gensym___103851375) == (null)) ? undefined : gensym___103851375.customBuilderParam2)})) ?? (content))) ?? (undefined)) + this.__backing_customBuilderParam1 = ((((({let gensym___20169645 = initializers; + (((gensym___20169645) == (null)) ? undefined : gensym___20169645.customBuilderParam1)})) ?? (content))) ?? (showTextBuilder)) + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_customBuilderParam2?: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined); + + public get customBuilderParam2(): (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) { + return this.__backing_customBuilderParam2; + } + + public set customBuilderParam2(value: (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)) { + this.__backing_customBuilderParam2 = value; + } + + private __backing_customBuilderParam1?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); + + public get customBuilderParam1(): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) { + return this.__backing_customBuilderParam1!; + } + + public set customBuilderParam1(value: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { + this.__backing_customBuilderParam1 = value; + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (234402485)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Row(__memo_context, ((__memo_id) + (46726221)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (213104625)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (this.customBuilderParam2) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + (this.customBuilderParam2 as ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (this.customBuilderParam2) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + this.customBuilderParam2!(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + this.customBuilderParam1(__memo_context, ((__memo_id) + (211301233))); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + @memo() public componentBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (179117969)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (218979098)), undefined, "Parent builder", undefined, undefined); + { + __memo_scope.recache(); + return; + } + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (245938697)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + (78055758)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (136716185)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Child._instantiateImpl(__memo_context, ((__memo_id) + (54078781)), undefined, (() => { + return new Child(); + }), { + customBuilderParam2: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (213687742)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + this.componentBuilder(__memo_context, ((__memo_id) + (192802443))); + { + __memo_scope.recache(); + return; + } + }), + }, undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} + +@Component() export interface __Options_Child { + set customBuilderParam2(customBuilderParam2: ((((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) | undefined)) + + get customBuilderParam2(): ((((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) | undefined) + set customBuilderParam1(customBuilderParam1: (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)) + + get customBuilderParam1(): (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) + +} + +@Component() export interface __Options_Parent { + +} +`; + +function testUICheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +function testMemoCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test optional builder param', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUICheckedTransformer], + 'checked:memo-no-recheck': [testMemoCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/builder/global-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/builder/global-builder.test.ts index 95740a1d5..478bc834a 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/builder/global-builder.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder/global-builder.test.ts @@ -14,58 +14,63 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; const FUNCTION_DIR_PATH: string = 'decorators/builder'; const buildConfig: BuildConfig = mockBuildConfig(); -buildConfig.compileFiles = [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'global-builder.ets'), -]; +buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'global-builder.ets')]; const pluginTester = new PluginTester('test global builder', buildConfig); const parsedTransform: Plugins = { name: 'global-builder', - parsed: uiTransform().parsed + parsed: uiTransform().parsed, }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { UIRowAttribute as UIRowAttribute } from "@ohos.arkui.component"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, Row as Row, Builder as Builder, Text as Text } from "@ohos.arkui.component"; function main() {} + @memo() function showTextBuilder() { Text(undefined, "Hello World", undefined, undefined); } @memo() function overBuilder(params: Tmp) { - Row(undefined, undefined, (() => { + Row(undefined, undefined, @memo() (() => { Text(undefined, (("UseStateVarByReference: ") + (params.paramA1)), undefined, undefined); })); } + class Tmp { public paramA1: string = ""; + public constructor() {} + } -@Component({freezeWhenInactive:false}) final class BuilderDemo extends CustomComponent { - public __initializeStruct(initializers: __Options_BuilderDemo | undefined, @memo() content: (()=> void) | undefined): void {} - public __updateStruct(initializers: __Options_BuilderDemo | undefined): void {} - @memo() public _build(@memo() style: ((instance: BuilderDemo)=> BuilderDemo) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_BuilderDemo | undefined): void { +@Component() final struct BuilderDemo extends CustomComponent { + public __initializeStruct(initializers: (__Options_BuilderDemo | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_BuilderDemo | undefined)): void {} + + @memo() public build() { Row(undefined, undefined, @memo() (() => { showTextBuilder(); overBuilder({ @@ -73,10 +78,12 @@ class Tmp { }); })); } + public constructor() {} + } -interface __Options_BuilderDemo { +@Component() export interface __Options_BuilderDemo { } `; @@ -87,7 +94,7 @@ function testCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'global builder', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { 'checked:ui-no-recheck': [testCheckedTransformer], }, diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/builder/local-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/builder/local-builder.test.ts index 9769285c5..e4ece6f3a 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/builder/local-builder.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder/local-builder.test.ts @@ -14,64 +14,73 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; const FUNCTION_DIR_PATH: string = 'decorators/builder'; const buildConfig: BuildConfig = mockBuildConfig(); -buildConfig.compileFiles = [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'local-builder.ets'), -]; +buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'local-builder.ets')]; const pluginTester = new PluginTester('test local builder', buildConfig); const parsedTransform: Plugins = { name: 'local-builder', - parsed: uiTransform().parsed + parsed: uiTransform().parsed, }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, Column as Column, Builder as Builder, Text as Text } from "@ohos.arkui.component"; function main() {} -@Component({freezeWhenInactive:false}) final class BuilderDemo extends CustomComponent { - public __initializeStruct(initializers: __Options_BuilderDemo | undefined, @memo() content: (()=> void) | undefined): void {} - public __updateStruct(initializers: __Options_BuilderDemo | undefined): void {} + + +@Component() final struct BuilderDemo extends CustomComponent { + public __initializeStruct(initializers: (__Options_BuilderDemo | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_BuilderDemo | undefined)): void {} + @memo() public showTextBuilder() { - Text(@memo() ((instance: UITextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { instance.fontSize(30); return; }), "Hello World", undefined, undefined); } + @memo() public showTextValueBuilder(param: string) { - Text(@memo() ((instance: UITextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { instance.fontSize(30); return; }), param, undefined, undefined); } - @memo() public _build(@memo() style: ((instance: BuilderDemo)=> BuilderDemo) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_BuilderDemo | undefined): void { + + @memo() public build() { Column(undefined, undefined, @memo() (() => { this.showTextBuilder(); this.showTextValueBuilder("Hello @Builder"); })); } + public constructor() {} + } -interface __Options_BuilderDemo { +@Component() export interface __Options_BuilderDemo { } `; @@ -82,7 +91,7 @@ function testCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'local builder', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { 'checked:ui-no-recheck': [testCheckedTransformer], }, diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-in-observedv2-class.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-in-observedv2-class.test.ts new file mode 100644 index 000000000..cd46414df --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-in-observedv2-class.test.ts @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/computed'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'computed-in-observedv2-class.ets'), +]; + +const pluginTester = new PluginTester('test @Computed decorator in @ObservedV2 class', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { Computed as Computed, ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ObservedV2() class Name implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"firstName"}) private __backing_firstName: string = "Hua"; + + @JSONStringifyIgnore() private __meta_firstName: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"lastName"}) private __backing_lastName: string = "Li"; + + @JSONStringifyIgnore() private __meta_lastName: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + private __computed_fullName = STATE_MGMT_FACTORY.makeComputed((() => { + console.info("---------Computed----------"); + return ((((this.firstName) + (" "))) + (this.lastName)); + }), "fullName"); + + @Computed() public get fullName(): string { + return this.__computed_fullName!.get(); + } + + public get firstName(): string { + this.conditionalAddRef(this.__meta_firstName); + return UIUtils.makeObserved(this.__backing_firstName); + } + + public set firstName(newValue: string) { + if (((this.__backing_firstName) !== (newValue))) { + this.__backing_firstName = newValue; + this.__meta_firstName.fireChange(); + this.executeOnSubscribingWatches("firstName"); + } + } + + public get lastName(): string { + this.conditionalAddRef(this.__meta_lastName); + return UIUtils.makeObserved(this.__backing_lastName); + } + + public set lastName(newValue: string) { + if (((this.__backing_lastName) !== (newValue))) { + this.__backing_lastName = newValue; + this.__meta_lastName.fireChange(); + this.executeOnSubscribingWatches("lastName"); + } + } + + public constructor() {} + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Computed decorator in @ObservedV2 class', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-in-struct.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-in-struct.test.ts new file mode 100644 index 000000000..e904a0ed4 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-in-struct.test.ts @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/computed'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'computed-in-struct.ets'), +]; + +const pluginTester = new PluginTester('test @Computed decorator in @ComponentV2 struct', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Button as Button, Divider as Divider, Text as Text } from "@ohos.arkui.component"; + +import { Computed as Computed, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_firstName = STATE_MGMT_FACTORY.makeLocal(this, "firstName", "Li"); + this.__backing_lastName = STATE_MGMT_FACTORY.makeLocal(this, "lastName", "Hua"); + this.__backing_age = ((({let gensym___216981064 = initializers; + (((gensym___216981064) == (null)) ? undefined : gensym___216981064.age)})) ?? (20)); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_firstName?: ILocalDecoratedVariable; + + public get firstName(): string { + return this.__backing_firstName!.get(); + } + + public set firstName(value: string) { + this.__backing_firstName!.set(value); + } + + private __backing_lastName?: ILocalDecoratedVariable; + + public get lastName(): string { + return this.__backing_lastName!.get(); + } + + public set lastName(value: string) { + this.__backing_lastName!.set(value); + } + + private __backing_age?: number; + + public get age(): number { + return (this.__backing_age as number); + } + + public set age(value: number) { + this.__backing_age = value; + } + + private __computed_fullName = STATE_MGMT_FACTORY.makeComputed((() => { + console.info("---------Computed----------"); + return ((((((this.firstName) + (" "))) + (this.lastName))) + (this.age)); + }), "fullName"); + + @Computed() public get fullName(): string { + return this.__computed_fullName!.get(); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, ((((this.lastName) + (" "))) + (this.firstName)), undefined, undefined); + Text(undefined, ((((this.lastName) + (" "))) + (this.firstName)), undefined, undefined); + Divider(undefined); + Text(undefined, this.fullName, undefined, undefined); + Text(undefined, this.fullName, undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.lastName += "a"; + })); + return; + }), "changed lastName", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + (this.age++); + })); + return; + }), "changed age", undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set firstName(firstName: (string | undefined)) + + get firstName(): (string | undefined) + set __backing_firstName(__backing_firstName: (ILocalDecoratedVariable | undefined)) + + get __backing_firstName(): (ILocalDecoratedVariable | undefined) + set lastName(lastName: (string | undefined)) + + get lastName(): (string | undefined) + set __backing_lastName(__backing_lastName: (ILocalDecoratedVariable | undefined)) + + get __backing_lastName(): (ILocalDecoratedVariable | undefined) + set age(age: (number | undefined)) + + get age(): (number | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Computed decorator in @ComponentV2 struct', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-no-return-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-no-return-type.test.ts new file mode 100644 index 000000000..c592e5333 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-no-return-type.test.ts @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/computed'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'computed-no-return-type.ets'), +]; + +const pluginTester = new PluginTester('test @Computed decorator with no return type', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { Computed as Computed, ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +import { ComponentV2 as ComponentV2, Column as Column, Button as Button, Divider as Divider, Text as Text } from "@ohos.arkui.component"; + +import { Computed as Computed, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ObservedV2() class Name implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"firstName"}) private __backing_firstName: string = "Hua"; + + @JSONStringifyIgnore() private __meta_firstName: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"lastName"}) private __backing_lastName: string = "Li"; + + @JSONStringifyIgnore() private __meta_lastName: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public age: number = 20; + + private __computed_fullName = STATE_MGMT_FACTORY.makeComputed<(Array | string)>((() => { + if (((this.age) >= (20))) { + return new Array(0, 1, 2); + } + return ((((this.firstName) + (" "))) + (this.lastName)); + }), "fullName"); + + @Computed() public get fullName(): (Array | string) { + return this.__computed_fullName!.get(); + } + + public get firstName(): string { + this.conditionalAddRef(this.__meta_firstName); + return UIUtils.makeObserved(this.__backing_firstName); + } + + public set firstName(newValue: string) { + if (((this.__backing_firstName) !== (newValue))) { + this.__backing_firstName = newValue; + this.__meta_firstName.fireChange(); + this.executeOnSubscribingWatches("firstName"); + } + } + + public get lastName(): string { + this.conditionalAddRef(this.__meta_lastName); + return UIUtils.makeObserved(this.__backing_lastName); + } + + public set lastName(newValue: string) { + if (((this.__backing_lastName) !== (newValue))) { + this.__backing_lastName = newValue; + this.__meta_lastName.fireChange(); + this.executeOnSubscribingWatches("lastName"); + } + } + + public constructor() {} + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_firstName = STATE_MGMT_FACTORY.makeLocal(this, "firstName", "Li"); + this.__backing_lastName = STATE_MGMT_FACTORY.makeLocal(this, "lastName", "Hua"); + this.__backing_age = ((({let gensym___216981064 = initializers; + (((gensym___216981064) == (null)) ? undefined : gensym___216981064.age)})) ?? (20)); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_firstName?: ILocalDecoratedVariable; + + public get firstName(): string { + return this.__backing_firstName!.get(); + } + + public set firstName(value: string) { + this.__backing_firstName!.set(value); + } + + private __backing_lastName?: ILocalDecoratedVariable; + + public get lastName(): string { + return this.__backing_lastName!.get(); + } + + public set lastName(value: string) { + this.__backing_lastName!.set(value); + } + + private __backing_age?: number; + + public get age(): number { + return (this.__backing_age as number); + } + + public set age(value: number) { + this.__backing_age = value; + } + + private __computed_fullName = STATE_MGMT_FACTORY.makeComputed<(Int | string)>((() => { + if (((this.age) >= (20))) { + return 500; + } + return ((((((this.firstName) + (" "))) + (this.lastName))) + (this.age)); + }), "fullName"); + + @Computed() public get fullName(): (Int | string) { + return this.__computed_fullName!.get(); + } + + private __computed_num5 = STATE_MGMT_FACTORY.makeComputed((() => { + return 5; + }), "num5"); + + @Computed() public get num5(): Int { + return this.__computed_num5!.get(); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set firstName(firstName: (string | undefined)) + + get firstName(): (string | undefined) + set __backing_firstName(__backing_firstName: (ILocalDecoratedVariable | undefined)) + + get __backing_firstName(): (ILocalDecoratedVariable | undefined) + set lastName(lastName: (string | undefined)) + + get lastName(): (string | undefined) + set __backing_lastName(__backing_lastName: (ILocalDecoratedVariable | undefined)) + + get __backing_lastName(): (ILocalDecoratedVariable | undefined) + set age(age: (number | undefined)) + + get age(): (number | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Computed decorator with no return type', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/computed/static-computed.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/computed/static-computed.test.ts new file mode 100644 index 000000000..2d7331ed6 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/computed/static-computed.test.ts @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/computed'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'static-computed.ets'), +]; + +const pluginTester = new PluginTester('test @Computed decorated static getter method', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Computed as Computed, ObservedV2 as ObservedV2, Trace as Trace, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ObservedV2() class Name implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"firstName"}) public static __backing_firstName: string = "Hua"; + + @JSONStringifyIgnore() public static __meta_firstName: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"lastName"}) public static __backing_lastName: string = "Li"; + + @JSONStringifyIgnore() public static __meta_lastName: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public static __computed_fullName = STATE_MGMT_FACTORY.makeComputed((() => { + return ((((Name.firstName) + (" "))) + (Name.lastName)); + }), "fullName"); + + @Computed() public static get fullName(): string { + return Name.__computed_fullName.get(); + } + + static { + + } + public static get firstName(): string { + Name.__meta_firstName.addRef(); + return UIUtils.makeObserved(Name.__backing_firstName); + } + + public static set firstName(newValue: string) { + if (((Name.__backing_firstName) !== (newValue))) { + Name.__backing_firstName = newValue; + Name.__meta_firstName.fireChange(); + } + } + + public static get lastName(): string { + Name.__meta_lastName.addRef(); + return UIUtils.makeObserved(Name.__backing_lastName); + } + + public static set lastName(newValue: string) { + if (((Name.__backing_lastName) !== (newValue))) { + Name.__backing_lastName = newValue; + Name.__meta_lastName.fireChange(); + } + } + + public constructor() {} + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + public static __backing_localVar1: ILocalDecoratedVariable = STATE_MGMT_FACTORY.makeStaticLocal("localVar1", "stateVar1"); + + public static get localVar1(): string { + return Parent.__backing_localVar1.get(); + } + + public static set localVar1(value: string) { + Parent.__backing_localVar1.set(value); + } + + public static __backing_localVar2: ILocalDecoratedVariable = STATE_MGMT_FACTORY.makeStaticLocal("localVar2", 50); + + public static get localVar2(): number { + return Parent.__backing_localVar2.get(); + } + + public static set localVar2(value: number) { + Parent.__backing_localVar2.set(value); + } + + public static __computed_fullName = STATE_MGMT_FACTORY.makeComputed((() => { + return Parent.localVar1; + }), "fullName"); + + @Computed() public static get fullName(): string { + return Parent.__computed_fullName.get(); + } + + @memo() public build() {} + + public constructor() {} + + static { + + } +} + +@ComponentV2() final struct Parent2 extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent2 | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_Parent2 | undefined)): void {} + + public static __computed_fullName = STATE_MGMT_FACTORY.makeComputed((() => { + return ((((Name.firstName) + (" "))) + (Name.lastName)); + }), "fullName"); + + @Computed() public static get fullName(): string { + return Parent2.__computed_fullName.get(); + } + + public static __computed_fullName2 = STATE_MGMT_FACTORY.makeComputed((() => { + return Parent.localVar1; + }), "fullName2"); + + @Computed() public static get fullName2(): string { + return Parent2.__computed_fullName2.get(); + } + + @memo() public build() {} + + public constructor() {} + + static { + + } +} + +@ObservedV2() class Name2 implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public static __computed_fullName = STATE_MGMT_FACTORY.makeComputed((() => { + return ((((Name.firstName) + (" "))) + (Name.lastName)); + }), "fullName"); + + @Computed() public static get fullName(): string { + return Name2.__computed_fullName.get(); + } + + static { + + } + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set localVar1(localVar1: (string | undefined)) + + get localVar1(): (string | undefined) + set __backing_localVar1(__backing_localVar1: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar1(): (ILocalDecoratedVariable | undefined) + set localVar2(localVar2: (number | undefined)) + + get localVar2(): (number | undefined) + set __backing_localVar2(__backing_localVar2: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar2(): (ILocalDecoratedVariable | undefined) + +} + +@ComponentV2() export interface __Options_Parent2 { + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Computed decorated static getter method', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/base-custom-dialog.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/base-custom-dialog.test.ts new file mode 100644 index 000000000..0e22b1f8c --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/base-custom-dialog.test.ts @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'decorators/custom-dialog'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'base-custom-dialog.ets'), +]; + +const pluginTester = new PluginTester('test basic capability of @CustomDialog', buildConfig); + +const parsedTransform: Plugins = { + name: 'base-custom-dialog', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { BaseCustomDialog as BaseCustomDialog } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Text as Text, Column as Column, Component as Component, Entry as Entry, Button as Button, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { State as State, Link as Link, Prop as Prop } from "@ohos.arkui.stateManagement"; + +import { CustomDialog as CustomDialog, CustomDialogController as CustomDialogController, DismissDialogAction as DismissDialogAction, DismissReason as DismissReason, DialogAlignment as DialogAlignment, CustomDialogControllerOptions as CustomDialogControllerOptions } from "@ohos.arkui.component"; + +import hilog from "@ohos.hilog"; + +function main() {} + +@CustomDialog() final struct CustomDialogExample extends BaseCustomDialog { + public __initializeStruct(initializers: (__Options_CustomDialogExample | undefined), @memo() content: ((()=> void) | undefined)): void { + if (({let gensym___45519047 = initializers; + (((gensym___45519047) == (null)) ? undefined : gensym___45519047.aaController)})) { + this.__backing_aaController = ((({let gensym___180078470 = initializers; + (((gensym___180078470) == (null)) ? undefined : gensym___180078470.aaController)})) ?? (undefined)); + } + this.__backing_text = STATE_MGMT_FACTORY.makeState(this, "text", ((({let gensym___217676902 = initializers; + (((gensym___217676902) == (null)) ? undefined : gensym___217676902.text)})) ?? ("text"))); + this.__backing_cancel = ((({let gensym___25471281 = initializers; + (((gensym___25471281) == (null)) ? undefined : gensym___25471281.cancel)})) ?? ((() => {}))); + this.__backing_confirm = ((({let gensym___213253394 = initializers; + (((gensym___213253394) == (null)) ? undefined : gensym___213253394.confirm)})) ?? ((() => {}))); + this.__backing_hh = STATE_MGMT_FACTORY.makeState(this, "hh", ((({let gensym___210200872 = initializers; + (((gensym___210200872) == (null)) ? undefined : gensym___210200872.hh)})) ?? ("nihao"))); + } + + public __updateStruct(initializers: (__Options_CustomDialogExample | undefined)): void {} + + private __backing_aaController?: (CustomDialogController | undefined); + + public get aaController(): (CustomDialogController | undefined) { + return (this.__backing_aaController as (CustomDialogController | undefined)); + } + + public set aaController(value: (CustomDialogController | undefined)) { + this.__backing_aaController = value; + } + + private __backing_text?: IStateDecoratedVariable; + + public get text(): string { + return this.__backing_text!.get(); + } + + public set text(value: string) { + this.__backing_text!.set(value); + } + + private __backing_cancel?: (()=> void); + + public get cancel(): (()=> void) { + return (this.__backing_cancel as (()=> void)); + } + + public set cancel(value: (()=> void)) { + this.__backing_cancel = value; + } + + private __backing_confirm?: (()=> void); + + public get confirm(): (()=> void) { + return (this.__backing_confirm as (()=> void)); + } + + public set confirm(value: (()=> void)) { + this.__backing_confirm = value; + } + + private __backing_hh?: IStateDecoratedVariable; + + public get hh(): string { + return this.__backing_hh!.get(); + } + + public set hh(value: string) { + this.__backing_hh!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(30).height(100); + return; + }), "CustomDialog One", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + if (((this.aaController) != (undefined))) { + this.aaController!.close(); + } + })).margin(20); + return; + }), "Close", undefined, undefined); + })); + } + + public constructor() {} + + public __setDialogController__(controller: CustomDialogController): void { + this.__backing_aaController = controller; + } +} + +@Component() final struct CustomDialogUser extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomDialogUser | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_dialogController = ((({let gensym___56650533 = initializers; + (((gensym___56650533) == (null)) ? undefined : gensym___56650533.dialogController)})) ?? (({let gensym___249621102: Any; + gensym___249621102 = new CustomDialogController({ + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___249621102 as CustomDialogController)); + return instance; + }), { + cancel: (() => { + this.onCancel(); + }), + confirm: (() => { + this.onAccept(); + }), + }, undefined); + }), + cancel: this.existApp, + autoCancel: true, + alignment: DialogAlignment.Center, + offset: { + dx: 0, + dy: -20, + }, + gridCount: 4, + showInSubWindow: true, + isModal: true, + customStyle: false, + cornerRadius: 10, + focusable: true, + baseComponent: this, + }) + (gensym___249621102 as CustomDialogController)}))); + } + + public __updateStruct(initializers: (__Options_CustomDialogUser | undefined)): void {} + + private __backing_dialogController?: (CustomDialogController | null); + + public get dialogController(): (CustomDialogController | null) { + return (this.__backing_dialogController as (CustomDialogController | null)); + } + + public set dialogController(value: (CustomDialogController | null)) { + this.__backing_dialogController = value; + } + + public aboutToDisappear() { + this.dialogController = null; + } + + public onCancel() { + console.info("Callback when the first button is clicked"); + } + + public onAccept() { + console.info("Callback when the second button is clicked"); + } + + public existApp() { + console.info("Click the callback in the blank area"); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + if (((this.dialogController) != (null))) { + this.dialogController!.open(); + } + })).backgroundColor(0x317aff); + return; + }), "click me", undefined, undefined); + })); + } + + public constructor() {} + +} + +@CustomDialog() export interface __Options_CustomDialogExample { + set aaController(aaController: ((CustomDialogController | undefined) | undefined)) + + get aaController(): ((CustomDialogController | undefined) | undefined) + set text(text: (string | undefined)) + + get text(): (string | undefined) + set __backing_text(__backing_text: (IStateDecoratedVariable | undefined)) + + get __backing_text(): (IStateDecoratedVariable | undefined) + set cancel(cancel: ((()=> void) | undefined)) + + get cancel(): ((()=> void) | undefined) + set confirm(confirm: ((()=> void) | undefined)) + + get confirm(): ((()=> void) | undefined) + set hh(hh: (string | undefined)) + + get hh(): (string | undefined) + set __backing_hh(__backing_hh: (IStateDecoratedVariable | undefined)) + + get __backing_hh(): (IStateDecoratedVariable | undefined) + +} + +@Component() export interface __Options_CustomDialogUser { + set dialogController(dialogController: ((CustomDialogController | null) | undefined)) + + get dialogController(): ((CustomDialogController | null) | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic capability of @CustomDialog', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/builder-dialog-options.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/builder-dialog-options.test.ts new file mode 100644 index 000000000..0d02a2478 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/builder-dialog-options.test.ts @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'decorators/custom-dialog'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'builder-dialog-options.ets'), +]; + +const pluginTester = new PluginTester('test CustomDialogControllerOptions with @Builder parameter', buildConfig); + +const parsedTransform: Plugins = { + name: 'builder-dialog-options', + parsed: uiTransform().parsed, +}; + +const expectedParsedScript: string = ` +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Text as Text, Column as Column, Component as Component, Builder as Builder } from "@ohos.arkui.component"; + +import { CustomDialog as CustomDialog, CustomDialogController as CustomDialogController, CustomDialogControllerOptions as CustomDialogControllerOptions } from "@kit.ArkUI"; + +import hilog from "@ohos.hilog"; + +@Builder() function builder1(str: string) {} + +@Builder() function builder2() {} + +@Component() final struct CustomDialogUser extends CustomComponent { + public dialogController: (CustomDialogController | null) = new CustomDialogController({ + builder: (() => { + builder1("nihao"); + }), + }); + + public build() { + Column(){}; + } + + public constructor() {} + +} + +@Component() final struct CustomDialogUser2 extends CustomComponent { + public dialogController: (CustomDialogController | null) = new CustomDialogController({ + builder: builder2, + }); + + public build() { + Column(){}; + } + + public constructor() {} + +} + +@Component() export interface __Options_CustomDialogUser { + dialogController?: (CustomDialogController | null); + +} + +@Component() export interface __Options_CustomDialogUser2 { + dialogController?: (CustomDialogController | null); + +} +`; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Text as Text, Column as Column, Component as Component, Builder as Builder } from "@ohos.arkui.component"; + +import { CustomDialog as CustomDialog, CustomDialogController as CustomDialogController, CustomDialogControllerOptions as CustomDialogControllerOptions } from "@kit.ArkUI"; + +import hilog from "@ohos.hilog"; + +function main() {} + +@memo() function builder1(str: string) {} + +@memo() function builder2() {} + +@Component() final struct CustomDialogUser extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomDialogUser | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_dialogController = ((({let gensym___51459619 = initializers; + (((gensym___51459619) == (null)) ? undefined : gensym___51459619.dialogController)})) ?? (({let gensym___203542966: Any; + gensym___203542966 = new CustomDialogController({ + builder: @memo() (() => { + builder1("nihao"); + }), + baseComponent: this, + }) + (gensym___203542966 as CustomDialogController)}))); + } + + public __updateStruct(initializers: (__Options_CustomDialogUser | undefined)): void {} + + private __backing_dialogController?: (CustomDialogController | null); + + public get dialogController(): (CustomDialogController | null) { + return (this.__backing_dialogController as (CustomDialogController | null)); + } + + public set dialogController(value: (CustomDialogController | null)) { + this.__backing_dialogController = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => {})); + } + + public constructor() {} + +} + +@Component() final struct CustomDialogUser2 extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomDialogUser2 | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_dialogController = ((({let gensym___176924847 = initializers; + (((gensym___176924847) == (null)) ? undefined : gensym___176924847.dialogController)})) ?? (({let gensym___46528967: Any; + gensym___46528967 = new CustomDialogController({ + builder: builder2, + baseComponent: this, + }) + (gensym___46528967 as CustomDialogController)}))); + } + + public __updateStruct(initializers: (__Options_CustomDialogUser2 | undefined)): void {} + + private __backing_dialogController?: (CustomDialogController | null); + + public get dialogController(): (CustomDialogController | null) { + return (this.__backing_dialogController as (CustomDialogController | null)); + } + + public set dialogController(value: (CustomDialogController | null)) { + this.__backing_dialogController = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => {})); + } + + public constructor() {} + +} + +@Component() export interface __Options_CustomDialogUser { + set dialogController(dialogController: ((CustomDialogController | null) | undefined)) + + get dialogController(): ((CustomDialogController | null) | undefined) + +} + +@Component() export interface __Options_CustomDialogUser2 { + set dialogController(dialogController: ((CustomDialogController | null) | undefined)) + + get dialogController(): ((CustomDialogController | null) | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test CustomDialogControllerOptions with @Builder parameter', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/controller-in-build.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/controller-in-build.test.ts new file mode 100644 index 000000000..ecad395ed --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/controller-in-build.test.ts @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'decorators/custom-dialog'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'controller-in-build.ets'), +]; + +const pluginTester = new PluginTester('test CustomDialogController in build', buildConfig); + +const parsedTransform: Plugins = { + name: 'controller-in-build', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { BaseCustomDialog as BaseCustomDialog } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Text as Text, Column as Column, Component as Component, Button as Button, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { State as State, Link as Link, Prop as Prop } from "@ohos.arkui.stateManagement"; + +import { CustomDialog as CustomDialog, CustomDialogController as CustomDialogController, CustomDialogControllerOptions as CustomDialogControllerOptions } from "@kit.ArkUI"; + +import hilog from "@ohos.hilog"; + +function main() {} + + + +@CustomDialog() final struct CustomDialogExample extends BaseCustomDialog { + public __initializeStruct(initializers: (__Options_CustomDialogExample | undefined), @memo() content: ((()=> void) | undefined)): void { + if (({let gensym___45519047 = initializers; + (((gensym___45519047) == (null)) ? undefined : gensym___45519047.aaController)})) { + this.__backing_aaController = ((({let gensym___180078470 = initializers; + (((gensym___180078470) == (null)) ? undefined : gensym___180078470.aaController)})) ?? (undefined)); + } + this.__backing_text = STATE_MGMT_FACTORY.makeState(this, "text", ((({let gensym___217676902 = initializers; + (((gensym___217676902) == (null)) ? undefined : gensym___217676902.text)})) ?? ("text"))); + this.__backing_hh = STATE_MGMT_FACTORY.makeState(this, "hh", ((({let gensym___112288773 = initializers; + (((gensym___112288773) == (null)) ? undefined : gensym___112288773.hh)})) ?? ("nihao"))); + } + + public __updateStruct(initializers: (__Options_CustomDialogExample | undefined)): void {} + + private __backing_aaController?: (CustomDialogController | undefined); + + public get aaController(): (CustomDialogController | undefined) { + return (this.__backing_aaController as (CustomDialogController | undefined)); + } + + public set aaController(value: (CustomDialogController | undefined)) { + this.__backing_aaController = value; + } + + private __backing_text?: IStateDecoratedVariable; + + public get text(): string { + return this.__backing_text!.get(); + } + + public set text(value: string) { + this.__backing_text!.set(value); + } + + private __backing_hh?: IStateDecoratedVariable; + + public get hh(): string { + return this.__backing_hh!.get(); + } + + public set hh(value: string) { + this.__backing_hh!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, "CustomDialog One", undefined, undefined); + })); + } + + public constructor() {} + + public __setDialogController__(controller: CustomDialogController): void { + this.__backing_aaController = controller; + } +} + +@Component() final struct CustomDialogUser extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomDialogUser | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_CustomDialogUser | undefined)): void {} + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + let dialogController: (CustomDialogController | undefined) = ({let gensym___220374545: Any; + gensym___220374545 = new CustomDialogController({ + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___220374545 as CustomDialogController)); + return instance; + }), {}, undefined); + }), + baseComponent: this, + }) + (gensym___220374545 as CustomDialogController)}); + })).backgroundColor(0x317aff); + return; + }), "click me", undefined, undefined); + })); + } + + public constructor() {} + +} + +@CustomDialog() export interface __Options_CustomDialogExample { + set aaController(aaController: ((CustomDialogController | undefined) | undefined)) + + get aaController(): ((CustomDialogController | undefined) | undefined) + set text(text: (string | undefined)) + + get text(): (string | undefined) + set __backing_text(__backing_text: (IStateDecoratedVariable | undefined)) + + get __backing_text(): (IStateDecoratedVariable | undefined) + set hh(hh: (string | undefined)) + + get hh(): (string | undefined) + set __backing_hh(__backing_hh: (IStateDecoratedVariable | undefined)) + + get __backing_hh(): (IStateDecoratedVariable | undefined) + +} + +@Component() export interface __Options_CustomDialogUser { + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test CustomDialogController in build', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/controller-in-method.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/controller-in-method.test.ts new file mode 100644 index 000000000..a5ae0dbe2 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/controller-in-method.test.ts @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'decorators/custom-dialog'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'controller-in-method.ets'), +]; + +const pluginTester = new PluginTester('test CutomDialogController assignment in method', buildConfig); + +const parsedTransform: Plugins = { + name: 'controller-in-method', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; +import { BaseCustomDialog as BaseCustomDialog } from "arkui.component.customComponent"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, CustomDialog as CustomDialog, CustomDialogController as CustomDialogController } from "@ohos.arkui.component"; +import hilog from "@ohos.hilog"; + +function main() {} + +@CustomDialog() final struct CustomDialogExample extends BaseCustomDialog { + public __initializeStruct(initializers: (__Options_CustomDialogExample | undefined), @memo() content: ((()=> void) | undefined)): void { + if (({let gensym___45519047 = initializers; + (((gensym___45519047) == (null)) ? undefined : gensym___45519047.aaController)})) { + this.__backing_aaController = ((({let gensym___180078470 = initializers; + (((gensym___180078470) == (null)) ? undefined : gensym___180078470.aaController)})) ?? (undefined)); + } + } + + public __updateStruct(initializers: (__Options_CustomDialogExample | undefined)): void {} + + private __backing_aaController?: (CustomDialogController | undefined); + + public get aaController(): (CustomDialogController | undefined) { + return (this.__backing_aaController as (CustomDialogController | undefined)); + } + + public set aaController(value: (CustomDialogController | undefined)) { + this.__backing_aaController = value; + } + + @memo() public build() {} + + public constructor() {} + + public __setDialogController__(controller: CustomDialogController): void { + this.__backing_aaController = controller; + } +} + +@Component() final struct CustomDialogUser extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomDialogUser | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_dialogController = ((({let gensym___95501822 = initializers; + (((gensym___95501822) == (null)) ? undefined : gensym___95501822.dialogController)})) ?? (({let gensym___46528967: Any; + gensym___46528967 = new CustomDialogController({ + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___46528967 as CustomDialogController)); + return instance; + }), {}, undefined); + }), + baseComponent: this, + }) + (gensym___46528967 as CustomDialogController)}))); + } + + public __updateStruct(initializers: (__Options_CustomDialogUser | undefined)): void {} + + private __backing_dialogController?: (CustomDialogController | null); + + public get dialogController(): (CustomDialogController | null) { + return (this.__backing_dialogController as (CustomDialogController | null)); + } + + public set dialogController(value: (CustomDialogController | null)) { + this.__backing_dialogController = value; + } + + public updateController1() { + this.dialogController = ({let gensym___17371929: Any; + gensym___17371929 = new CustomDialogController({ + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___17371929 as CustomDialogController)); + return instance; + }), {}, undefined); + }), + autoCancel: true, + baseComponent: this, + }) + (gensym___17371929 as CustomDialogController)}); + } + + public updateController2() { + let temp = ({let gensym___90667230: Any; + gensym___90667230 = new CustomDialogController({ + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___90667230 as CustomDialogController)); + return instance; + }), {}, undefined); + }), + autoCancel: true, + baseComponent: this, + }) + (gensym___90667230 as CustomDialogController)}); + this.dialogController = temp; + } + + @memo() public build() {} + + public constructor() {} + +} + +@CustomDialog() export interface __Options_CustomDialogExample { + set aaController(aaController: ((CustomDialogController | undefined) | undefined)) + + get aaController(): ((CustomDialogController | undefined) | undefined) + +} + +@Component() export interface __Options_CustomDialogUser { + set dialogController(dialogController: ((CustomDialogController | null) | undefined)) + + get dialogController(): ((CustomDialogController | null) | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test CutomDialogController assignment in method', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/declare-custom-dialog.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/declare-custom-dialog.test.ts new file mode 100644 index 000000000..bbbd0eb25 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/declare-custom-dialog.test.ts @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'decorators/custom-dialog'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'declare-custom-dialog.ets'), +]; + +const pluginTester = new PluginTester('test declared struct @CustomDialog transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +}; + +const expectedCheckedScript: string = ` +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { BaseCustomDialog as BaseCustomDialog } from "arkui.component.customComponent"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { State as State } from "@ohos.arkui.stateManagement"; +import { CustomDialog as CustomDialog, CustomDialogController as CustomDialogController, ComponentV2 as ComponentV2, Component as Component, Builder as Builder } from "@ohos.arkui.component"; + +function main() {} + +@CustomDialog() export declare final struct CustomDialogExample extends BaseCustomDialog { + public aaController?: (CustomDialogController | undefined); + + @State() public text: string; + + public hh: string; + + @Builder() @memo() public build(): void + + public constructor() {} + + public static _buildCompatibleNode(options: __Options_CustomDialogExample): void + +} + +@Component() final struct CustomDialogUserV1 extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomDialogUserV1 | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_dialogController = ((({let gensym___51459619 = initializers; + (((gensym___51459619) == (null)) ? undefined : gensym___51459619.dialogController)})) ?? (({let gensym___203542966: Any; + gensym___203542966 = new CustomDialogController({ + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___203542966 as CustomDialogController)); + return instance; + }), undefined, undefined); + }), + baseComponent: this, + }) + (gensym___203542966 as CustomDialogController)}))); + } + + public __updateStruct(initializers: (__Options_CustomDialogUserV1 | undefined)): void {} + + private __backing_dialogController?: (CustomDialogController | null); + + public get dialogController(): (CustomDialogController | null) { + return (this.__backing_dialogController as (CustomDialogController | null)); + } + + public set dialogController(value: (CustomDialogController | null)) { + this.__backing_dialogController = value; + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() final struct CustomDialogUserV2 extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_CustomDialogUserV2 | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_dialogController = ((({let gensym___176924847 = initializers; + (((gensym___176924847) == (null)) ? undefined : gensym___176924847.dialogController)})) ?? (({let gensym___46528967: Any; + gensym___46528967 = new CustomDialogController({ + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___46528967 as CustomDialogController)); + return instance; + }), undefined, undefined); + }), + baseComponent: this, + }) + (gensym___46528967 as CustomDialogController)}))); + } + + public __updateStruct(initializers: (__Options_CustomDialogUserV2 | undefined)): void {} + + private __backing_dialogController?: (CustomDialogController | null); + + public get dialogController(): (CustomDialogController | null) { + return (this.__backing_dialogController as (CustomDialogController | null)); + } + + public set dialogController(value: (CustomDialogController | null)) { + this.__backing_dialogController = value; + } + + @memo() public build() {} + + public constructor() {} + +} + +@CustomDialog() export declare interface __Options_CustomDialogExample { + set aaController(aaController: ((CustomDialogController | undefined) | undefined)) + + get aaController(): ((CustomDialogController | undefined) | undefined) + set text(text: (string | undefined)) + + get text(): (string | undefined) + set __backing_text(__backing_text: (IStateDecoratedVariable | undefined)) + + get __backing_text(): (IStateDecoratedVariable | undefined) + set hh(hh: (string | undefined)) + + get hh(): (string | undefined) + +} + +@Component() export interface __Options_CustomDialogUserV1 { + set dialogController(dialogController: ((CustomDialogController | null) | undefined)) + + get dialogController(): ((CustomDialogController | null) | undefined) + +} + +@ComponentV2() export interface __Options_CustomDialogUserV2 { + set dialogController(dialogController: ((CustomDialogController | null) | undefined)) + + get dialogController(): ((CustomDialogController | null) | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test declared struct @CustomDialog transformation', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); \ No newline at end of file diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/extends-dialog-controller.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/extends-dialog-controller.test.ts new file mode 100644 index 000000000..f7ab1a6ec --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/extends-dialog-controller.test.ts @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'decorators/custom-dialog'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'extends-dialog-controller.ets'), +]; + +const pluginTester = new PluginTester('test extends class of CutomDialogController', buildConfig); + +const parsedTransform: Plugins = { + name: 'extends-dialog-controller', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { BaseCustomDialog as BaseCustomDialog } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Text as Text, Column as Column, Component as Component, Entry as Entry, Button as Button, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { CustomDialog as CustomDialog, CustomDialogController as CustomDialogController, CustomDialogControllerOptions as CustomDialogControllerOptions } from "@kit.ArkUI"; + +function main() {} + +@CustomDialog() final struct CustomDialogExample extends BaseCustomDialog { + public __initializeStruct(initializers: (__Options_CustomDialogExample | undefined), @memo() content: ((()=> void) | undefined)): void { + if (({let gensym___45519047 = initializers; + (((gensym___45519047) == (null)) ? undefined : gensym___45519047.aaController)})) { + this.__backing_aaController = ((({let gensym___180078470 = initializers; + (((gensym___180078470) == (null)) ? undefined : gensym___180078470.aaController)})) ?? (undefined)); + } + } + + public __updateStruct(initializers: (__Options_CustomDialogExample | undefined)): void {} + + private __backing_aaController?: (CustomDialogController | undefined); + + public get aaController(): (CustomDialogController | undefined) { + return (this.__backing_aaController as (CustomDialogController | undefined)); + } + + public set aaController(value: (CustomDialogController | undefined)) { + this.__backing_aaController = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => {})); + } + + public constructor() {} + + public __setDialogController__(controller: CustomDialogController): void { + this.__backing_aaController = controller; + } +} + +class DialogControllerV2 extends CustomDialogController { + public options: CustomDialogControllerOptions; + + public constructor(options: CustomDialogControllerOptions) { + super(options); + this.options = options; + } + +} + +class DialogControllerV3 extends DialogControllerV2 { + public options: CustomDialogControllerOptions; + + public constructor(options: CustomDialogControllerOptions) { + super(options); + this.options = options; + } + +} + +@Component() final struct CustomDialogUser extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomDialogUser | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_dialogController = ((({let gensym___176924847 = initializers; + (((gensym___176924847) == (null)) ? undefined : gensym___176924847.dialogController)})) ?? ((({let gensym___46528967: Any; + gensym___46528967 = new CustomDialogController(({ + gridCount: 4, + showInSubWindow: true, + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___46528967 as CustomDialogController)); + return instance; + }), undefined, undefined); + }), + baseComponent: this, + } as CustomDialogControllerOptions)) + (gensym___46528967 as CustomDialogController)}) as DialogControllerV3))); + } + + public __updateStruct(initializers: (__Options_CustomDialogUser | undefined)): void {} + + private __backing_dialogController?: (CustomDialogController | null); + + public get dialogController(): (CustomDialogController | null) { + return (this.__backing_dialogController as (CustomDialogController | null)); + } + + public set dialogController(value: (CustomDialogController | null)) { + this.__backing_dialogController = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + if (((this.dialogController) != (null))) { + this.dialogController!.open(); + } + })); + return; + }), "click me", undefined, undefined); + })); + } + + public constructor() {} + +} + +@CustomDialog() export interface __Options_CustomDialogExample { + set aaController(aaController: ((CustomDialogController | undefined) | undefined)) + + get aaController(): ((CustomDialogController | undefined) | undefined) + +} + +@Component() export interface __Options_CustomDialogUser { + set dialogController(dialogController: ((CustomDialogController | null) | undefined)) + + get dialogController(): ((CustomDialogController | null) | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test extends class of CutomDialogController', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/decorator-no-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/decorator-no-type.test.ts new file mode 100644 index 000000000..f726ecd68 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/decorator-no-type.test.ts @@ -0,0 +1,872 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'decorator-no-type.ets'), +]; + +const pluginTester = new PluginTester('test no typeAnnotation variables', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { IConsumerDecoratedVariable as IConsumerDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IProviderDecoratedVariable as IProviderDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IParamDecoratedVariable as IParamDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IParamOnceDecoratedVariable as IParamOnceDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { BaseCustomDialog as BaseCustomDialog } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, ComponentV2 as ComponentV2, CustomDialog as CustomDialog } from "@ohos.arkui.component"; + +import { State as State, Prop as Prop, Provide as Provide, Event as Event, Local as Local, Param as Param } from "@ohos.arkui.stateManagement"; + +import { Provider as Provider, Consumer as Consumer, Once as Once, Observed as Observed, ObservedV2 as ObservedV2, Trace as Trace, Track as Track } from "@ohos.arkui.stateManagement"; + +function main() {} + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class StateType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: StateType = new StateType(0, 0); + + public static readonly TYPE2: StateType = new StateType(1, 1); + + public static readonly TYPE3: StateType = new StateType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: StateType[] = [StateType.TYPE1, StateType.TYPE2, StateType.TYPE3]; + + public getName(): String { + return StateType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): StateType { + for (let i = 0;((i) < (StateType.#NamesArray.length));(++i)) { + if (((name) == (StateType.#NamesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant StateType.") + (name))); + } + + public static fromValue(value: int): StateType { + for (let i = 0;((i) < (StateType.#ValuesArray.length));(++i)) { + if (((value) == (StateType.#ValuesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum StateType with value ") + (value))); + } + + public valueOf(): int { + return StateType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return StateType.#StringValuesArray[this.#ordinal]; + } + + public static values(): StateType[] { + return StateType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: StateType): String { + return e.getName(); + } + +} + +@ObservedV2() class OO implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"hi"}) private __backing_hi: "150" = "150"; + + @JSONStringifyIgnore() private __meta_hi: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get hi(): "150" { + this.conditionalAddRef(this.__meta_hi); + return UIUtils.makeObserved(this.__backing_hi); + } + + public set hi(newValue: "150") { + if (((this.__backing_hi) !== (newValue))) { + this.__backing_hi = newValue; + this.__meta_hi.fireChange(); + this.executeOnSubscribingWatches("hi"); + } + } + + public constructor() {} + +} + +@Observed() class RR implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONRename({newName:"hi"}) private __backing_hi: "150" = "150"; + + @JSONStringifyIgnore() private __meta_hi: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + + public get hi(): "150" { + this.conditionalAddRef(this.__meta_hi); + return this.__backing_hi; + } + + public set hi(newValue: "150") { + if (((this.__backing_hi) !== (newValue))) { + this.__backing_hi = newValue; + this.__meta_hi.fireChange(); + this.executeOnSubscribingWatches("hi"); + } + } + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_stateVar1 = STATE_MGMT_FACTORY.makeState(this, "stateVar1", (((({let gensym___213853607 = initializers; + (((gensym___213853607) == (null)) ? undefined : gensym___213853607.stateVar1)})) ?? (new Per(6))) as Per)); + this.__backing_stateVar2 = STATE_MGMT_FACTORY.makeProp>(this, "stateVar2", (((({let gensym___113574154 = initializers; + (((gensym___113574154) == (null)) ? undefined : gensym___113574154.stateVar2)})) ?? (new Array(3, 6, 8))) as Array)); + this.__backing_stateVar3 = STATE_MGMT_FACTORY.makeProvide(this, "stateVar3", "stateVar3", (((({let gensym___120612294 = initializers; + (((gensym___120612294) == (null)) ? undefined : gensym___120612294.stateVar3)})) ?? (StateType.TYPE3)) as StateType), false); + this.__backing_stateVar8 = ((({let gensym___188075012 = initializers; + (((gensym___188075012) == (null)) ? undefined : gensym___188075012.stateVar8)})) ?? (((sr: string) => {}))); + this.__backing_stateVar9 = ((({let gensym___53672736 = initializers; + (((gensym___53672736) == (null)) ? undefined : gensym___53672736.stateVar9)})) ?? (new Date("2025-4-23"))); + this.__backing_stateVar11113 = STATE_MGMT_FACTORY.makeProvide(this, "stateVar11113", "me0", (((({let gensym___105441066 = initializers; + (((gensym___105441066) == (null)) ? undefined : gensym___105441066.stateVar11113)})) ?? (true)) as Boolean), false); + this.__backing_stateVar11114 = STATE_MGMT_FACTORY.makeProvide(this, "stateVar11114", "stateVar11114", (((({let gensym___141950305 = initializers; + (((gensym___141950305) == (null)) ? undefined : gensym___141950305.stateVar11114)})) ?? (undefined)) as undefined), false); + this.__backing_stateVar11115 = STATE_MGMT_FACTORY.makeState(this, "stateVar11115", (((({let gensym___159362057 = initializers; + (((gensym___159362057) == (null)) ? undefined : gensym___159362057.stateVar11115)})) ?? (null)) as null)); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void { + if (((({let gensym___130780487 = initializers; + (((gensym___130780487) == (null)) ? undefined : gensym___130780487.stateVar2)})) !== (undefined))) { + this.__backing_stateVar2!.update((initializers!.stateVar2 as Array)); + } + } + + private __backing_stateVar1?: IStateDecoratedVariable; + + public get stateVar1(): Per { + return this.__backing_stateVar1!.get(); + } + + public set stateVar1(value: Per) { + this.__backing_stateVar1!.set(value); + } + + private __backing_stateVar2?: IPropDecoratedVariable>; + + public get stateVar2(): Array { + return this.__backing_stateVar2!.get(); + } + + public set stateVar2(value: Array) { + this.__backing_stateVar2!.set(value); + } + + private __backing_stateVar3?: IProvideDecoratedVariable; + + public get stateVar3(): StateType { + return this.__backing_stateVar3!.get(); + } + + public set stateVar3(value: StateType) { + this.__backing_stateVar3!.set(value); + } + + private __backing_stateVar8?: Any; + + public get stateVar8(): (sr: String) => void { + return (this.__backing_stateVar8 as (sr: String) => void); + } + + public set stateVar8(value: (sr: String) => void) { + this.__backing_stateVar8 = value; + } + + private __backing_stateVar9?: Any; + + public get stateVar9(): Date { + return (this.__backing_stateVar9 as Date); + } + + public set stateVar9(value: Date) { + this.__backing_stateVar9 = value; + } + + private __backing_stateVar11113?: IProvideDecoratedVariable; + + public get stateVar11113(): Boolean { + return this.__backing_stateVar11113!.get(); + } + + public set stateVar11113(value: Boolean) { + this.__backing_stateVar11113!.set(value); + } + + private __backing_stateVar11114?: IProvideDecoratedVariable; + + public get stateVar11114(): undefined { + return this.__backing_stateVar11114!.get(); + } + + public set stateVar11114(value: undefined) { + this.__backing_stateVar11114!.set(value); + } + + private __backing_stateVar11115?: IStateDecoratedVariable; + + public get stateVar11115(): null { + return this.__backing_stateVar11115!.get(); + } + + public set stateVar11115(value: null) { + this.__backing_stateVar11115!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() final struct V2Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_V2Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_stateVar4 = STATE_MGMT_FACTORY.makeParamOnce>(this, "stateVar4", (((({let gensym___73118717 = initializers; + (((gensym___73118717) == (null)) ? undefined : gensym___73118717.stateVar4)})) ?? (new Set(new Array("aa", "bb")))) as Set)); + this.__backing_stateVar5 = STATE_MGMT_FACTORY.makeLocal>(this, "stateVar5", [true, false]); + this.__backing_stateVar6 = STATE_MGMT_FACTORY.makeLocal>(this, "stateVar6", new Array(new Per(7), new Per(11))); + this.__backing_stateVar7 = ((({let gensym___57869708 = initializers; + (((gensym___57869708) == (null)) ? undefined : gensym___57869708.stateVar7)})) ?? ([new Per(7), new Per(11)])); + this.__backing_stateVar8 = ((({let gensym___248714217 = initializers; + (((gensym___248714217) == (null)) ? undefined : gensym___248714217.stateVar8)})) ?? (((sr: string) => {}))); + this.__backing_stateVar9 = STATE_MGMT_FACTORY.makeParam(this, "stateVar9", (((({let gensym___78493121 = initializers; + (((gensym___78493121) == (null)) ? undefined : gensym___78493121.stateVar9)})) ?? (new Date("2025-4-23"))) as Date)); + this.__backing_stateVar10 = STATE_MGMT_FACTORY.makeParam>(this, "stateVar10", (((({let gensym___142105696 = initializers; + (((gensym___142105696) == (null)) ? undefined : gensym___142105696.stateVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]]))) as Map)); + this.__backing_stateVar11 = STATE_MGMT_FACTORY.makeParamOnce(this, "stateVar11", (((({let gensym___198110537 = initializers; + (((gensym___198110537) == (null)) ? undefined : gensym___198110537.stateVar11)})) ?? (0.0)) as Double)); + this.__backing_stateVar12 = STATE_MGMT_FACTORY.makeProvider(this, "stateVar12", "stateVar12", new Per(6)); + this.__backing_stateVar11111 = STATE_MGMT_FACTORY.makeConsumer<"stateVar1">(this, "stateVar11111", "stateVar11111", "stateVar1"); + this.__backing_stateVar11188 = STATE_MGMT_FACTORY.makeProvider<"">(this, "stateVar11188", "var", ""); + this.__backing_stateVar11112 = STATE_MGMT_FACTORY.makeConsumer(this, "stateVar11112", "nihao", 50); + } + + public __updateStruct(initializers: (__Options_V2Parent | undefined)): void { + if (((({let gensym___68371156 = initializers; + (((gensym___68371156) == (null)) ? undefined : gensym___68371156.stateVar9)})) !== (undefined))) { + this.__backing_stateVar9!.update((initializers!.stateVar9 as Date)); + } + if (((({let gensym___20754573 = initializers; + (((gensym___20754573) == (null)) ? undefined : gensym___20754573.stateVar10)})) !== (undefined))) { + this.__backing_stateVar10!.update((initializers!.stateVar10 as Map)); + } + } + + private __backing_stateVar4?: IParamOnceDecoratedVariable>; + + public get stateVar4(): Set { + return this.__backing_stateVar4!.get(); + } + + public set stateVar4(value: Set) { + this.__backing_stateVar4!.set(value); + } + + private __backing_stateVar5?: ILocalDecoratedVariable>; + + public get stateVar5(): Array { + return this.__backing_stateVar5!.get(); + } + + public set stateVar5(value: Array) { + this.__backing_stateVar5!.set(value); + } + + private __backing_stateVar6?: ILocalDecoratedVariable>; + + public get stateVar6(): Array { + return this.__backing_stateVar6!.get(); + } + + public set stateVar6(value: Array) { + this.__backing_stateVar6!.set(value); + } + + private __backing_stateVar7?: Any; + + public get stateVar7(): Array { + return (this.__backing_stateVar7 as Array); + } + + public set stateVar7(value: Array) { + this.__backing_stateVar7 = value; + } + + private __backing_stateVar8?: Any; + + public get stateVar8(): (sr: String) => void { + return (this.__backing_stateVar8 as (sr: String) => void); + } + + public set stateVar8(value: (sr: String) => void) { + this.__backing_stateVar8 = value; + } + + private __backing_stateVar9?: IParamDecoratedVariable; + + public get stateVar9(): Date { + return this.__backing_stateVar9!.get(); + } + + private __backing_stateVar10?: IParamDecoratedVariable>; + + public get stateVar10(): Map { + return this.__backing_stateVar10!.get(); + } + + private __backing_stateVar11?: IParamOnceDecoratedVariable; + + public get stateVar11(): Double { + return this.__backing_stateVar11!.get(); + } + + public set stateVar11(value: Double) { + this.__backing_stateVar11!.set(value); + } + + private __backing_stateVar12?: IProviderDecoratedVariable; + + public get stateVar12(): Per { + return this.__backing_stateVar12!.get(); + } + + public set stateVar12(value: Per) { + this.__backing_stateVar12!.set(value); + } + + private __backing_stateVar11111?: IConsumerDecoratedVariable<"stateVar1">; + + public get stateVar11111(): "stateVar1" { + return this.__backing_stateVar11111!.get(); + } + + public set stateVar11111(value: "stateVar1") { + this.__backing_stateVar11111!.set(value); + } + + private __backing_stateVar11188?: IProviderDecoratedVariable<"">; + + public get stateVar11188(): "" { + return this.__backing_stateVar11188!.get(); + } + + public set stateVar11188(value: "") { + this.__backing_stateVar11188!.set(value); + } + + private __backing_stateVar11112?: IConsumerDecoratedVariable; + + public get stateVar11112(): Int { + return this.__backing_stateVar11112!.get(); + } + + public set stateVar11112(value: Int) { + this.__backing_stateVar11112!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@CustomDialog() final struct CC extends BaseCustomDialog { + public __initializeStruct(initializers: (__Options_CC | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_stateVar4 = STATE_MGMT_FACTORY.makeParamOnce>(this, "stateVar4", (((({let gensym___177598336 = initializers; + (((gensym___177598336) == (null)) ? undefined : gensym___177598336.stateVar4)})) ?? (new Set(new Array("aa", "bb")))) as Set)); + this.__backing_stateVar5 = STATE_MGMT_FACTORY.makeLocal>(this, "stateVar5", [true, false]); + this.__backing_stateVar6 = STATE_MGMT_FACTORY.makeLocal>(this, "stateVar6", new Array(new Per(7), new Per(11))); + this.__backing_stateVar7 = ((({let gensym___136470640 = initializers; + (((gensym___136470640) == (null)) ? undefined : gensym___136470640.stateVar7)})) ?? ([new Per(7), new Per(11)])); + this.__backing_stateVar8 = ((({let gensym___59026893 = initializers; + (((gensym___59026893) == (null)) ? undefined : gensym___59026893.stateVar8)})) ?? (((sr: string) => {}))); + this.__backing_stateVar9 = STATE_MGMT_FACTORY.makeParam(this, "stateVar9", (((({let gensym___121624319 = initializers; + (((gensym___121624319) == (null)) ? undefined : gensym___121624319.stateVar9)})) ?? (new Date("2025-4-23"))) as Date)); + this.__backing_stateVar10 = STATE_MGMT_FACTORY.makeParam>(this, "stateVar10", (((({let gensym___40863473 = initializers; + (((gensym___40863473) == (null)) ? undefined : gensym___40863473.stateVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]]))) as Map)); + this.__backing_stateVar11 = STATE_MGMT_FACTORY.makeParamOnce(this, "stateVar11", (((({let gensym___101188994 = initializers; + (((gensym___101188994) == (null)) ? undefined : gensym___101188994.stateVar11)})) ?? (0.0)) as Double)); + this.__backing_stateVar12 = STATE_MGMT_FACTORY.makeProvider(this, "stateVar12", "stateVar12", new Per(6)); + this.__backing_stateVar11111 = STATE_MGMT_FACTORY.makeConsumer<"stateVar1">(this, "stateVar11111", "stateVar11111", "stateVar1"); + this.__backing_stateVar11188 = STATE_MGMT_FACTORY.makeProvider<"">(this, "stateVar11188", "var", ""); + this.__backing_stateVar11112 = STATE_MGMT_FACTORY.makeConsumer(this, "stateVar11112", "nihao", 50); + } + + public __updateStruct(initializers: (__Options_CC | undefined)): void { + if (((({let gensym___177096870 = initializers; + (((gensym___177096870) == (null)) ? undefined : gensym___177096870.stateVar9)})) !== (undefined))) { + this.__backing_stateVar9!.update((initializers!.stateVar9 as Date)); + } + if (((({let gensym___35982320 = initializers; + (((gensym___35982320) == (null)) ? undefined : gensym___35982320.stateVar10)})) !== (undefined))) { + this.__backing_stateVar10!.update((initializers!.stateVar10 as Map)); + } + } + + private __backing_stateVar4?: IParamOnceDecoratedVariable>; + + public get stateVar4(): Set { + return this.__backing_stateVar4!.get(); + } + + public set stateVar4(value: Set) { + this.__backing_stateVar4!.set(value); + } + + private __backing_stateVar5?: ILocalDecoratedVariable>; + + public get stateVar5(): Array { + return this.__backing_stateVar5!.get(); + } + + public set stateVar5(value: Array) { + this.__backing_stateVar5!.set(value); + } + + private __backing_stateVar6?: ILocalDecoratedVariable>; + + public get stateVar6(): Array { + return this.__backing_stateVar6!.get(); + } + + public set stateVar6(value: Array) { + this.__backing_stateVar6!.set(value); + } + + private __backing_stateVar7?: Any; + + public get stateVar7(): Array { + return (this.__backing_stateVar7 as Array); + } + + public set stateVar7(value: Array) { + this.__backing_stateVar7 = value; + } + + private __backing_stateVar8?: Any; + + public get stateVar8(): (sr: String) => void { + return (this.__backing_stateVar8 as (sr: String) => void); + } + + public set stateVar8(value: (sr: String) => void) { + this.__backing_stateVar8 = value; + } + + private __backing_stateVar9?: IParamDecoratedVariable; + + public get stateVar9(): Date { + return this.__backing_stateVar9!.get(); + } + + private __backing_stateVar10?: IParamDecoratedVariable>; + + public get stateVar10(): Map { + return this.__backing_stateVar10!.get(); + } + + private __backing_stateVar11?: IParamOnceDecoratedVariable; + + public get stateVar11(): Double { + return this.__backing_stateVar11!.get(); + } + + public set stateVar11(value: Double) { + this.__backing_stateVar11!.set(value); + } + + private __backing_stateVar12?: IProviderDecoratedVariable; + + public get stateVar12(): Per { + return this.__backing_stateVar12!.get(); + } + + public set stateVar12(value: Per) { + this.__backing_stateVar12!.set(value); + } + + private __backing_stateVar11111?: IConsumerDecoratedVariable<"stateVar1">; + + public get stateVar11111(): "stateVar1" { + return this.__backing_stateVar11111!.get(); + } + + public set stateVar11111(value: "stateVar1") { + this.__backing_stateVar11111!.set(value); + } + + private __backing_stateVar11188?: IProviderDecoratedVariable<"">; + + public get stateVar11188(): "" { + return this.__backing_stateVar11188!.get(); + } + + public set stateVar11188(value: "") { + this.__backing_stateVar11188!.set(value); + } + + private __backing_stateVar11112?: IConsumerDecoratedVariable; + + public get stateVar11112(): Int { + return this.__backing_stateVar11112!.get(); + } + + public set stateVar11112(value: Int) { + this.__backing_stateVar11112!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_Parent { + set stateVar1(stateVar1: (Any | undefined)) + + get stateVar1(): (Any | undefined) + set __backing_stateVar1(__backing_stateVar1: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar1(): (IStateDecoratedVariable | undefined) + set stateVar2(stateVar2: (Any | undefined)) + + get stateVar2(): (Any | undefined) + set __backing_stateVar2(__backing_stateVar2: (IPropDecoratedVariable | undefined)) + + get __backing_stateVar2(): (IPropDecoratedVariable | undefined) + set stateVar3(stateVar3: (Any | undefined)) + + get stateVar3(): (Any | undefined) + set __backing_stateVar3(__backing_stateVar3: (IProvideDecoratedVariable | undefined)) + + get __backing_stateVar3(): (IProvideDecoratedVariable | undefined) + set stateVar8(stateVar8: (Any | undefined)) + + get stateVar8(): (Any | undefined) + set stateVar9(stateVar9: (Any | undefined)) + + get stateVar9(): (Any | undefined) + set stateVar11113(stateVar11113: (Any | undefined)) + + get stateVar11113(): (Any | undefined) + set __backing_stateVar11113(__backing_stateVar11113: (IProvideDecoratedVariable | undefined)) + + get __backing_stateVar11113(): (IProvideDecoratedVariable | undefined) + set stateVar11114(stateVar11114: (Any | undefined)) + + get stateVar11114(): (Any | undefined) + set __backing_stateVar11114(__backing_stateVar11114: (IProvideDecoratedVariable | undefined)) + + get __backing_stateVar11114(): (IProvideDecoratedVariable | undefined) + set stateVar11115(stateVar11115: (Any | undefined)) + + get stateVar11115(): (Any | undefined) + set __backing_stateVar11115(__backing_stateVar11115: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar11115(): (IStateDecoratedVariable | undefined) + +} + +@ComponentV2() export interface __Options_V2Parent { + set stateVar4(stateVar4: (Any | undefined)) + + get stateVar4(): (Any | undefined) + @Param() set __backing_stateVar4(__backing_stateVar4: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_stateVar4(): (IParamOnceDecoratedVariable | undefined) + set stateVar5(stateVar5: (Any | undefined)) + + get stateVar5(): (Any | undefined) + set __backing_stateVar5(__backing_stateVar5: (ILocalDecoratedVariable | undefined)) + + get __backing_stateVar5(): (ILocalDecoratedVariable | undefined) + set stateVar6(stateVar6: (Any | undefined)) + + get stateVar6(): (Any | undefined) + set __backing_stateVar6(__backing_stateVar6: (ILocalDecoratedVariable | undefined)) + + get __backing_stateVar6(): (ILocalDecoratedVariable | undefined) + set stateVar7(stateVar7: (Any | undefined)) + + get stateVar7(): (Any | undefined) + set stateVar8(stateVar8: (Any | undefined)) + + get stateVar8(): (Any | undefined) + set stateVar9(stateVar9: (Any | undefined)) + + get stateVar9(): (Any | undefined) + set __backing_stateVar9(__backing_stateVar9: (IParamDecoratedVariable | undefined)) + + get __backing_stateVar9(): (IParamDecoratedVariable | undefined) + set stateVar10(stateVar10: (Any | undefined)) + + get stateVar10(): (Any | undefined) + set __backing_stateVar10(__backing_stateVar10: (IParamDecoratedVariable | undefined)) + + get __backing_stateVar10(): (IParamDecoratedVariable | undefined) + set stateVar11(stateVar11: (Any | undefined)) + + get stateVar11(): (Any | undefined) + @Param() set __backing_stateVar11(__backing_stateVar11: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_stateVar11(): (IParamOnceDecoratedVariable | undefined) + set stateVar12(stateVar12: (Any | undefined)) + + get stateVar12(): (Any | undefined) + set __backing_stateVar12(__backing_stateVar12: (IProviderDecoratedVariable | undefined)) + + get __backing_stateVar12(): (IProviderDecoratedVariable | undefined) + set stateVar11111(stateVar11111: (Any | undefined)) + + get stateVar11111(): (Any | undefined) + set __backing_stateVar11111(__backing_stateVar11111: (IConsumerDecoratedVariable | undefined)) + + get __backing_stateVar11111(): (IConsumerDecoratedVariable | undefined) + set stateVar11188(stateVar11188: (Any | undefined)) + + get stateVar11188(): (Any | undefined) + set __backing_stateVar11188(__backing_stateVar11188: (IProviderDecoratedVariable | undefined)) + + get __backing_stateVar11188(): (IProviderDecoratedVariable | undefined) + set stateVar11112(stateVar11112: (Any | undefined)) + + get stateVar11112(): (Any | undefined) + set __backing_stateVar11112(__backing_stateVar11112: (IConsumerDecoratedVariable | undefined)) + + get __backing_stateVar11112(): (IConsumerDecoratedVariable | undefined) + +} + +@CustomDialog() export interface __Options_CC { + set stateVar4(stateVar4: (Any | undefined)) + + get stateVar4(): (Any | undefined) + @Param() set __backing_stateVar4(__backing_stateVar4: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_stateVar4(): (IParamOnceDecoratedVariable | undefined) + set stateVar5(stateVar5: (Any | undefined)) + + get stateVar5(): (Any | undefined) + set __backing_stateVar5(__backing_stateVar5: (ILocalDecoratedVariable | undefined)) + + get __backing_stateVar5(): (ILocalDecoratedVariable | undefined) + set stateVar6(stateVar6: (Any | undefined)) + + get stateVar6(): (Any | undefined) + set __backing_stateVar6(__backing_stateVar6: (ILocalDecoratedVariable | undefined)) + + get __backing_stateVar6(): (ILocalDecoratedVariable | undefined) + set stateVar7(stateVar7: (Any | undefined)) + + get stateVar7(): (Any | undefined) + set stateVar8(stateVar8: (Any | undefined)) + + get stateVar8(): (Any | undefined) + set stateVar9(stateVar9: (Any | undefined)) + + get stateVar9(): (Any | undefined) + set __backing_stateVar9(__backing_stateVar9: (IParamDecoratedVariable | undefined)) + + get __backing_stateVar9(): (IParamDecoratedVariable | undefined) + set stateVar10(stateVar10: (Any | undefined)) + + get stateVar10(): (Any | undefined) + set __backing_stateVar10(__backing_stateVar10: (IParamDecoratedVariable | undefined)) + + get __backing_stateVar10(): (IParamDecoratedVariable | undefined) + set stateVar11(stateVar11: (Any | undefined)) + + get stateVar11(): (Any | undefined) + @Param() set __backing_stateVar11(__backing_stateVar11: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_stateVar11(): (IParamOnceDecoratedVariable | undefined) + set stateVar12(stateVar12: (Any | undefined)) + + get stateVar12(): (Any | undefined) + set __backing_stateVar12(__backing_stateVar12: (IProviderDecoratedVariable | undefined)) + + get __backing_stateVar12(): (IProviderDecoratedVariable | undefined) + set stateVar11111(stateVar11111: (Any | undefined)) + + get stateVar11111(): (Any | undefined) + set __backing_stateVar11111(__backing_stateVar11111: (IConsumerDecoratedVariable | undefined)) + + get __backing_stateVar11111(): (IConsumerDecoratedVariable | undefined) + set stateVar11188(stateVar11188: (Any | undefined)) + + get stateVar11188(): (Any | undefined) + set __backing_stateVar11188(__backing_stateVar11188: (IProviderDecoratedVariable | undefined)) + + get __backing_stateVar11188(): (IProviderDecoratedVariable | undefined) + set stateVar11112(stateVar11112: (Any | undefined)) + + get stateVar11112(): (Any | undefined) + set __backing_stateVar11112(__backing_stateVar11112: (IConsumerDecoratedVariable | undefined)) + + get __backing_stateVar11112(): (IConsumerDecoratedVariable | undefined) + +} + +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test no typeAnnotation variables', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/event/event-initialize.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/event/event-initialize.test.ts new file mode 100644 index 000000000..e5e8c36a3 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/event/event-initialize.test.ts @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/event'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'event-initialize.ets'), +]; + +const pluginTester = new PluginTester('test @Event decorator transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedParsedScript: string = ` +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Text as Text } from "@ohos.arkui.component"; + +import { Event as Event, Param as Param, Local as Local } from "@ohos.arkui.stateManagement"; + +@ComponentV2() final struct Child extends CustomComponentV2 { + @Param() public index: number = 0; + + @Event() public changeIndex!: ((val: number)=> void); + + @Event() public testEvent!: ((val: number)=> number); + + @Event() public testEvent2: ((val: number)=> number) = ((val: number) => { + return val; + }); + + public build() { + Column(){ + Text(\`Child index: \${this.index}\`).onClick(((e) => { + this.changeIndex(20); + console.log(\`after changeIndex \${this.index}\`); + })); + }; + } + + public constructor() {} + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + @Local() public index: number = 0; + + public build() { + Column(){ + Child({ + index: this.index, + changeIndex: ((val: number) => { + this.index = val; + console.log(\`in changeIndex \${this.index}\`); + }), + }); + }; + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Child { + index?: number; + @Param() __backing_index?: number; + changeIndex?: ((val: number)=> void); + testEvent?: ((val: number)=> number); + testEvent2?: ((val: number)=> number); + +} + +@ComponentV2() export interface __Options_Index { + index?: number; + @Local() __backing_index?: number; + +} +`; + +const expectedCheckedScript: string = ` +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IParamDecoratedVariable as IParamDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Text as Text } from "@ohos.arkui.component"; + +import { Event as Event, Param as Param, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ComponentV2() final struct Child extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_index = STATE_MGMT_FACTORY.makeParam(this, "index", ((({let gensym___23942905 = initializers; + (((gensym___23942905) == (null)) ? undefined : gensym___23942905.index)})) ?? (0))); + this.__backing_changeIndex = ((({let gensym___204042774 = initializers; + (((gensym___204042774) == (null)) ? undefined : gensym___204042774.changeIndex)})) ?? (undefined)); + this.__backing_testEvent = ((({let gensym___124585092 = initializers; + (((gensym___124585092) == (null)) ? undefined : gensym___124585092.testEvent)})) ?? (undefined)); + this.__backing_testEvent2 = ((({let gensym___189097286 = initializers; + (((gensym___189097286) == (null)) ? undefined : gensym___189097286.testEvent2)})) ?? (((val: number) => { + return val; + }))); + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void { + if (((({let gensym___91647805 = initializers; + (((gensym___91647805) == (null)) ? undefined : gensym___91647805.index)})) !== (undefined))) { + this.__backing_index!.update((initializers!.index as number)); + } + } + + private __backing_index?: IParamDecoratedVariable; + + public get index(): number { + return this.__backing_index!.get(); + } + + private __backing_changeIndex?: ((val: number)=> void); + + public get changeIndex(): ((val: number)=> void) { + return (this.__backing_changeIndex as ((val: number)=> void)); + } + + public set changeIndex(value: ((val: number)=> void)) { + this.__backing_changeIndex = value; + } + + private __backing_testEvent?: ((val: number)=> number); + + public get testEvent(): ((val: number)=> number) { + return (this.__backing_testEvent as ((val: number)=> number)); + } + + public set testEvent(value: ((val: number)=> number)) { + this.__backing_testEvent = value; + } + + private __backing_testEvent2?: ((val: number)=> number); + + public get testEvent2(): ((val: number)=> number) { + return (this.__backing_testEvent2 as ((val: number)=> number)); + } + + public set testEvent2(value: ((val: number)=> number)) { + this.__backing_testEvent2 = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { + instance.onClick(((e) => { + this.changeIndex(20); + console.log(\`after changeIndex \${this.index}\`); + })); + return; + }), \`Child index: \${this.index}\`, undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_index = STATE_MGMT_FACTORY.makeLocal(this, "index", 0); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_index?: ILocalDecoratedVariable; + + public get index(): number { + return this.__backing_index!.get(); + } + + public set index(value: number) { + this.__backing_index!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + index: this.index, + changeIndex: ((val: number) => { + this.index = val; + console.log(\`in changeIndex \${this.index}\`); + }), + }, undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Child { + set index(index: (number | undefined)) + + get index(): (number | undefined) + set __backing_index(__backing_index: (IParamDecoratedVariable | undefined)) + + get __backing_index(): (IParamDecoratedVariable | undefined) + set changeIndex(changeIndex: (((val: number)=> void) | undefined)) + + get changeIndex(): (((val: number)=> void) | undefined) + set testEvent(testEvent: (((val: number)=> number) | undefined)) + + get testEvent(): (((val: number)=> number) | undefined) + set testEvent2(testEvent2: (((val: number)=> number) | undefined)) + + get testEvent2(): (((val: number)=> number) | undefined) + +} + +@ComponentV2() export interface __Options_Index { + set index(index: (number | undefined)) + + get index(): (number | undefined) + set __backing_index(__backing_index: (ILocalDecoratedVariable | undefined)) + + get __backing_index(): (ILocalDecoratedVariable | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test @Event decorator transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-basic-type.test.ts index 340db6a9e..2ce876529 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-basic-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-basic-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,101 +38,142 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { DecoratedV1VariableBase as DecoratedV1VariableBase } from "@ohos.arkui.stateManagement"; -import { LinkDecoratedVariable as LinkDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; + +import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { Link as Link } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class LinkParent extends CustomComponent { - public __initializeStruct(initializers: __Options_LinkParent | undefined, @memo() content: (()=> void) | undefined): void { + + +@Component() final struct LinkParent extends CustomComponent { + public __initializeStruct(initializers: (__Options_LinkParent | undefined), @memo() content: ((()=> void) | undefined)): void { if (({let gensym___11910109 = initializers; (((gensym___11910109) == (null)) ? undefined : gensym___11910109.__backing_linkVar1)})) { - this.__backing_linkVar1 = new LinkDecoratedVariable("linkVar1", initializers!.__backing_linkVar1!); + this.__backing_linkVar1 = STATE_MGMT_FACTORY.makeLink(this, "linkVar1", initializers!.__backing_linkVar1!); }; if (({let gensym___181684045 = initializers; (((gensym___181684045) == (null)) ? undefined : gensym___181684045.__backing_linkVar2)})) { - this.__backing_linkVar2 = new LinkDecoratedVariable("linkVar2", initializers!.__backing_linkVar2!); + this.__backing_linkVar2 = STATE_MGMT_FACTORY.makeLink(this, "linkVar2", initializers!.__backing_linkVar2!); }; if (({let gensym___24446313 = initializers; (((gensym___24446313) == (null)) ? undefined : gensym___24446313.__backing_linkVar3)})) { - this.__backing_linkVar3 = new LinkDecoratedVariable("linkVar3", initializers!.__backing_linkVar3!); + this.__backing_linkVar3 = STATE_MGMT_FACTORY.makeLink(this, "linkVar3", initializers!.__backing_linkVar3!); }; if (({let gensym___167989826 = initializers; (((gensym___167989826) == (null)) ? undefined : gensym___167989826.__backing_linkVar4)})) { - this.__backing_linkVar4 = new LinkDecoratedVariable("linkVar4", initializers!.__backing_linkVar4!); + this.__backing_linkVar4 = STATE_MGMT_FACTORY.makeLink(this, "linkVar4", initializers!.__backing_linkVar4!); }; if (({let gensym___157566097 = initializers; (((gensym___157566097) == (null)) ? undefined : gensym___157566097.__backing_linkVar5)})) { - this.__backing_linkVar5 = new LinkDecoratedVariable("linkVar5", initializers!.__backing_linkVar5!); + this.__backing_linkVar5 = STATE_MGMT_FACTORY.makeLink(this, "linkVar5", initializers!.__backing_linkVar5!); }; } - public __updateStruct(initializers: __Options_LinkParent | undefined): void {} - private __backing_linkVar1?: LinkDecoratedVariable; + + public __updateStruct(initializers: (__Options_LinkParent | undefined)): void {} + + private __backing_linkVar1?: ILinkDecoratedVariable; + public get linkVar1(): string { return this.__backing_linkVar1!.get(); } + public set linkVar1(value: string) { this.__backing_linkVar1!.set(value); } - private __backing_linkVar2?: LinkDecoratedVariable; + + private __backing_linkVar2?: ILinkDecoratedVariable; + public get linkVar2(): number { return this.__backing_linkVar2!.get(); } + public set linkVar2(value: number) { this.__backing_linkVar2!.set(value); } - private __backing_linkVar3?: LinkDecoratedVariable; + + private __backing_linkVar3?: ILinkDecoratedVariable; + public get linkVar3(): boolean { return this.__backing_linkVar3!.get(); } + public set linkVar3(value: boolean) { this.__backing_linkVar3!.set(value); } - private __backing_linkVar4?: LinkDecoratedVariable; + + private __backing_linkVar4?: ILinkDecoratedVariable; + public get linkVar4(): undefined { return this.__backing_linkVar4!.get(); } + public set linkVar4(value: undefined) { this.__backing_linkVar4!.set(value); } - private __backing_linkVar5?: LinkDecoratedVariable; + + private __backing_linkVar5?: ILinkDecoratedVariable; + public get linkVar5(): null { return this.__backing_linkVar5!.get(); } + public set linkVar5(value: null) { this.__backing_linkVar5!.set(value); } - @memo() public _build(@memo() style: ((instance: LinkParent)=> LinkParent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_LinkParent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_LinkParent { - set linkVar1(linkVar1: string | undefined) - get linkVar1(): string | undefined - set __backing_linkVar1(__backing_linkVar1: DecoratedV1VariableBase | undefined) - get __backing_linkVar1(): DecoratedV1VariableBase | undefined - set linkVar2(linkVar2: number | undefined) - get linkVar2(): number | undefined - set __backing_linkVar2(__backing_linkVar2: DecoratedV1VariableBase | undefined) - get __backing_linkVar2(): DecoratedV1VariableBase | undefined - set linkVar3(linkVar3: boolean | undefined) - get linkVar3(): boolean | undefined - set __backing_linkVar3(__backing_linkVar3: DecoratedV1VariableBase | undefined) - get __backing_linkVar3(): DecoratedV1VariableBase | undefined - set linkVar4(linkVar4: undefined | undefined) - get linkVar4(): undefined | undefined - set __backing_linkVar4(__backing_linkVar4: DecoratedV1VariableBase | undefined) - get __backing_linkVar4(): DecoratedV1VariableBase | undefined - set linkVar5(linkVar5: null | undefined) - get linkVar5(): null | undefined - set __backing_linkVar5(__backing_linkVar5: DecoratedV1VariableBase | undefined) - get __backing_linkVar5(): DecoratedV1VariableBase | undefined +@Retention({policy:"SOURCE"}) @interface __Link_intrinsic {} + +@Component() export interface __Options_LinkParent { + @__Link_intrinsic() set linkVar1(linkVar1: (string | undefined)) + + @__Link_intrinsic() get linkVar1(): (string | undefined) + set __backing_linkVar1(__backing_linkVar1: (LinkSourceType | undefined)) + + get __backing_linkVar1(): (LinkSourceType | undefined) + @__Link_intrinsic() set linkVar2(linkVar2: (number | undefined)) + + @__Link_intrinsic() get linkVar2(): (number | undefined) + set __backing_linkVar2(__backing_linkVar2: (LinkSourceType | undefined)) + + get __backing_linkVar2(): (LinkSourceType | undefined) + @__Link_intrinsic() set linkVar3(linkVar3: (boolean | undefined)) + + @__Link_intrinsic() get linkVar3(): (boolean | undefined) + set __backing_linkVar3(__backing_linkVar3: (LinkSourceType | undefined)) + + get __backing_linkVar3(): (LinkSourceType | undefined) + @__Link_intrinsic() set linkVar4(linkVar4: (undefined | undefined)) + + @__Link_intrinsic() get linkVar4(): (undefined | undefined) + set __backing_linkVar4(__backing_linkVar4: (LinkSourceType | undefined)) + + get __backing_linkVar4(): (LinkSourceType | undefined) + @__Link_intrinsic() set linkVar5(linkVar5: (null | undefined)) + + @__Link_intrinsic() get linkVar5(): (null | undefined) + set __backing_linkVar5(__backing_linkVar5: (LinkSourceType | undefined)) + + get __backing_linkVar5(): (LinkSourceType | undefined) + } `; @@ -141,9 +183,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test basic type @Link decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-complex-type.test.ts index 486097a84..f174a08a5 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-complex-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,41 +38,60 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { DecoratedV1VariableBase as DecoratedV1VariableBase } from "@ohos.arkui.stateManagement"; -import { LinkDecoratedVariable as LinkDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; + +import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { Link as Link } from "@ohos.arkui.stateManagement"; function main() {} class Per { public num: number; + public constructor(num: number) { this.num = num; } + } final class LinkType extends BaseEnum { private readonly #ordinal: int; + private static () {} + public constructor(ordinal: int, value: int) { super(value); this.#ordinal = ordinal; } + public static readonly TYPE1: LinkType = new LinkType(0, 0); + public static readonly TYPE2: LinkType = new LinkType(1, 1); + public static readonly TYPE3: LinkType = new LinkType(2, 3); + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + private static readonly #ValuesArray: int[] = [0, 1, 3]; + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + private static readonly #ItemsArray: LinkType[] = [LinkType.TYPE1, LinkType.TYPE2, LinkType.TYPE3]; + public getName(): String { return LinkType.#NamesArray[this.#ordinal]; } + public static getValueOf(name: String): LinkType { for (let i = 0;((i) < (LinkType.#NamesArray.length));(++i)) { if (((name) == (LinkType.#NamesArray[i]))) { @@ -89,212 +109,285 @@ final class LinkType extends BaseEnum { } throw new Error((("No enum LinkType with value ") + (value))); } + public valueOf(): int { return LinkType.#ValuesArray[this.#ordinal]; } + public toString(): String { return LinkType.#StringValuesArray[this.#ordinal]; } + public static values(): LinkType[] { return LinkType.#ItemsArray; } + public getOrdinal(): int { return this.#ordinal; } + public static $_get(e: LinkType): String { return e.getName(); } + } -@Component({freezeWhenInactive:false}) final class Parent extends CustomComponent { - public __initializeStruct(initializers: __Options_Parent | undefined, @memo() content: (()=> void) | undefined): void { +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { if (({let gensym___11910109 = initializers; (((gensym___11910109) == (null)) ? undefined : gensym___11910109.__backing_linkVar1)})) { - this.__backing_linkVar1 = new LinkDecoratedVariable("linkVar1", initializers!.__backing_linkVar1!); + this.__backing_linkVar1 = STATE_MGMT_FACTORY.makeLink(this, "linkVar1", initializers!.__backing_linkVar1!); }; if (({let gensym___181684045 = initializers; (((gensym___181684045) == (null)) ? undefined : gensym___181684045.__backing_linkVar2)})) { - this.__backing_linkVar2 = new LinkDecoratedVariable>("linkVar2", initializers!.__backing_linkVar2!); + this.__backing_linkVar2 = STATE_MGMT_FACTORY.makeLink>(this, "linkVar2", initializers!.__backing_linkVar2!); }; if (({let gensym___24446313 = initializers; (((gensym___24446313) == (null)) ? undefined : gensym___24446313.__backing_linkVar3)})) { - this.__backing_linkVar3 = new LinkDecoratedVariable("linkVar3", initializers!.__backing_linkVar3!); + this.__backing_linkVar3 = STATE_MGMT_FACTORY.makeLink(this, "linkVar3", initializers!.__backing_linkVar3!); }; if (({let gensym___167989826 = initializers; (((gensym___167989826) == (null)) ? undefined : gensym___167989826.__backing_linkVar4)})) { - this.__backing_linkVar4 = new LinkDecoratedVariable>("linkVar4", initializers!.__backing_linkVar4!); + this.__backing_linkVar4 = STATE_MGMT_FACTORY.makeLink>(this, "linkVar4", initializers!.__backing_linkVar4!); }; if (({let gensym___157566097 = initializers; (((gensym___157566097) == (null)) ? undefined : gensym___157566097.__backing_linkVar5)})) { - this.__backing_linkVar5 = new LinkDecoratedVariable>("linkVar5", initializers!.__backing_linkVar5!); + this.__backing_linkVar5 = STATE_MGMT_FACTORY.makeLink>(this, "linkVar5", initializers!.__backing_linkVar5!); }; if (({let gensym___60105491 = initializers; (((gensym___60105491) == (null)) ? undefined : gensym___60105491.__backing_linkVar6)})) { - this.__backing_linkVar6 = new LinkDecoratedVariable>("linkVar6", initializers!.__backing_linkVar6!); + this.__backing_linkVar6 = STATE_MGMT_FACTORY.makeLink>(this, "linkVar6", initializers!.__backing_linkVar6!); }; if (({let gensym___3429048 = initializers; (((gensym___3429048) == (null)) ? undefined : gensym___3429048.__backing_linkVar7)})) { - this.__backing_linkVar7 = new LinkDecoratedVariable>("linkVar7", initializers!.__backing_linkVar7!); + this.__backing_linkVar7 = STATE_MGMT_FACTORY.makeLink>(this, "linkVar7", initializers!.__backing_linkVar7!); }; if (({let gensym___139916435 = initializers; (((gensym___139916435) == (null)) ? undefined : gensym___139916435.__backing_linkVar8)})) { - this.__backing_linkVar8 = new LinkDecoratedVariable<((sr: string)=> void)>("linkVar8", initializers!.__backing_linkVar8!); + this.__backing_linkVar8 = STATE_MGMT_FACTORY.makeLink<((sr: string)=> void)>(this, "linkVar8", initializers!.__backing_linkVar8!); }; if (({let gensym___145003260 = initializers; (((gensym___145003260) == (null)) ? undefined : gensym___145003260.__backing_linkVar9)})) { - this.__backing_linkVar9 = new LinkDecoratedVariable("linkVar9", initializers!.__backing_linkVar9!); + this.__backing_linkVar9 = STATE_MGMT_FACTORY.makeLink(this, "linkVar9", initializers!.__backing_linkVar9!); }; if (({let gensym___122643185 = initializers; (((gensym___122643185) == (null)) ? undefined : gensym___122643185.__backing_linkVar10)})) { - this.__backing_linkVar10 = new LinkDecoratedVariable>("linkVar10", initializers!.__backing_linkVar10!); + this.__backing_linkVar10 = STATE_MGMT_FACTORY.makeLink>(this, "linkVar10", initializers!.__backing_linkVar10!); }; if (({let gensym___222468503 = initializers; (((gensym___222468503) == (null)) ? undefined : gensym___222468503.__backing_linkVar11)})) { - this.__backing_linkVar11 = new LinkDecoratedVariable("linkVar11", initializers!.__backing_linkVar11!); + this.__backing_linkVar11 = STATE_MGMT_FACTORY.makeLink<(string | number)>(this, "linkVar11", initializers!.__backing_linkVar11!); }; if (({let gensym___243301539 = initializers; (((gensym___243301539) == (null)) ? undefined : gensym___243301539.__backing_linkVar12)})) { - this.__backing_linkVar12 = new LinkDecoratedVariable | Per>("linkVar12", initializers!.__backing_linkVar12!); + this.__backing_linkVar12 = STATE_MGMT_FACTORY.makeLink<(Set | Per)>(this, "linkVar12", initializers!.__backing_linkVar12!); }; } - public __updateStruct(initializers: __Options_Parent | undefined): void {} - private __backing_linkVar1?: LinkDecoratedVariable; + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_linkVar1?: ILinkDecoratedVariable; + public get linkVar1(): Per { return this.__backing_linkVar1!.get(); } + public set linkVar1(value: Per) { this.__backing_linkVar1!.set(value); } - private __backing_linkVar2?: LinkDecoratedVariable>; + + private __backing_linkVar2?: ILinkDecoratedVariable>; + public get linkVar2(): Array { return this.__backing_linkVar2!.get(); } + public set linkVar2(value: Array) { this.__backing_linkVar2!.set(value); } - private __backing_linkVar3?: LinkDecoratedVariable; + + private __backing_linkVar3?: ILinkDecoratedVariable; + public get linkVar3(): LinkType { return this.__backing_linkVar3!.get(); } + public set linkVar3(value: LinkType) { this.__backing_linkVar3!.set(value); } - private __backing_linkVar4?: LinkDecoratedVariable>; + + private __backing_linkVar4?: ILinkDecoratedVariable>; + public get linkVar4(): Set { return this.__backing_linkVar4!.get(); } + public set linkVar4(value: Set) { this.__backing_linkVar4!.set(value); } - private __backing_linkVar5?: LinkDecoratedVariable>; + + private __backing_linkVar5?: ILinkDecoratedVariable>; + public get linkVar5(): Array { return this.__backing_linkVar5!.get(); } + public set linkVar5(value: Array) { this.__backing_linkVar5!.set(value); } - private __backing_linkVar6?: LinkDecoratedVariable>; + + private __backing_linkVar6?: ILinkDecoratedVariable>; + public get linkVar6(): Array { return this.__backing_linkVar6!.get(); } + public set linkVar6(value: Array) { this.__backing_linkVar6!.set(value); } - private __backing_linkVar7?: LinkDecoratedVariable>; + + private __backing_linkVar7?: ILinkDecoratedVariable>; + public get linkVar7(): Array { return this.__backing_linkVar7!.get(); } + public set linkVar7(value: Array) { this.__backing_linkVar7!.set(value); } - private __backing_linkVar8?: LinkDecoratedVariable<((sr: string)=> void)>; + + private __backing_linkVar8?: ILinkDecoratedVariable<((sr: string)=> void)>; + public get linkVar8(): ((sr: string)=> void) { return this.__backing_linkVar8!.get(); } + public set linkVar8(value: ((sr: string)=> void)) { this.__backing_linkVar8!.set(value); } - private __backing_linkVar9?: LinkDecoratedVariable; + + private __backing_linkVar9?: ILinkDecoratedVariable; + public get linkVar9(): Date { return this.__backing_linkVar9!.get(); } + public set linkVar9(value: Date) { this.__backing_linkVar9!.set(value); } - private __backing_linkVar10?: LinkDecoratedVariable>; + + private __backing_linkVar10?: ILinkDecoratedVariable>; + public get linkVar10(): Map { return this.__backing_linkVar10!.get(); } + public set linkVar10(value: Map) { this.__backing_linkVar10!.set(value); } - private __backing_linkVar11?: LinkDecoratedVariable; - public get linkVar11(): string | number { + + private __backing_linkVar11?: ILinkDecoratedVariable<(string | number)>; + + public get linkVar11(): (string | number) { return this.__backing_linkVar11!.get(); } - public set linkVar11(value: string | number) { + + public set linkVar11(value: (string | number)) { this.__backing_linkVar11!.set(value); } - private __backing_linkVar12?: LinkDecoratedVariable | Per>; - public get linkVar12(): Set | Per { + + private __backing_linkVar12?: ILinkDecoratedVariable<(Set | Per)>; + + public get linkVar12(): (Set | Per) { return this.__backing_linkVar12!.get(); } - public set linkVar12(value: Set | Per) { + + public set linkVar12(value: (Set | Per)) { this.__backing_linkVar12!.set(value); } - @memo() public _build(@memo() style: ((instance: Parent)=> Parent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_Parent { - set linkVar1(linkVar1: Per | undefined) - get linkVar1(): Per | undefined - set __backing_linkVar1(__backing_linkVar1: DecoratedV1VariableBase | undefined) - get __backing_linkVar1(): DecoratedV1VariableBase | undefined - set linkVar2(linkVar2: Array | undefined) - get linkVar2(): Array | undefined - set __backing_linkVar2(__backing_linkVar2: DecoratedV1VariableBase> | undefined) - get __backing_linkVar2(): DecoratedV1VariableBase> | undefined - set linkVar3(linkVar3: LinkType | undefined) - get linkVar3(): LinkType | undefined - set __backing_linkVar3(__backing_linkVar3: DecoratedV1VariableBase | undefined) - get __backing_linkVar3(): DecoratedV1VariableBase | undefined - set linkVar4(linkVar4: Set | undefined) - get linkVar4(): Set | undefined - set __backing_linkVar4(__backing_linkVar4: DecoratedV1VariableBase> | undefined) - get __backing_linkVar4(): DecoratedV1VariableBase> | undefined - set linkVar5(linkVar5: Array | undefined) - get linkVar5(): Array | undefined - set __backing_linkVar5(__backing_linkVar5: DecoratedV1VariableBase> | undefined) - get __backing_linkVar5(): DecoratedV1VariableBase> | undefined - set linkVar6(linkVar6: Array | undefined) - get linkVar6(): Array | undefined - set __backing_linkVar6(__backing_linkVar6: DecoratedV1VariableBase> | undefined) - get __backing_linkVar6(): DecoratedV1VariableBase> | undefined - set linkVar7(linkVar7: Array | undefined) - get linkVar7(): Array | undefined - set __backing_linkVar7(__backing_linkVar7: DecoratedV1VariableBase> | undefined) - get __backing_linkVar7(): DecoratedV1VariableBase> | undefined - set linkVar8(linkVar8: ((sr: string)=> void) | undefined) - get linkVar8(): ((sr: string)=> void) | undefined - set __backing_linkVar8(__backing_linkVar8: DecoratedV1VariableBase<((sr: string)=> void)> | undefined) - get __backing_linkVar8(): DecoratedV1VariableBase<((sr: string)=> void)> | undefined - set linkVar9(linkVar9: Date | undefined) - get linkVar9(): Date | undefined - set __backing_linkVar9(__backing_linkVar9: DecoratedV1VariableBase | undefined) - get __backing_linkVar9(): DecoratedV1VariableBase | undefined - set linkVar10(linkVar10: Map | undefined) - get linkVar10(): Map | undefined - set __backing_linkVar10(__backing_linkVar10: DecoratedV1VariableBase> | undefined) - get __backing_linkVar10(): DecoratedV1VariableBase> | undefined - set linkVar11(linkVar11: string | number | undefined) - get linkVar11(): string | number | undefined - set __backing_linkVar11(__backing_linkVar11: DecoratedV1VariableBase | undefined) - get __backing_linkVar11(): DecoratedV1VariableBase | undefined - set linkVar12(linkVar12: Set | Per | undefined) - get linkVar12(): Set | Per | undefined - set __backing_linkVar12(__backing_linkVar12: DecoratedV1VariableBase | Per> | undefined) - get __backing_linkVar12(): DecoratedV1VariableBase | Per> | undefined +@Retention({policy:"SOURCE"}) @interface __Link_intrinsic {} + +@Component() export interface __Options_Parent { + @__Link_intrinsic() set linkVar1(linkVar1: (Per | undefined)) + + @__Link_intrinsic() get linkVar1(): (Per | undefined) + set __backing_linkVar1(__backing_linkVar1: (LinkSourceType | undefined)) + + get __backing_linkVar1(): (LinkSourceType | undefined) + @__Link_intrinsic() set linkVar2(linkVar2: (Array | undefined)) + + @__Link_intrinsic() get linkVar2(): (Array | undefined) + set __backing_linkVar2(__backing_linkVar2: (LinkSourceType> | undefined)) + + get __backing_linkVar2(): (LinkSourceType> | undefined) + @__Link_intrinsic() set linkVar3(linkVar3: (LinkType | undefined)) + + @__Link_intrinsic() get linkVar3(): (LinkType | undefined) + set __backing_linkVar3(__backing_linkVar3: (LinkSourceType | undefined)) + + get __backing_linkVar3(): (LinkSourceType | undefined) + @__Link_intrinsic() set linkVar4(linkVar4: (Set | undefined)) + + @__Link_intrinsic() get linkVar4(): (Set | undefined) + set __backing_linkVar4(__backing_linkVar4: (LinkSourceType> | undefined)) + + get __backing_linkVar4(): (LinkSourceType> | undefined) + @__Link_intrinsic() set linkVar5(linkVar5: (Array | undefined)) + + @__Link_intrinsic() get linkVar5(): (Array | undefined) + set __backing_linkVar5(__backing_linkVar5: (LinkSourceType> | undefined)) + + get __backing_linkVar5(): (LinkSourceType> | undefined) + @__Link_intrinsic() set linkVar6(linkVar6: (Array | undefined)) + + @__Link_intrinsic() get linkVar6(): (Array | undefined) + set __backing_linkVar6(__backing_linkVar6: (LinkSourceType> | undefined)) + + get __backing_linkVar6(): (LinkSourceType> | undefined) + @__Link_intrinsic() set linkVar7(linkVar7: (Array | undefined)) + + @__Link_intrinsic() get linkVar7(): (Array | undefined) + set __backing_linkVar7(__backing_linkVar7: (LinkSourceType> | undefined)) + + get __backing_linkVar7(): (LinkSourceType> | undefined) + @__Link_intrinsic() set linkVar8(linkVar8: (((sr: string)=> void) | undefined)) + + @__Link_intrinsic() get linkVar8(): (((sr: string)=> void) | undefined) + set __backing_linkVar8(__backing_linkVar8: (LinkSourceType<((sr: string)=> void)> | undefined)) + + get __backing_linkVar8(): (LinkSourceType<((sr: string)=> void)> | undefined) + @__Link_intrinsic() set linkVar9(linkVar9: (Date | undefined)) + + @__Link_intrinsic() get linkVar9(): (Date | undefined) + set __backing_linkVar9(__backing_linkVar9: (LinkSourceType | undefined)) + + get __backing_linkVar9(): (LinkSourceType | undefined) + @__Link_intrinsic() set linkVar10(linkVar10: (Map | undefined)) + + @__Link_intrinsic() get linkVar10(): (Map | undefined) + set __backing_linkVar10(__backing_linkVar10: (LinkSourceType> | undefined)) + + get __backing_linkVar10(): (LinkSourceType> | undefined) + @__Link_intrinsic() set linkVar11(linkVar11: ((string | number) | undefined)) + + @__Link_intrinsic() get linkVar11(): ((string | number) | undefined) + set __backing_linkVar11(__backing_linkVar11: (LinkSourceType<(string | number)> | undefined)) + + get __backing_linkVar11(): (LinkSourceType<(string | number)> | undefined) + @__Link_intrinsic() set linkVar12(linkVar12: ((Set | Per) | undefined)) + + @__Link_intrinsic() get linkVar12(): ((Set | Per) | undefined) + set __backing_linkVar12(__backing_linkVar12: (LinkSourceType<(Set | Per)> | undefined)) + + get __backing_linkVar12(): (LinkSourceType<(Set | Per)> | undefined) + } `; @@ -304,9 +397,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test complex type @Link decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts index 01a8275f9..88ca21cd4 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,67 +38,81 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; + +import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { PropDecoratedVariable as PropDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { DecoratedV1VariableBase as DecoratedV1VariableBase } from "@ohos.arkui.stateManagement"; -import { LinkDecoratedVariable as LinkDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { UITextInputAttribute as UITextInputAttribute } from "@ohos.arkui.component"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, Column as Column, TextInput as TextInput } from "@ohos.arkui.component"; + import { Link as Link, State as State, Prop as Prop } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class Parant extends CustomComponent { - public __initializeStruct(initializers: __Options_Parant | undefined, @memo() content: (()=> void) | undefined): void { +@Component() final struct Parant extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parant | undefined), @memo() content: ((()=> void) | undefined)): void { if (({let gensym___10127521 = initializers; (((gensym___10127521) == (null)) ? undefined : gensym___10127521.__backing_text1)})) { - this.__backing_text1 = new LinkDecoratedVariable("text1", initializers!.__backing_text1!); + this.__backing_text1 = STATE_MGMT_FACTORY.makeLink(this, "text1", initializers!.__backing_text1!); }; } - public __updateStruct(initializers: __Options_Parant | undefined): void {} - private __backing_text1?: LinkDecoratedVariable; + + public __updateStruct(initializers: (__Options_Parant | undefined)): void {} + + private __backing_text1?: ILinkDecoratedVariable; + public get text1(): string { return this.__backing_text1!.get(); } + public set text1(value: string) { this.__backing_text1!.set(value); } - @memo() public _build(@memo() style: ((instance: Parant)=> Parant) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parant | undefined): void { - Column(undefined, undefined, (() => { + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { TextInput(undefined, { text: this.text1, }, undefined); Child._instantiateImpl(undefined, (() => { return new Child(); - }), ({ + }), { __backing_childText: this.__backing_text1, childText2: this.text1, childText3: this.text1, childText4: this.text1, - } as __Options_Child), undefined, undefined); + }, undefined, undefined); })); } + public constructor() {} + } -@Component({freezeWhenInactive:false}) final class Child extends CustomComponent { - public __initializeStruct(initializers: __Options_Child | undefined, @memo() content: (()=> void) | undefined): void { +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { if (({let gensym___161337494 = initializers; (((gensym___161337494) == (null)) ? undefined : gensym___161337494.__backing_childText)})) { - this.__backing_childText = new LinkDecoratedVariable("childText", initializers!.__backing_childText!); + this.__backing_childText = STATE_MGMT_FACTORY.makeLink(this, "childText", initializers!.__backing_childText!); }; - this.__backing_childText2 = new StateDecoratedVariable("childText2", ((({let gensym___95513066 = initializers; + this.__backing_childText2 = STATE_MGMT_FACTORY.makeState(this, "childText2", ((({let gensym___95513066 = initializers; (((gensym___95513066) == (null)) ? undefined : gensym___95513066.childText2)})) ?? ("sss"))); - this.__backing_childText3 = new PropDecoratedVariable("childText3", (initializers!.childText3 as string)); - this.__backing_childText4 = new PropDecoratedVariable("childText4", ((({let gensym___162028107 = initializers; + this.__backing_childText3 = STATE_MGMT_FACTORY.makeProp(this, "childText3", (initializers!.childText3 as string)); + this.__backing_childText4 = STATE_MGMT_FACTORY.makeProp(this, "childText4", ((({let gensym___162028107 = initializers; (((gensym___162028107) == (null)) ? undefined : gensym___162028107.childText4)})) ?? ("cc"))); } - public __updateStruct(initializers: __Options_Child | undefined): void { + + public __updateStruct(initializers: (__Options_Child | undefined)): void { if (((({let gensym___77632518 = initializers; (((gensym___77632518) == (null)) ? undefined : gensym___77632518.childText3)})) !== (undefined))) { this.__backing_childText3!.update((initializers!.childText3 as string)); @@ -107,66 +122,95 @@ function main() {} this.__backing_childText4!.update((initializers!.childText4 as string)); } } - private __backing_childText?: LinkDecoratedVariable; + + private __backing_childText?: ILinkDecoratedVariable; + public get childText(): string { return this.__backing_childText!.get(); } + public set childText(value: string) { this.__backing_childText!.set(value); } - private __backing_childText2?: StateDecoratedVariable; + + private __backing_childText2?: IStateDecoratedVariable; + public get childText2(): string { return this.__backing_childText2!.get(); } + public set childText2(value: string) { this.__backing_childText2!.set(value); } - private __backing_childText3?: PropDecoratedVariable; + + private __backing_childText3?: IPropDecoratedVariable; + public get childText3(): string { return this.__backing_childText3!.get(); } + public set childText3(value: string) { this.__backing_childText3!.set(value); } - private __backing_childText4?: PropDecoratedVariable; + + private __backing_childText4?: IPropDecoratedVariable; + public get childText4(): string { return this.__backing_childText4!.get(); } + public set childText4(value: string) { this.__backing_childText4!.set(value); } - @memo() public _build(@memo() style: ((instance: Child)=> Child) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Child | undefined): void { + + @memo() public build() { TextInput(undefined, { text: this.childText, }, undefined); } + public constructor() {} + } -interface __Options_Parant { - set text1(text1: string | undefined) - get text1(): string | undefined - set __backing_text1(__backing_text1: DecoratedV1VariableBase | undefined) - get __backing_text1(): DecoratedV1VariableBase | undefined +@Retention({policy:"SOURCE"}) @interface __Link_intrinsic {} + +@Component() export interface __Options_Parant { + @__Link_intrinsic() set text1(text1: (string | undefined)) + + @__Link_intrinsic() get text1(): (string | undefined) + set __backing_text1(__backing_text1: (LinkSourceType | undefined)) + + get __backing_text1(): (LinkSourceType | undefined) + } -interface __Options_Child { - set childText(childText: string | undefined) - get childText(): string | undefined - set __backing_childText(__backing_childText: DecoratedV1VariableBase | undefined) - get __backing_childText(): DecoratedV1VariableBase | undefined - set childText2(childText2: string | undefined) - get childText2(): string | undefined - set __backing_childText2(__backing_childText2: StateDecoratedVariable | undefined) - get __backing_childText2(): StateDecoratedVariable | undefined - set childText3(childText3: string | undefined) - get childText3(): string | undefined - set __backing_childText3(__backing_childText3: PropDecoratedVariable | undefined) - get __backing_childText3(): PropDecoratedVariable | undefined - set childText4(childText4: string | undefined) - get childText4(): string | undefined - set __backing_childText4(__backing_childText4: PropDecoratedVariable | undefined) - get __backing_childText4(): PropDecoratedVariable | undefined +@Component() export interface __Options_Child { + @__Link_intrinsic() set childText(childText: (string | undefined)) + + @__Link_intrinsic() get childText(): (string | undefined) + set __backing_childText(__backing_childText: (LinkSourceType | undefined)) + + get __backing_childText(): (LinkSourceType | undefined) + set childText2(childText2: (string | undefined)) + + get childText2(): (string | undefined) + set __backing_childText2(__backing_childText2: (IStateDecoratedVariable | undefined)) + + get __backing_childText2(): (IStateDecoratedVariable | undefined) + set childText3(childText3: (string | undefined)) + + get childText3(): (string | undefined) + set __backing_childText3(__backing_childText3: (IPropDecoratedVariable | undefined)) + + get __backing_childText3(): (IPropDecoratedVariable | undefined) + set childText4(childText4: (string | undefined)) + + get childText4(): (string | undefined) + set __backing_childText4(__backing_childText4: (IPropDecoratedVariable | undefined)) + + get __backing_childText4(): (IPropDecoratedVariable | undefined) + } `; @@ -176,9 +220,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test @Link decorated variables passing to other variables', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/link/state-to-link.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/link/state-to-link.test.ts index cd9424e90..c22a650ba 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/link/state-to-link.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/state-to-link.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,46 +38,70 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; + +import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { DecoratedV1VariableBase as DecoratedV1VariableBase } from "@ohos.arkui.stateManagement"; -import { LinkDecoratedVariable as LinkDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { UIDatePickerAttribute as UIDatePickerAttribute } from "@ohos.arkui.component"; -import { UIButtonAttribute as UIButtonAttribute } from "@ohos.arkui.component"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + import { EntryPoint as EntryPoint } from "arkui.UserView"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, Entry as Entry, Column as Column, Button as Button, DatePicker as DatePicker, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + import { Link as Link, State as State } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class DateComponent extends CustomComponent { - public __initializeStruct(initializers: __Options_DateComponent | undefined, @memo() content: (()=> void) | undefined): void { +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/link/state-to-link", + pageFullPath: "test/demo/mock/decorators/link/state-to-link", + integratedHsp: "false", + } as NavInterface)); + +@Component() final struct DateComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_DateComponent | undefined), @memo() content: ((()=> void) | undefined)): void { if (({let gensym___164314175 = initializers; (((gensym___164314175) == (null)) ? undefined : gensym___164314175.__backing_selectedDate)})) { - this.__backing_selectedDate = new LinkDecoratedVariable("selectedDate", initializers!.__backing_selectedDate!); + this.__backing_selectedDate = STATE_MGMT_FACTORY.makeLink(this, "selectedDate", initializers!.__backing_selectedDate!); }; } - public __updateStruct(initializers: __Options_DateComponent | undefined): void {} - private __backing_selectedDate?: LinkDecoratedVariable; + + public __updateStruct(initializers: (__Options_DateComponent | undefined)): void {} + + private __backing_selectedDate?: ILinkDecoratedVariable; + public get selectedDate(): Date { return this.__backing_selectedDate!.get(); } + public set selectedDate(value: Date) { this.__backing_selectedDate!.set(value); } - @memo() public _build(@memo() style: ((instance: DateComponent)=> DateComponent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_DateComponent | undefined): void { - Column(undefined, undefined, (() => { - Button(@memo() ((instance: UIButtonAttribute): void => { + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.selectedDate.setFullYear(((this.selectedDate.getFullYear()) + (1))); })); return; }), "child increase the year by 1", undefined, undefined); - Button(@memo() ((instance: UIButtonAttribute): void => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.margin(10).onClick(((e: ClickEvent) => { this.selectedDate = new Date("2023-09-09"); })); @@ -89,32 +114,38 @@ function main() {} }, undefined); })); } + public constructor() {} } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class ParentComponent extends CustomComponent { - public __initializeStruct(initializers: __Options_ParentComponent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_parentSelectedDate = new StateDecoratedVariable("parentSelectedDate", ((({let gensym___80922148 = initializers; +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct ParentComponent extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_ParentComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_parentSelectedDate = STATE_MGMT_FACTORY.makeState(this, "parentSelectedDate", ((({let gensym___80922148 = initializers; (((gensym___80922148) == (null)) ? undefined : gensym___80922148.parentSelectedDate)})) ?? (new Date("2021-08-08")))); } - public __updateStruct(initializers: __Options_ParentComponent | undefined): void {} - private __backing_parentSelectedDate?: StateDecoratedVariable; + + public __updateStruct(initializers: (__Options_ParentComponent | undefined)): void {} + + private __backing_parentSelectedDate?: IStateDecoratedVariable; + public get parentSelectedDate(): Date { return this.__backing_parentSelectedDate!.get(); } + public set parentSelectedDate(value: Date) { this.__backing_parentSelectedDate!.set(value); } - @memo() public _build(@memo() style: ((instance: ParentComponent)=> ParentComponent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_ParentComponent | undefined): void { - Column(undefined, undefined, (() => { - Button(@memo() ((instance: UIButtonAttribute): void => { + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.margin(10).onClick(((e: ClickEvent) => { this.parentSelectedDate.setMonth(((this.parentSelectedDate.getMonth()) + (1))); })); return; }), "parent increase the month by 1", undefined, undefined); - Button(@memo() ((instance: UIButtonAttribute): void => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.margin(10).onClick(((e: ClickEvent) => { this.parentSelectedDate = new Date("2023-07-07"); })); @@ -127,26 +158,36 @@ function main() {} }, undefined); DateComponent._instantiateImpl(undefined, (() => { return new DateComponent(); - }), ({ + }), { __backing_selectedDate: this.__backing_parentSelectedDate, - } as __Options_DateComponent), undefined, undefined); + }, undefined, undefined); })); } + public constructor() {} + } -interface __Options_DateComponent { - set selectedDate(selectedDate: Date | undefined) - get selectedDate(): Date | undefined - set __backing_selectedDate(__backing_selectedDate: DecoratedV1VariableBase | undefined) - get __backing_selectedDate(): DecoratedV1VariableBase | undefined +@Retention({policy:"SOURCE"}) @interface __Link_intrinsic {} + +@Component() export interface __Options_DateComponent { + @__Link_intrinsic() set selectedDate(selectedDate: (Date | undefined)) + + @__Link_intrinsic() get selectedDate(): (Date | undefined) + set __backing_selectedDate(__backing_selectedDate: (LinkSourceType | undefined)) + + get __backing_selectedDate(): (LinkSourceType | undefined) + } -interface __Options_ParentComponent { - set parentSelectedDate(parentSelectedDate: Date | undefined) - get parentSelectedDate(): Date | undefined - set __backing_parentSelectedDate(__backing_parentSelectedDate: StateDecoratedVariable | undefined) - get __backing_parentSelectedDate(): StateDecoratedVariable | undefined +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_ParentComponent { + set parentSelectedDate(parentSelectedDate: (Date | undefined)) + + get parentSelectedDate(): (Date | undefined) + set __backing_parentSelectedDate(__backing_parentSelectedDate: (IStateDecoratedVariable | undefined)) + + get __backing_parentSelectedDate(): (IStateDecoratedVariable | undefined) + } class __EntryWrapper extends EntryPoint { @@ -155,7 +196,9 @@ class __EntryWrapper extends EntryPoint { return new ParentComponent(); }), undefined, undefined, undefined); } + public constructor() {} + } `; @@ -165,9 +208,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test @Link decorated variables passing', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/local/local-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/local/local-basic-type.test.ts new file mode 100644 index 000000000..fcfd6824a --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/local/local-basic-type.test.ts @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/local'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'local-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @Local decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_localVar1 = STATE_MGMT_FACTORY.makeLocal(this, "localVar1", "stateVar1"); + this.__backing_localVar2 = STATE_MGMT_FACTORY.makeLocal(this, "localVar2", 50); + this.__backing_localVar3 = STATE_MGMT_FACTORY.makeLocal(this, "localVar3", true); + this.__backing_localVar4 = STATE_MGMT_FACTORY.makeLocal(this, "localVar4", undefined); + this.__backing_localVar5 = STATE_MGMT_FACTORY.makeLocal(this, "localVar5", null); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_localVar1?: ILocalDecoratedVariable; + + public get localVar1(): string { + return this.__backing_localVar1!.get(); + } + + public set localVar1(value: string) { + this.__backing_localVar1!.set(value); + } + + private __backing_localVar2?: ILocalDecoratedVariable; + + public get localVar2(): number { + return this.__backing_localVar2!.get(); + } + + public set localVar2(value: number) { + this.__backing_localVar2!.set(value); + } + + private __backing_localVar3?: ILocalDecoratedVariable; + + public get localVar3(): boolean { + return this.__backing_localVar3!.get(); + } + + public set localVar3(value: boolean) { + this.__backing_localVar3!.set(value); + } + + private __backing_localVar4?: ILocalDecoratedVariable; + + public get localVar4(): undefined { + return this.__backing_localVar4!.get(); + } + + public set localVar4(value: undefined) { + this.__backing_localVar4!.set(value); + } + + private __backing_localVar5?: ILocalDecoratedVariable; + + public get localVar5(): null { + return this.__backing_localVar5!.get(); + } + + public set localVar5(value: null) { + this.__backing_localVar5!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set localVar1(localVar1: (string | undefined)) + + get localVar1(): (string | undefined) + set __backing_localVar1(__backing_localVar1: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar1(): (ILocalDecoratedVariable | undefined) + set localVar2(localVar2: (number | undefined)) + + get localVar2(): (number | undefined) + set __backing_localVar2(__backing_localVar2: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar2(): (ILocalDecoratedVariable | undefined) + set localVar3(localVar3: (boolean | undefined)) + + get localVar3(): (boolean | undefined) + set __backing_localVar3(__backing_localVar3: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar3(): (ILocalDecoratedVariable | undefined) + set localVar4(localVar4: (undefined | undefined)) + + get localVar4(): (undefined | undefined) + set __backing_localVar4(__backing_localVar4: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar4(): (ILocalDecoratedVariable | undefined) + set localVar5(localVar5: (null | undefined)) + + get localVar5(): (null | undefined) + set __backing_localVar5(__backing_localVar5: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar5(): (ILocalDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic type @Local decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/local/local-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/local/local-complex-type.test.ts new file mode 100644 index 000000000..ad39578d1 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/local/local-complex-type.test.ts @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/local'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'local-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @Local decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class StateType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: StateType = new StateType(0, 0); + + public static readonly TYPE2: StateType = new StateType(1, 1); + + public static readonly TYPE3: StateType = new StateType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: StateType[] = [StateType.TYPE1, StateType.TYPE2, StateType.TYPE3]; + + public getName(): String { + return StateType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): StateType { + for (let i = 0;((i) < (StateType.#NamesArray.length));(++i)) { + if (((name) == (StateType.#NamesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant StateType.") + (name))); + } + + public static fromValue(value: int): StateType { + for (let i = 0;((i) < (StateType.#ValuesArray.length));(++i)) { + if (((value) == (StateType.#ValuesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum StateType with value ") + (value))); + } + + public valueOf(): int { + return StateType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return StateType.#StringValuesArray[this.#ordinal]; + } + + public static values(): StateType[] { + return StateType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: StateType): String { + return e.getName(); + } + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_localVar1 = STATE_MGMT_FACTORY.makeLocal(this, "localVar1", new Per(6)); + this.__backing_localVar2 = STATE_MGMT_FACTORY.makeLocal>(this, "localVar2", new Array(3, 6, 8)); + this.__backing_localVar3 = STATE_MGMT_FACTORY.makeLocal(this, "localVar3", StateType.TYPE3); + this.__backing_localVar4 = STATE_MGMT_FACTORY.makeLocal>(this, "localVar4", new Set(new Array("aa", "bb"))); + this.__backing_localVar5 = STATE_MGMT_FACTORY.makeLocal>(this, "localVar5", [true, false]); + this.__backing_localVar6 = STATE_MGMT_FACTORY.makeLocal>(this, "localVar6", new Array(new Per(7), new Per(11))); + this.__backing_localVar7 = STATE_MGMT_FACTORY.makeLocal>(this, "localVar7", [new Per(7), new Per(11)]); + this.__backing_localVar9 = STATE_MGMT_FACTORY.makeLocal(this, "localVar9", new Date("2025-4-23")); + this.__backing_localVar10 = STATE_MGMT_FACTORY.makeLocal>(this, "localVar10", new Map([[0, new Per(7)], [1, new Per(10)]])); + this.__backing_localVar11 = STATE_MGMT_FACTORY.makeLocal<(string | number)>(this, "localVar11", 0.0); + this.__backing_localVar12 = STATE_MGMT_FACTORY.makeLocal<(Set | Per)>(this, "localVar12", new Per(6)); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_localVar1?: ILocalDecoratedVariable; + + public get localVar1(): Per { + return this.__backing_localVar1!.get(); + } + + public set localVar1(value: Per) { + this.__backing_localVar1!.set(value); + } + + private __backing_localVar2?: ILocalDecoratedVariable>; + + public get localVar2(): Array { + return this.__backing_localVar2!.get(); + } + + public set localVar2(value: Array) { + this.__backing_localVar2!.set(value); + } + + private __backing_localVar3?: ILocalDecoratedVariable; + + public get localVar3(): StateType { + return this.__backing_localVar3!.get(); + } + + public set localVar3(value: StateType) { + this.__backing_localVar3!.set(value); + } + + private __backing_localVar4?: ILocalDecoratedVariable>; + + public get localVar4(): Set { + return this.__backing_localVar4!.get(); + } + + public set localVar4(value: Set) { + this.__backing_localVar4!.set(value); + } + + private __backing_localVar5?: ILocalDecoratedVariable>; + + public get localVar5(): Array { + return this.__backing_localVar5!.get(); + } + + public set localVar5(value: Array) { + this.__backing_localVar5!.set(value); + } + + private __backing_localVar6?: ILocalDecoratedVariable>; + + public get localVar6(): Array { + return this.__backing_localVar6!.get(); + } + + public set localVar6(value: Array) { + this.__backing_localVar6!.set(value); + } + + private __backing_localVar7?: ILocalDecoratedVariable>; + + public get localVar7(): Array { + return this.__backing_localVar7!.get(); + } + + public set localVar7(value: Array) { + this.__backing_localVar7!.set(value); + } + + private __backing_localVar9?: ILocalDecoratedVariable; + + public get localVar9(): Date { + return this.__backing_localVar9!.get(); + } + + public set localVar9(value: Date) { + this.__backing_localVar9!.set(value); + } + + private __backing_localVar10?: ILocalDecoratedVariable>; + + public get localVar10(): Map { + return this.__backing_localVar10!.get(); + } + + public set localVar10(value: Map) { + this.__backing_localVar10!.set(value); + } + + private __backing_localVar11?: ILocalDecoratedVariable<(string | number)>; + + public get localVar11(): (string | number) { + return this.__backing_localVar11!.get(); + } + + public set localVar11(value: (string | number)) { + this.__backing_localVar11!.set(value); + } + + private __backing_localVar12?: ILocalDecoratedVariable<(Set | Per)>; + + public get localVar12(): (Set | Per) { + return this.__backing_localVar12!.get(); + } + + public set localVar12(value: (Set | Per)) { + this.__backing_localVar12!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set localVar1(localVar1: (Per | undefined)) + + get localVar1(): (Per | undefined) + set __backing_localVar1(__backing_localVar1: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar1(): (ILocalDecoratedVariable | undefined) + set localVar2(localVar2: (Array | undefined)) + + get localVar2(): (Array | undefined) + set __backing_localVar2(__backing_localVar2: (ILocalDecoratedVariable> | undefined)) + + get __backing_localVar2(): (ILocalDecoratedVariable> | undefined) + set localVar3(localVar3: (StateType | undefined)) + + get localVar3(): (StateType | undefined) + set __backing_localVar3(__backing_localVar3: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar3(): (ILocalDecoratedVariable | undefined) + set localVar4(localVar4: (Set | undefined)) + + get localVar4(): (Set | undefined) + set __backing_localVar4(__backing_localVar4: (ILocalDecoratedVariable> | undefined)) + + get __backing_localVar4(): (ILocalDecoratedVariable> | undefined) + set localVar5(localVar5: (Array | undefined)) + + get localVar5(): (Array | undefined) + set __backing_localVar5(__backing_localVar5: (ILocalDecoratedVariable> | undefined)) + + get __backing_localVar5(): (ILocalDecoratedVariable> | undefined) + set localVar6(localVar6: (Array | undefined)) + + get localVar6(): (Array | undefined) + set __backing_localVar6(__backing_localVar6: (ILocalDecoratedVariable> | undefined)) + + get __backing_localVar6(): (ILocalDecoratedVariable> | undefined) + set localVar7(localVar7: (Array | undefined)) + + get localVar7(): (Array | undefined) + set __backing_localVar7(__backing_localVar7: (ILocalDecoratedVariable> | undefined)) + + get __backing_localVar7(): (ILocalDecoratedVariable> | undefined) + set localVar9(localVar9: (Date | undefined)) + + get localVar9(): (Date | undefined) + set __backing_localVar9(__backing_localVar9: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar9(): (ILocalDecoratedVariable | undefined) + set localVar10(localVar10: (Map | undefined)) + + get localVar10(): (Map | undefined) + set __backing_localVar10(__backing_localVar10: (ILocalDecoratedVariable> | undefined)) + + get __backing_localVar10(): (ILocalDecoratedVariable> | undefined) + set localVar11(localVar11: ((string | number) | undefined)) + + get localVar11(): ((string | number) | undefined) + set __backing_localVar11(__backing_localVar11: (ILocalDecoratedVariable<(string | number)> | undefined)) + + get __backing_localVar11(): (ILocalDecoratedVariable<(string | number)> | undefined) + set localVar12(localVar12: ((Set | Per) | undefined)) + + get localVar12(): ((Set | Per) | undefined) + set __backing_localVar12(__backing_localVar12: (ILocalDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_localVar12(): (ILocalDecoratedVariable<(Set | Per)> | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test complex type @Local decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/local/static-local.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/local/static-local.test.ts new file mode 100644 index 000000000..b85513d93 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/local/static-local.test.ts @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/local'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'static-local.ets'), +]; + +const pluginTester = new PluginTester('test static @Local decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + +class ABB { + public constructor() {} + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + public static __backing_localVar1: ILocalDecoratedVariable = STATE_MGMT_FACTORY.makeStaticLocal("localVar1", "stateVar1"); + + public static get localVar1(): string { + return Parent.__backing_localVar1.get(); + } + + public static set localVar1(value: string) { + Parent.__backing_localVar1.set(value); + } + + public static __backing_localVar2: ILocalDecoratedVariable = STATE_MGMT_FACTORY.makeStaticLocal("localVar2", 50); + + public static get localVar2(): number { + return Parent.__backing_localVar2.get(); + } + + public static set localVar2(value: number) { + Parent.__backing_localVar2.set(value); + } + + public static __backing_localVar3: ILocalDecoratedVariable = STATE_MGMT_FACTORY.makeStaticLocal("localVar3", new ABB()); + + public static get localVar3(): ABB { + return Parent.__backing_localVar3.get(); + } + + public static set localVar3(value: ABB) { + Parent.__backing_localVar3.set(value); + } + + @memo() public build() {} + + public constructor() {} + + static { + + } +} + +@ComponentV2() export interface __Options_Parent { + set localVar1(localVar1: (string | undefined)) + + get localVar1(): (string | undefined) + set __backing_localVar1(__backing_localVar1: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar1(): (ILocalDecoratedVariable | undefined) + set localVar2(localVar2: (number | undefined)) + + get localVar2(): (number | undefined) + set __backing_localVar2(__backing_localVar2: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar2(): (ILocalDecoratedVariable | undefined) + set localVar3(localVar3: (ABB | undefined)) + + get localVar3(): (ABB | undefined) + set __backing_localVar3(__backing_localVar3: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar3(): (ILocalDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test static @Local decorated variables transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-complex-type.test.ts new file mode 100644 index 000000000..d5e77e56c --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-complex-type.test.ts @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const LOCAL_STORAGELINK_DIR_PATH: string = 'decorators/localstoragelink'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, LOCAL_STORAGELINK_DIR_PATH, 'localstoragelink-complex-type.ets'), +]; + +const localStorageLinkTransform: Plugins = { + name: 'localStorageLink', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test LocalStorageLink complex type transform', buildConfig); + +const expectedScript: string = ` + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalStorageLinkDecoratedVariable as ILocalStorageLinkDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorageLink as LocalStorageLink } from "@ohos.arkui.stateManagement"; + +function main() {} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/localstoragelink/localstoragelink-complex-type", + pageFullPath: "test/demo/mock/decorators/localstoragelink/localstoragelink-complex-type", + integratedHsp: "false", +} as NavInterface)); + +class Person { + public name: string = ""; + + public constructor(name: string) {} + +} + +final class Status extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly Success: Status = new Status(0, 200); + + public static readonly NotFound: Status = new Status(1, 404); + + public static readonly ServerError: Status = new Status(2, 500); + + private static readonly #NamesArray: String[] = ["Success", "NotFound", "ServerError"]; + + private static readonly #ValuesArray: int[] = [200, 404, 500]; + + private static readonly #StringValuesArray: String[] = ["200", "404", "500"]; + + private static readonly #ItemsArray: Status[] = [Status.Success, Status.NotFound, Status.ServerError]; + + public getName(): String { + return Status.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): Status { + for (let i = 0;((i) < (Status.#NamesArray.length));(++i)) { + if (((name) == (Status.#NamesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum constant Status.") + (name))); + } + + public static fromValue(value: int): Status { + for (let i = 0;((i) < (Status.#ValuesArray.length));(++i)) { + if (((value) == (Status.#ValuesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum Status with value ") + (value))); + } + + public valueOf(): int { + return Status.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return Status.#StringValuesArray[this.#ordinal]; + } + + public static values(): Status[] { + return Status.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: Status): String { + return e.getName(); + } + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_arrayA = STATE_MGMT_FACTORY.makeLocalStorageLink>(this, "Prop1", "arrayA", [1, 2, 3]) + this.__backing_objectA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop2", "objectA", {}) + this.__backing_dateA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop3", "dateA", new Date("2021-08-08")) + this.__backing_setA = STATE_MGMT_FACTORY.makeLocalStorageLink>(this, "Prop4", "setA", new Set()) + this.__backing_mapA = STATE_MGMT_FACTORY.makeLocalStorageLink>(this, "Prop5", "mapA", new Map()) + this.__backing_classA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop7", "classA", new Person("John")) + this.__backing_enumA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop8", "enumA", Status.NotFound) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_arrayA?: ILocalStorageLinkDecoratedVariable>; + + public get arrayA(): Array { + return this.__backing_arrayA!.get(); + } + + public set arrayA(value: Array) { + this.__backing_arrayA!.set(value); + } + + private __backing_objectA?: ILocalStorageLinkDecoratedVariable; + + public get objectA(): Object { + return this.__backing_objectA!.get(); + } + + public set objectA(value: Object) { + this.__backing_objectA!.set(value); + } + + private __backing_dateA?: ILocalStorageLinkDecoratedVariable; + + public get dateA(): Date { + return this.__backing_dateA!.get(); + } + + public set dateA(value: Date) { + this.__backing_dateA!.set(value); + } + + private __backing_setA?: ILocalStorageLinkDecoratedVariable>; + + public get setA(): Set { + return this.__backing_setA!.get(); + } + + public set setA(value: Set) { + this.__backing_setA!.set(value); + } + + private __backing_mapA?: ILocalStorageLinkDecoratedVariable>; + + public get mapA(): Map { + return this.__backing_mapA!.get(); + } + + public set mapA(value: Map) { + this.__backing_mapA!.set(value); + } + + private __backing_classA?: ILocalStorageLinkDecoratedVariable; + + public get classA(): Person { + return this.__backing_classA!.get(); + } + + public set classA(value: Person) { + this.__backing_classA!.set(value); + } + + private __backing_enumA?: ILocalStorageLinkDecoratedVariable; + + public get enumA(): Status { + return this.__backing_enumA!.get(); + } + + public set enumA(value: Status) { + this.__backing_enumA!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set arrayA(arrayA: (Array | undefined)) + + get arrayA(): (Array | undefined) + set __backing_arrayA(__backing_arrayA: (ILocalStorageLinkDecoratedVariable> | undefined)) + + get __backing_arrayA(): (ILocalStorageLinkDecoratedVariable> | undefined) + set objectA(objectA: (Object | undefined)) + + get objectA(): (Object | undefined) + set __backing_objectA(__backing_objectA: (ILocalStorageLinkDecoratedVariable | undefined)) + + get __backing_objectA(): (ILocalStorageLinkDecoratedVariable | undefined) + set dateA(dateA: (Date | undefined)) + + get dateA(): (Date | undefined) + set __backing_dateA(__backing_dateA: (ILocalStorageLinkDecoratedVariable | undefined)) + + get __backing_dateA(): (ILocalStorageLinkDecoratedVariable | undefined) + set setA(setA: (Set | undefined)) + + get setA(): (Set | undefined) + set __backing_setA(__backing_setA: (ILocalStorageLinkDecoratedVariable> | undefined)) + + get __backing_setA(): (ILocalStorageLinkDecoratedVariable> | undefined) + set mapA(mapA: (Map | undefined)) + + get mapA(): (Map | undefined) + set __backing_mapA(__backing_mapA: (ILocalStorageLinkDecoratedVariable> | undefined)) + + get __backing_mapA(): (ILocalStorageLinkDecoratedVariable> | undefined) + set classA(classA: (Person | undefined)) + + get classA(): (Person | undefined) + set __backing_classA(__backing_classA: (ILocalStorageLinkDecoratedVariable | undefined)) + + get __backing_classA(): (ILocalStorageLinkDecoratedVariable | undefined) + set enumA(enumA: (Status | undefined)) + + get enumA(): (Status | undefined) + set __backing_enumA(__backing_enumA: (ILocalStorageLinkDecoratedVariable | undefined)) + + get __backing_enumA(): (ILocalStorageLinkDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testLocalStorageLinkTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test LocalStorageLink complex type transform', + [localStorageLinkTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testLocalStorageLinkTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-primitive-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-primitive-type.test.ts new file mode 100644 index 000000000..fea624d8d --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-primitive-type.test.ts @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const LOCAL_STORAGELINK_DIR_PATH: string = 'decorators/localstoragelink'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, LOCAL_STORAGELINK_DIR_PATH, 'localstoragelink-primitive-type.ets'), +]; + +const localStorageLinkTransform: Plugins = { + name: 'localStorageLink', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test LocalStorageLink primitive type transform', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalStorageLinkDecoratedVariable as ILocalStorageLinkDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorageLink as LocalStorageLink } from "@ohos.arkui.stateManagement"; + +function main() {} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/localstoragelink/localstoragelink-primitive-type", + pageFullPath: "test/demo/mock/decorators/localstoragelink/localstoragelink-primitive-type", + integratedHsp: "false", +} as NavInterface)); + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_numA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop1", "numA", 33) + this.__backing_stringA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop2", "stringA", "AA") + this.__backing_booleanA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop3", "booleanA", true) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_numA?: ILocalStorageLinkDecoratedVariable; + + public get numA(): number { + return this.__backing_numA!.get(); + } + + public set numA(value: number) { + this.__backing_numA!.set(value); + } + + private __backing_stringA?: ILocalStorageLinkDecoratedVariable; + + public get stringA(): string { + return this.__backing_stringA!.get(); + } + + public set stringA(value: string) { + this.__backing_stringA!.set(value); + } + + private __backing_booleanA?: ILocalStorageLinkDecoratedVariable; + + public get booleanA(): boolean { + return this.__backing_booleanA!.get(); + } + + public set booleanA(value: boolean) { + this.__backing_booleanA!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set numA(numA: (number | undefined)) + + get numA(): (number | undefined) + set __backing_numA(__backing_numA: (ILocalStorageLinkDecoratedVariable | undefined)) + + get __backing_numA(): (ILocalStorageLinkDecoratedVariable | undefined) + set stringA(stringA: (string | undefined)) + + get stringA(): (string | undefined) + set __backing_stringA(__backing_stringA: (ILocalStorageLinkDecoratedVariable | undefined)) + + get __backing_stringA(): (ILocalStorageLinkDecoratedVariable | undefined) + set booleanA(booleanA: (boolean | undefined)) + + get booleanA(): (boolean | undefined) + set __backing_booleanA(__backing_booleanA: (ILocalStorageLinkDecoratedVariable | undefined)) + + get __backing_booleanA(): (ILocalStorageLinkDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testLocalStorageLinkTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test LocalStorageLink primitive type transform', + [localStorageLinkTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testLocalStorageLinkTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/localstorageprop-ref/localstorageprop-ref-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/localstorageprop-ref/localstorageprop-ref-complex-type.test.ts new file mode 100644 index 000000000..8d6f9b383 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/localstorageprop-ref/localstorageprop-ref-complex-type.test.ts @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STORAGEPROP_DIR_PATH: string = 'decorators/localstorageprop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGEPROP_DIR_PATH, 'localstorageprop-ref-complex-type.ets'), +]; + +const storagePropTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @LocalStoragePropRef complex type transform', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalStoragePropRefDecoratedVariable as ILocalStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { LocalStoragePropRef as LocalStoragePropRef } from "@ohos.arkui.stateManagement"; + +function main() {} + +class Person { + public name: string = ""; + + public constructor(name: string) {} + +} + +final class Status extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly Success: Status = new Status(0, 200); + + public static readonly NotFound: Status = new Status(1, 404); + + public static readonly ServerError: Status = new Status(2, 500); + + private static readonly #NamesArray: String[] = ["Success", "NotFound", "ServerError"]; + + private static readonly #ValuesArray: int[] = [200, 404, 500]; + + private static readonly #StringValuesArray: String[] = ["200", "404", "500"]; + + private static readonly #ItemsArray: Status[] = [Status.Success, Status.NotFound, Status.ServerError]; + + public getName(): String { + return Status.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): Status { + for (let i = 0;((i) < (Status.#NamesArray.length));(++i)) { + if (((name) == (Status.#NamesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum constant Status.") + (name))); + } + + public static fromValue(value: int): Status { + for (let i = 0;((i) < (Status.#ValuesArray.length));(++i)) { + if (((value) == (Status.#ValuesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum Status with value ") + (value))); + } + + public valueOf(): int { + return Status.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return Status.#StringValuesArray[this.#ordinal]; + } + + public static values(): Status[] { + return Status.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: Status): String { + return e.getName(); + } + +} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_arrayB = STATE_MGMT_FACTORY.makeLocalStoragePropRef>(this, "Prop1", "arrayB", [1, 2, 3]) + this.__backing_objectB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop2", "objectB", {}) + this.__backing_dateB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop3", "dateB", new Date("2021-09-09")) + this.__backing_setB = STATE_MGMT_FACTORY.makeLocalStoragePropRef>(this, "Prop4", "setB", new Set()) + this.__backing_mapB = STATE_MGMT_FACTORY.makeLocalStoragePropRef>(this, "Prop5", "mapB", new Map()) + this.__backing_classB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop7", "classB", new Person("Kevin")) + this.__backing_enumB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop8", "enumB", Status.NotFound) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_arrayB?: ILocalStoragePropRefDecoratedVariable>; + + public get arrayB(): Array { + return this.__backing_arrayB!.get(); + } + + public set arrayB(value: Array) { + this.__backing_arrayB!.set(value); + } + + private __backing_objectB?: ILocalStoragePropRefDecoratedVariable; + + public get objectB(): Object { + return this.__backing_objectB!.get(); + } + + public set objectB(value: Object) { + this.__backing_objectB!.set(value); + } + + private __backing_dateB?: ILocalStoragePropRefDecoratedVariable; + + public get dateB(): Date { + return this.__backing_dateB!.get(); + } + + public set dateB(value: Date) { + this.__backing_dateB!.set(value); + } + + private __backing_setB?: ILocalStoragePropRefDecoratedVariable>; + + public get setB(): Set { + return this.__backing_setB!.get(); + } + + public set setB(value: Set) { + this.__backing_setB!.set(value); + } + + private __backing_mapB?: ILocalStoragePropRefDecoratedVariable>; + + public get mapB(): Map { + return this.__backing_mapB!.get(); + } + + public set mapB(value: Map) { + this.__backing_mapB!.set(value); + } + + private __backing_classB?: ILocalStoragePropRefDecoratedVariable; + + public get classB(): Person { + return this.__backing_classB!.get(); + } + + public set classB(value: Person) { + this.__backing_classB!.set(value); + } + + private __backing_enumB?: ILocalStoragePropRefDecoratedVariable; + + public get enumB(): Status { + return this.__backing_enumB!.get(); + } + + public set enumB(value: Status) { + this.__backing_enumB!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set arrayB(arrayB: (Array | undefined)) + + get arrayB(): (Array | undefined) + set __backing_arrayB(__backing_arrayB: (ILocalStoragePropRefDecoratedVariable> | undefined)) + + get __backing_arrayB(): (ILocalStoragePropRefDecoratedVariable> | undefined) + set objectB(objectB: (Object | undefined)) + + get objectB(): (Object | undefined) + set __backing_objectB(__backing_objectB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_objectB(): (ILocalStoragePropRefDecoratedVariable | undefined) + set dateB(dateB: (Date | undefined)) + + get dateB(): (Date | undefined) + set __backing_dateB(__backing_dateB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_dateB(): (ILocalStoragePropRefDecoratedVariable | undefined) + set setB(setB: (Set | undefined)) + + get setB(): (Set | undefined) + set __backing_setB(__backing_setB: (ILocalStoragePropRefDecoratedVariable> | undefined)) + + get __backing_setB(): (ILocalStoragePropRefDecoratedVariable> | undefined) + set mapB(mapB: (Map | undefined)) + + get mapB(): (Map | undefined) + set __backing_mapB(__backing_mapB: (ILocalStoragePropRefDecoratedVariable> | undefined)) + + get __backing_mapB(): (ILocalStoragePropRefDecoratedVariable> | undefined) + set classB(classB: (Person | undefined)) + + get classB(): (Person | undefined) + set __backing_classB(__backing_classB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_classB(): (ILocalStoragePropRefDecoratedVariable | undefined) + set enumB(enumB: (Status | undefined)) + + get enumB(): (Status | undefined) + set __backing_enumB(__backing_enumB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_enumB(): (ILocalStoragePropRefDecoratedVariable | undefined) + +} +`; + +function testStoragePropTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @LocalStoragePropRef complex type transform', + [storagePropTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testStoragePropTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/localstorageprop-ref/localstorageprop-ref-primitive-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/localstorageprop-ref/localstorageprop-ref-primitive-type.test.ts new file mode 100644 index 000000000..407d8ea12 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/localstorageprop-ref/localstorageprop-ref-primitive-type.test.ts @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STORAGEPROP_DIR_PATH: string = 'decorators/localstorageprop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGEPROP_DIR_PATH, 'localstorageprop-ref-primitive-type.ets'), +]; + +const storagePropTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @LocalStoragePropRef primitive type transform', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalStoragePropRefDecoratedVariable as ILocalStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { LocalStoragePropRef as LocalStoragePropRef } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_numB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop1", "numB", 43) + this.__backing_stringB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop2", "stringB", "BB") + this.__backing_booleanB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop3", "booleanB", false) + this.__backing_undefinedB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop4", "undefinedB", undefined) + this.__backing_nullB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop5", "nullB", null) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_numB?: ILocalStoragePropRefDecoratedVariable; + + public get numB(): number { + return this.__backing_numB!.get(); + } + + public set numB(value: number) { + this.__backing_numB!.set(value); + } + + private __backing_stringB?: ILocalStoragePropRefDecoratedVariable; + + public get stringB(): string { + return this.__backing_stringB!.get(); + } + + public set stringB(value: string) { + this.__backing_stringB!.set(value); + } + + private __backing_booleanB?: ILocalStoragePropRefDecoratedVariable; + + public get booleanB(): boolean { + return this.__backing_booleanB!.get(); + } + + public set booleanB(value: boolean) { + this.__backing_booleanB!.set(value); + } + + private __backing_undefinedB?: ILocalStoragePropRefDecoratedVariable; + + public get undefinedB(): undefined { + return this.__backing_undefinedB!.get(); + } + + public set undefinedB(value: undefined) { + this.__backing_undefinedB!.set(value); + } + + private __backing_nullB?: ILocalStoragePropRefDecoratedVariable; + + public get nullB(): null { + return this.__backing_nullB!.get(); + } + + public set nullB(value: null) { + this.__backing_nullB!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set numB(numB: (number | undefined)) + + get numB(): (number | undefined) + set __backing_numB(__backing_numB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_numB(): (ILocalStoragePropRefDecoratedVariable | undefined) + set stringB(stringB: (string | undefined)) + + get stringB(): (string | undefined) + set __backing_stringB(__backing_stringB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_stringB(): (ILocalStoragePropRefDecoratedVariable | undefined) + set booleanB(booleanB: (boolean | undefined)) + + get booleanB(): (boolean | undefined) + set __backing_booleanB(__backing_booleanB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_booleanB(): (ILocalStoragePropRefDecoratedVariable | undefined) + set undefinedB(undefinedB: (undefined | undefined)) + + get undefinedB(): (undefined | undefined) + set __backing_undefinedB(__backing_undefinedB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_undefinedB(): (ILocalStoragePropRefDecoratedVariable | undefined) + set nullB(nullB: (null | undefined)) + + get nullB(): (null | undefined) + set __backing_nullB(__backing_nullB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_nullB(): (ILocalStoragePropRefDecoratedVariable | undefined) + +} +`; + +function testStoragePropTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @LocalStoragePropRef primitive type transform', + [storagePropTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testStoragePropTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/monitor/enum-monitor-params.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/enum-monitor-params.test.ts new file mode 100644 index 000000000..d7289b2b4 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/enum-monitor-params.test.ts @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/monitor'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'enum-monitor-params.ets'), +]; + +const pluginTester = new PluginTester('test @Monitor decorator enum parameters transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { IMonitorDecoratedVariable as IMonitorDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Monitor as Monitor, IMonitor as IMonitor, ObservedV2 as ObservedV2, Trace as Trace, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + + +class FFF { + public ff: GGG = new GGG(); + + public constructor() {} + +} + +class GGG { + public constructor() {} + +} + +final class MonitorNames extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: String) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly name1: MonitorNames = new MonitorNames(0, "strArr.0"); + + public static readonly name2: MonitorNames = new MonitorNames(1, "name"); + + public static readonly name3: MonitorNames = new MonitorNames(2, "strArr.0"); + + public static readonly name4: MonitorNames = new MonitorNames(3, "varF.ff"); + + private static readonly #NamesArray: String[] = ["name1", "name2", "name3", "name4"]; + + private static readonly #StringValuesArray: String[] = ["strArr.0", "name", "strArr.0", "varF.ff"]; + + private static readonly #ItemsArray: MonitorNames[] = [MonitorNames.name1, MonitorNames.name2, MonitorNames.name3, MonitorNames.name4]; + + public getName(): String { + return MonitorNames.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): MonitorNames { + for (let i = 0;((i) < (MonitorNames.#NamesArray.length));(++i)) { + if (((name) == (MonitorNames.#NamesArray[i]))) { + return MonitorNames.#ItemsArray[i]; + } + } + throw new Error((("No enum constant MonitorNames.") + (name))); + } + + public static fromValue(value: String): MonitorNames { + for (let i = 0;((i) < (MonitorNames.#StringValuesArray.length));(++i)) { + if (((value) == (MonitorNames.#StringValuesArray[i]))) { + return MonitorNames.#ItemsArray[i]; + } + } + throw new Error((("No enum MonitorNames with value ") + (value))); + } + + public valueOf(): String { + return MonitorNames.#StringValuesArray[this.#ordinal]; + } + + public toString(): String { + return MonitorNames.#StringValuesArray[this.#ordinal]; + } + + public static values(): MonitorNames[] { + return MonitorNames.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: MonitorNames): String { + return e.getName(); + } + +} + +@ObservedV2() class Info implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"name"}) private __backing_name: string = "Tom"; + + @JSONStringifyIgnore() private __meta_name: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"strArr"}) private __backing_strArr: Array = ["North", "east"]; + + @JSONStringifyIgnore() private __meta_strArr: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + private __monitor_changeCCC: (IMonitorDecoratedVariable | undefined); + + @Monitor({value:[MonitorNames.name1, MonitorNames.name2, MonitorNames.name3]}) public changeCCC(monitor: IMonitor) {} + + public get name(): string { + this.conditionalAddRef(this.__meta_name); + return UIUtils.makeObserved(this.__backing_name); + } + + public set name(newValue: string) { + if (((this.__backing_name) !== (newValue))) { + this.__backing_name = newValue; + this.__meta_name.fireChange(); + this.executeOnSubscribingWatches("name"); + } + } + + public get strArr(): Array { + this.conditionalAddRef(this.__meta_strArr); + return UIUtils.makeObserved(this.__backing_strArr); + } + + public set strArr(newValue: Array) { + if (((this.__backing_strArr) !== (newValue))) { + this.__backing_strArr = newValue; + this.__meta_strArr.fireChange(); + this.executeOnSubscribingWatches("strArr"); + } + } + + public constructor() { + this.__monitor_changeCCC = STATE_MGMT_FACTORY.makeMonitor([{ + path: "strArr.0", + valueCallback: ((): Any => { + return ({let gensym___127309370 = this.strArr; + (((gensym___127309370) == (null)) ? undefined : gensym___127309370.$_get(0))}); + }), + }, { + path: "name", + valueCallback: ((): Any => { + return this.name; + }), + }, { + path: "strArr.0", + valueCallback: ((): Any => { + return ({let gensym___214506040 = this.strArr; + (((gensym___214506040) == (null)) ? undefined : gensym___214506040.$_get(0))}); + }), + }], ((_m: IMonitor) => { + this.changeCCC(_m); + })); + } + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_varF = STATE_MGMT_FACTORY.makeLocal(this, "varF", new FFF()); + this.__monitor_changeEEE = STATE_MGMT_FACTORY.makeMonitor([{ + path: "varF.ff", + valueCallback: ((): Any => { + return ({let gensym___55756901 = this.varF; + (((gensym___55756901) == (null)) ? undefined : gensym___55756901.ff)}); + }), + }], ((_m: IMonitor) => { + this.changeEEE(_m); + })); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_varF?: ILocalDecoratedVariable; + + public get varF(): FFF { + return this.__backing_varF!.get(); + } + + public set varF(value: FFF) { + this.__backing_varF!.set(value); + } + + private __monitor_changeEEE: (IMonitorDecoratedVariable | undefined); + + @Monitor({value:[MonitorNames.name4]}) public changeEEE(monitor: IMonitor) {} + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set varF(varF: (FFF | undefined)) + + get varF(): (FFF | undefined) + set __backing_varF(__backing_varF: (ILocalDecoratedVariable | undefined)) + + get __backing_varF(): (ILocalDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Monitor decorator enum parameters transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-before-state-variable.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-before-state-variable.test.ts new file mode 100644 index 000000000..30dcef186 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-before-state-variable.test.ts @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/monitor'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'monitor-before-state-variable.ets'), +]; + +const pluginTester = new PluginTester('test @Monitor method declared before state variables', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IMonitorDecoratedVariable as IMonitorDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Monitor as Monitor, Local as Local, IMonitor as IMonitor } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_message = STATE_MGMT_FACTORY.makeLocal(this, "message", "Hello World"); + this.__backing_name = STATE_MGMT_FACTORY.makeLocal(this, "name", "Tom"); + this.__backing_age = STATE_MGMT_FACTORY.makeLocal(this, "age", 24); + this.__monitor_onStrChange2 = STATE_MGMT_FACTORY.makeMonitor([{ + path: "message", + valueCallback: ((): Any => { + return this.message; + }), + }, { + path: "name", + valueCallback: ((): Any => { + return this.name; + }), + }], ((_m: IMonitor) => { + this.onStrChange2(_m); + })); + this.__monitor_onStrChange3 = STATE_MGMT_FACTORY.makeMonitor([{ + path: "name", + valueCallback: ((): Any => { + return this.name; + }), + }], ((_m: IMonitor) => { + this.onStrChange3(_m); + })); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __monitor_onStrChange1: (IMonitorDecoratedVariable | undefined); + + private __monitor_onStrChange2: (IMonitorDecoratedVariable | undefined); + + private __monitor_onStrChange3: (IMonitorDecoratedVariable | undefined); + + private __backing_message?: ILocalDecoratedVariable; + + public get message(): string { + return this.__backing_message!.get(); + } + + public set message(value: string) { + this.__backing_message!.set(value); + } + + private __backing_name?: ILocalDecoratedVariable; + + public get name(): string { + return this.__backing_name!.get(); + } + + public set name(value: string) { + this.__backing_name!.set(value); + } + + private __backing_age?: ILocalDecoratedVariable; + + public get age(): number { + return this.__backing_age!.get(); + } + + public set age(value: number) { + this.__backing_age!.set(value); + } + + @Monitor({value:["message", "name"]}) public onStrChange1(monitor: IMonitor) { + monitor.dirty.forEach(((path: string) => { + console.info(\`\${path} changed from \${({let gensym%%_74 = monitor.value(path); + (((gensym%%_74) == (null)) ? undefined : gensym%%_74.before)})} to \${({let gensym%%_75 = monitor.value(path); + (((gensym%%_75) == (null)) ? undefined : gensym%%_75.now)})}\`); + })); + } + + @Monitor({value:["message", "name"]}) public onStrChange2(monitor: IMonitor) { + monitor.dirty.forEach(((path: string) => { + console.info(\`\${path} changed from \${({let gensym%%_76 = monitor.value(path); + (((gensym%%_76) == (null)) ? undefined : gensym%%_76.before)})} to \${({let gensym%%_77 = monitor.value(path); + (((gensym%%_77) == (null)) ? undefined : gensym%%_77.now)})}\`); + })); + } + + @Monitor({value:["name"]}) public onStrChange3(monitor: IMonitor) { + monitor.dirty.forEach(((path: string) => { + console.info(\`\${path} changed from \${({let gensym%%_78 = monitor.value(path); + (((gensym%%_78) == (null)) ? undefined : gensym%%_78.before)})} to \${({let gensym%%_79 = monitor.value(path); + (((gensym%%_79) == (null)) ? undefined : gensym%%_79.now)})}\`); + })); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set message(message: (string | undefined)) + + get message(): (string | undefined) + set __backing_message(__backing_message: (ILocalDecoratedVariable | undefined)) + + get __backing_message(): (ILocalDecoratedVariable | undefined) + set name(name: (string | undefined)) + + get name(): (string | undefined) + set __backing_name(__backing_name: (ILocalDecoratedVariable | undefined)) + + get __backing_name(): (ILocalDecoratedVariable | undefined) + set age(age: (number | undefined)) + + get age(): (number | undefined) + set __backing_age(__backing_age: (ILocalDecoratedVariable | undefined)) + + get __backing_age(): (ILocalDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Monitor method declared before state variables', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-in-observedv2-class.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-in-observedv2-class.test.ts new file mode 100644 index 000000000..b98dbc0ab --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-in-observedv2-class.test.ts @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/monitor'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'monitor-in-observedv2-class.ets'), +]; + +const pluginTester = new PluginTester('test @Monitor decorator in @ObservedV2 class transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { IMonitorDecoratedVariable as IMonitorDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Button as Button } from "@ohos.arkui.component"; + +import { Monitor as Monitor, IMonitor as IMonitor, ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ObservedV2() class Info implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"name"}) private __backing_name: string = "Tom"; + + @JSONStringifyIgnore() private __meta_name: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"region"}) private __backing_region: string = "North"; + + @JSONStringifyIgnore() private __meta_region: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"job"}) private __backing_job: string = "Teacher"; + + @JSONStringifyIgnore() private __meta_job: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public age: number = 25; + + private __monitor_onNameChange: (IMonitorDecoratedVariable | undefined); + + private __monitor_onAgeChange: (IMonitorDecoratedVariable | undefined); + + private __monitor_onChange: (IMonitorDecoratedVariable | undefined); + + @Monitor({value:["name"]}) public onNameChange(monitor: IMonitor) { + console.info(\`name change from \${({let gensym%%_43 = monitor.value(); + (((gensym%%_43) == (null)) ? undefined : gensym%%_43.before)})} to \${({let gensym%%_44 = monitor.value(); + (((gensym%%_44) == (null)) ? undefined : gensym%%_44.now)})}\`); + } + + @Monitor({value:["age"]}) public onAgeChange(monitor: IMonitor) { + console.info(\`age change from \${({let gensym%%_45 = monitor.value(); + (((gensym%%_45) == (null)) ? undefined : gensym%%_45.before)})} to \${({let gensym%%_46 = monitor.value(); + (((gensym%%_46) == (null)) ? undefined : gensym%%_46.now)})}\`); + } + + @Monitor({value:["region", "job"]}) public onChange(monitor: IMonitor) { + monitor.dirty.forEach(((path: string) => { + console.info(\`\${path} change from \${({let gensym%%_47 = monitor.value(path); + (((gensym%%_47) == (null)) ? undefined : gensym%%_47.before)})} to \${({let gensym%%_48 = monitor.value(path); + (((gensym%%_48) == (null)) ? undefined : gensym%%_48.now)})}\`); + })); + } + + public get name(): string { + this.conditionalAddRef(this.__meta_name); + return UIUtils.makeObserved(this.__backing_name); + } + + public set name(newValue: string) { + if (((this.__backing_name) !== (newValue))) { + this.__backing_name = newValue; + this.__meta_name.fireChange(); + this.executeOnSubscribingWatches("name"); + } + } + + public get region(): string { + this.conditionalAddRef(this.__meta_region); + return UIUtils.makeObserved(this.__backing_region); + } + + public set region(newValue: string) { + if (((this.__backing_region) !== (newValue))) { + this.__backing_region = newValue; + this.__meta_region.fireChange(); + this.executeOnSubscribingWatches("region"); + } + } + + public get job(): string { + this.conditionalAddRef(this.__meta_job); + return UIUtils.makeObserved(this.__backing_job); + } + + public set job(newValue: string) { + if (((this.__backing_job) !== (newValue))) { + this.__backing_job = newValue; + this.__meta_job.fireChange(); + this.executeOnSubscribingWatches("job"); + } + } + + public constructor() { + this.__monitor_onNameChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "name", + valueCallback: ((): Any => { + return this.name; + }), + }], ((_m: IMonitor) => { + this.onNameChange(_m); + })); + this.__monitor_onAgeChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "age", + valueCallback: ((): Any => { + return this.age; + }), + }], ((_m: IMonitor) => { + this.onAgeChange(_m); + })); + this.__monitor_onChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "region", + valueCallback: ((): Any => { + return this.region; + }), + }, { + path: "job", + valueCallback: ((): Any => { + return this.job; + }), + }], ((_m: IMonitor) => { + this.onChange(_m); + })); + } + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_info = ((({let gensym___130514200 = initializers; + (((gensym___130514200) == (null)) ? undefined : gensym___130514200.info)})) ?? (new Info())); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_info?: Info; + + public get info(): Info { + return (this.__backing_info as Info); + } + + public set info(value: Info) { + this.__backing_info = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.info.name = "Jack"; + })); + return; + }), "change name", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.info.age = 26; + })); + return; + }), "change age", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.info.region = "South"; + })); + return; + }), "change region", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.info.job = "Driver"; + })); + return; + }), "change job", undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set info(info: (Info | undefined)) + + get info(): (Info | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Monitor decorator in @ObservedV2 class transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-in-struct.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-in-struct.test.ts new file mode 100644 index 000000000..ecc359ecf --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-in-struct.test.ts @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/monitor'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'monitor-in-struct.ets'), +]; + +const pluginTester = new PluginTester('test @Monitor decorator in struct transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { IMonitorDecoratedVariable as IMonitorDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Button as Button } from "@ohos.arkui.component"; + +import { Monitor as Monitor, Local as Local, IMonitor as IMonitor } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_message = STATE_MGMT_FACTORY.makeLocal(this, "message", "Hello World"); + this.__backing_name = STATE_MGMT_FACTORY.makeLocal(this, "name", "Tom"); + this.__backing_age = STATE_MGMT_FACTORY.makeLocal(this, "age", 24); + this.__monitor_onStrChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "message", + valueCallback: ((): Any => { + return this.message; + }), + }, { + path: "name", + valueCallback: ((): Any => { + return this.name; + }), + }], ((_m: IMonitor) => { + this.onStrChange(_m); + })); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_message?: ILocalDecoratedVariable; + + public get message(): string { + return this.__backing_message!.get(); + } + + public set message(value: string) { + this.__backing_message!.set(value); + } + + private __backing_name?: ILocalDecoratedVariable; + + public get name(): string { + return this.__backing_name!.get(); + } + + public set name(value: string) { + this.__backing_name!.set(value); + } + + private __backing_age?: ILocalDecoratedVariable; + + public get age(): number { + return this.__backing_age!.get(); + } + + public set age(value: number) { + this.__backing_age!.set(value); + } + + private __monitor_onStrChange: (IMonitorDecoratedVariable | undefined); + + @Monitor({value:["message", "name"]}) public onStrChange(monitor: IMonitor) { + monitor.dirty.forEach(((path: string) => { + console.info(\`\${path} changed from \${({let gensym%%_43 = monitor.value(path); + (((gensym%%_43) == (null)) ? undefined : gensym%%_43.before)})} to \${({let gensym%%_44 = monitor.value(path); + (((gensym%%_44) == (null)) ? undefined : gensym%%_44.now)})}\`); + })); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.message += "!"; + this.name = "Jack"; + })); + return; + }), "change string", undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set message(message: (string | undefined)) + + get message(): (string | undefined) + set __backing_message(__backing_message: (ILocalDecoratedVariable | undefined)) + + get __backing_message(): (ILocalDecoratedVariable | undefined) + set name(name: (string | undefined)) + + get name(): (string | undefined) + set __backing_name(__backing_name: (ILocalDecoratedVariable | undefined)) + + get __backing_name(): (ILocalDecoratedVariable | undefined) + set age(age: (number | undefined)) + + get age(): (number | undefined) + set __backing_age(__backing_age: (ILocalDecoratedVariable | undefined)) + + get __backing_age(): (ILocalDecoratedVariable | undefined) + +} + +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Monitor decorator in struct transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-params.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-params.test.ts new file mode 100644 index 000000000..5479104e5 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-params.test.ts @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/monitor'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'monitor-params.ets'), +]; + +const pluginTester = new PluginTester('test @Monitor decorator parameters transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { IMonitorDecoratedVariable as IMonitorDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Button as Button } from "@ohos.arkui.component"; + +import { Monitor as Monitor, IMonitor as IMonitor, ObservedV2 as ObservedV2, Trace as Trace, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class AAA { + public aaa: BBB = new BBB(); + + public constructor() {} + +} + +class BBB { + public bbb: CCC = new CCC(); + + public constructor() {} + +} + +class CCC { + public ccc: Array = new Array(new DDD(10), new DDD(12), new DDD(16)); + + public constructor() {} + +} + +class DDD { + public dd: Array; + + public constructor(dd: number) { + this.dd = []; + for (let i = 4;((i) < (dd));(i++)) { + this.dd.push(i); + } + } + +} + +class EEE { + public ee: FFF = new FFF(); + + public constructor() {} + +} + +class FFF { + public ff: GGG = new GGG(); + + public constructor() {} + +} + +class GGG { + public constructor() {} + +} + +@ObservedV2() class Info implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"name"}) private __backing_name: string = "Tom"; + + @JSONStringifyIgnore() private __meta_name: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"strArr"}) private __backing_strArr: Array = ["North", "east"]; + + @JSONStringifyIgnore() private __meta_strArr: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"job"}) private __backing_job: AAA = new AAA(); + + @JSONStringifyIgnore() private __meta_job: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public age: number = 25; + + private __monitor_onNameChange: (IMonitorDecoratedVariable | undefined); + + private __monitor_onAgeChange: (IMonitorDecoratedVariable | undefined); + + private __monitor_onJobChange: (IMonitorDecoratedVariable | undefined); + + @Monitor({value:["name"]}) public onNameChange(monitor: IMonitor) {} + + @Monitor({value:["strArr.0", "age"]}) public onAgeChange(monitor: IMonitor) {} + + @Monitor({value:["job.aaa.bbb.ccc.1.dd.0"]}) public onJobChange(monitor: IMonitor) {} + + public get name(): string { + this.conditionalAddRef(this.__meta_name); + return UIUtils.makeObserved(this.__backing_name); + } + + public set name(newValue: string) { + if (((this.__backing_name) !== (newValue))) { + this.__backing_name = newValue; + this.__meta_name.fireChange(); + this.executeOnSubscribingWatches("name"); + } + } + + public get strArr(): Array { + this.conditionalAddRef(this.__meta_strArr); + return UIUtils.makeObserved(this.__backing_strArr); + } + + public set strArr(newValue: Array) { + if (((this.__backing_strArr) !== (newValue))) { + this.__backing_strArr = newValue; + this.__meta_strArr.fireChange(); + this.executeOnSubscribingWatches("strArr"); + } + } + + public get job(): AAA { + this.conditionalAddRef(this.__meta_job); + return UIUtils.makeObserved(this.__backing_job); + } + + public set job(newValue: AAA) { + if (((this.__backing_job) !== (newValue))) { + this.__backing_job = newValue; + this.__meta_job.fireChange(); + this.executeOnSubscribingWatches("job"); + } + } + + public constructor() { + this.__monitor_onNameChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "name", + valueCallback: ((): Any => { + return this.name; + }), + }], ((_m: IMonitor) => { + this.onNameChange(_m); + })); + this.__monitor_onAgeChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "strArr.0", + valueCallback: ((): Any => { + return ({let gensym___127309370 = this.strArr; + (((gensym___127309370) == (null)) ? undefined : gensym___127309370.$_get(0))}); + }), + }, { + path: "age", + valueCallback: ((): Any => { + return this.age; + }), + }], ((_m: IMonitor) => { + this.onAgeChange(_m); + })); + this.__monitor_onJobChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "job.aaa.bbb.ccc.1.dd.0", + valueCallback: ((): Any => { + return ({let gensym___1661894 = ({let gensym___87426618 = ({let gensym___223024592 = ({let gensym___215921992 = ({let gensym___22088104 = ({let gensym___245550938 = this.job; + (((gensym___245550938) == (null)) ? undefined : gensym___245550938.aaa)}); + (((gensym___22088104) == (null)) ? undefined : gensym___22088104.bbb)}); + (((gensym___215921992) == (null)) ? undefined : gensym___215921992.ccc)}); + (((gensym___223024592) == (null)) ? undefined : gensym___223024592.$_get(1))}); + (((gensym___87426618) == (null)) ? undefined : gensym___87426618.dd)}); + (((gensym___1661894) == (null)) ? undefined : gensym___1661894.$_get(0))}); + }), + }], ((_m: IMonitor) => { + this.onJobChange(_m); + })); + } + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_per = STATE_MGMT_FACTORY.makeLocal(this, "per", new EEE()); + this.__backing_v1 = STATE_MGMT_FACTORY.makeLocal(this, "v1", true); + this.__backing_numArr = STATE_MGMT_FACTORY.makeLocal>(this, "numArr", ["1", "3", "5"]); + this.__monitor_onPerChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "per.ee.ff", + valueCallback: ((): Any => { + return ({let gensym___164069504 = ({let gensym___239523226 = this.per; + (((gensym___239523226) == (null)) ? undefined : gensym___239523226.ee)}); + (((gensym___164069504) == (null)) ? undefined : gensym___164069504.ff)}); + }), + }, { + path: "v1", + valueCallback: ((): Any => { + return this.v1; + }), + }, { + path: "numArr.1", + valueCallback: ((): Any => { + return ({let gensym___124152275 = this.numArr; + (((gensym___124152275) == (null)) ? undefined : gensym___124152275.$_get(1))}); + }), + }], ((_m: IMonitor) => { + this.onPerChange(_m); + })); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_per?: ILocalDecoratedVariable; + + public get per(): EEE { + return this.__backing_per!.get(); + } + + public set per(value: EEE) { + this.__backing_per!.set(value); + } + + private __backing_v1?: ILocalDecoratedVariable; + + public get v1(): boolean { + return this.__backing_v1!.get(); + } + + public set v1(value: boolean) { + this.__backing_v1!.set(value); + } + + private __backing_numArr?: ILocalDecoratedVariable>; + + public get numArr(): Array { + return this.__backing_numArr!.get(); + } + + public set numArr(value: Array) { + this.__backing_numArr!.set(value); + } + + private __monitor_onPerChange: (IMonitorDecoratedVariable | undefined); + + @Monitor({value:["per.ee.ff", "v1", "numArr.1"]}) public onPerChange(monitor: IMonitor) {} + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set per(per: (EEE | undefined)) + + get per(): (EEE | undefined) + set __backing_per(__backing_per: (ILocalDecoratedVariable | undefined)) + + get __backing_per(): (ILocalDecoratedVariable | undefined) + set v1(v1: (boolean | undefined)) + + get v1(): (boolean | undefined) + set __backing_v1(__backing_v1: (ILocalDecoratedVariable | undefined)) + + get __backing_v1(): (ILocalDecoratedVariable | undefined) + set numArr(numArr: (Array | undefined)) + + get numArr(): (Array | undefined) + set __backing_numArr(__backing_numArr: (ILocalDecoratedVariable> | undefined)) + + get __backing_numArr(): (ILocalDecoratedVariable> | undefined) + +} + +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Monitor decorator parameters transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-basic.test.ts new file mode 100644 index 000000000..60707178b --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-basic.test.ts @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBJECTLINK_DIR_PATH: string = 'decorators/objectlink'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBJECTLINK_DIR_PATH, 'objectlink-basic.ets'), +]; + +const objectlinkTrackTransform: Plugins = { + name: 'objectlink', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test objectlink basic transform', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IObjectLinkDecoratedVariable as IObjectLinkDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Observed as Observed, ObjectLink as ObjectLink } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Observed() class A implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + +} + +@Observed() class B implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + +} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_objectlinkvar = STATE_MGMT_FACTORY.makeObjectLink(this, "objectlinkvar", (({let gensym___248819442 = initializers; + (((gensym___248819442) == (null)) ? undefined : gensym___248819442.objectlinkvar)}) as A)) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void { + if (((({let gensym___97362509 = initializers; + (((gensym___97362509) == (null)) ? undefined : gensym___97362509.objectlinkvar)})) !== (undefined))) { + this.__backing_objectlinkvar!.update(initializers!.objectlinkvar!); + } + } + + private __backing_objectlinkvar?: IObjectLinkDecoratedVariable; + + public get objectlinkvar(): A { + return this.__backing_objectlinkvar!.get(); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() final struct MyStateSample2 extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample2 | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_objectlinkvar1 = STATE_MGMT_FACTORY.makeObjectLink<(A | undefined)>(this, "objectlinkvar1", (({let gensym___219806589 = initializers; + (((gensym___219806589) == (null)) ? undefined : gensym___219806589.objectlinkvar1)}) as (A | undefined))) + this.__backing_objectlinkvar2 = STATE_MGMT_FACTORY.makeObjectLink<(A | B)>(this, "objectlinkvar2", (({let gensym___217261862 = initializers; + (((gensym___217261862) == (null)) ? undefined : gensym___217261862.objectlinkvar2)}) as (A | B))) + this.__backing_objectlinkvar3 = STATE_MGMT_FACTORY.makeObjectLink<(A | B | null)>(this, "objectlinkvar3", (({let gensym___199257778 = initializers; + (((gensym___199257778) == (null)) ? undefined : gensym___199257778.objectlinkvar3)}) as (A | B | null))) + } + + public __updateStruct(initializers: (__Options_MyStateSample2 | undefined)): void { + if (((({let gensym___82770935 = initializers; + (((gensym___82770935) == (null)) ? undefined : gensym___82770935.objectlinkvar1)})) !== (undefined))) { + this.__backing_objectlinkvar1!.update(initializers!.objectlinkvar1!); + } + if (((({let gensym___225818999 = initializers; + (((gensym___225818999) == (null)) ? undefined : gensym___225818999.objectlinkvar2)})) !== (undefined))) { + this.__backing_objectlinkvar2!.update(initializers!.objectlinkvar2!); + } + if (((({let gensym___3063329 = initializers; + (((gensym___3063329) == (null)) ? undefined : gensym___3063329.objectlinkvar3)})) !== (undefined))) { + this.__backing_objectlinkvar3!.update(initializers!.objectlinkvar3!); + } + } + + private __backing_objectlinkvar1?: IObjectLinkDecoratedVariable<(A | undefined)>; + + public get objectlinkvar1(): (A | undefined) { + return this.__backing_objectlinkvar1!.get(); + } + + private __backing_objectlinkvar2?: IObjectLinkDecoratedVariable<(A | B)>; + + public get objectlinkvar2(): (A | B) { + return this.__backing_objectlinkvar2!.get(); + } + + private __backing_objectlinkvar3?: IObjectLinkDecoratedVariable<(A | B | null)>; + + public get objectlinkvar3(): (A | B | null) { + return this.__backing_objectlinkvar3!.get(); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set objectlinkvar(objectlinkvar: (A | undefined)) + + get objectlinkvar(): (A | undefined) + set __backing_objectlinkvar(__backing_objectlinkvar: (IObjectLinkDecoratedVariable | undefined)) + + get __backing_objectlinkvar(): (IObjectLinkDecoratedVariable | undefined) + +} + +@Component() export interface __Options_MyStateSample2 { + set objectlinkvar1(objectlinkvar1: ((A | undefined) | undefined)) + + get objectlinkvar1(): ((A | undefined) | undefined) + set __backing_objectlinkvar1(__backing_objectlinkvar1: (IObjectLinkDecoratedVariable<(A | undefined)> | undefined)) + + get __backing_objectlinkvar1(): (IObjectLinkDecoratedVariable<(A | undefined)> | undefined) + set objectlinkvar2(objectlinkvar2: ((A | B) | undefined)) + + get objectlinkvar2(): ((A | B) | undefined) + set __backing_objectlinkvar2(__backing_objectlinkvar2: (IObjectLinkDecoratedVariable<(A | B)> | undefined)) + + get __backing_objectlinkvar2(): (IObjectLinkDecoratedVariable<(A | B)> | undefined) + set objectlinkvar3(objectlinkvar3: ((A | B | null) | undefined)) + + get objectlinkvar3(): ((A | B | null) | undefined) + set __backing_objectlinkvar3(__backing_objectlinkvar3: (IObjectLinkDecoratedVariable<(A | B | null)> | undefined)) + + get __backing_objectlinkvar3(): (IObjectLinkDecoratedVariable<(A | B | null)> | undefined) + +} +`; + +function testObjectLinkTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test objectlink basic transform', + [objectlinkTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testObjectLinkTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-observed.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-observed.test.ts new file mode 100644 index 000000000..640a8e961 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-observed.test.ts @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBJECTLINK_DIR_PATH: string = 'decorators/objectlink'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBJECTLINK_DIR_PATH, 'objectlink-observed.ets'), +]; + +const objectlinkTrackTransform: Plugins = { + name: 'objectlink', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test objectlink observed transform', buildConfig); + +const expectedScript: string = ` + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IObjectLinkDecoratedVariable as IObjectLinkDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry, Column as Column, Button as Button, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { State as State, ObjectLink as ObjectLink, Observed as Observed } from "@ohos.arkui.stateManagement"; + +function main() {} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/objectlink/objectlink-observed", + pageFullPath: "test/demo/mock/decorators/objectlink/objectlink-observed", + integratedHsp: "false", + } as NavInterface)); + +@Observed() class DateClass extends Date implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor(args: (number | string)) { + super(args); + } + +} + +@Observed() class NewDate implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"data"}) private __backing_data: DateClass = new DateClass(11); + + public constructor(data: DateClass) { + this.data = data; + } + + public get data(): DateClass { + this.conditionalAddRef(this.__meta); + return this.__backing_data; + } + + public set data(newValue: DateClass) { + if (((this.__backing_data) !== (newValue))) { + this.__backing_data = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("data"); + } + } + +} + +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_label = ((({let gensym___171896504 = initializers; + (((gensym___171896504) == (null)) ? undefined : gensym___171896504.label)})) ?? ("date")); + this.__backing_data = STATE_MGMT_FACTORY.makeObjectLink(this, "data", (({let gensym___209155591 = initializers; + (((gensym___209155591) == (null)) ? undefined : gensym___209155591.data)}) as DateClass)) + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void { + if (((({let gensym___232946400 = initializers; + (((gensym___232946400) == (null)) ? undefined : gensym___232946400.data)})) !== (undefined))) { + this.__backing_data!.update(initializers!.data!); + } + } + + private __backing_label?: string; + + public get label(): string { + return (this.__backing_label as string); + } + + public set label(value: string) { + this.__backing_label = value; + } + + private __backing_data?: IObjectLinkDecoratedVariable; + + public get data(): DateClass { + return this.__backing_data!.get(); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.data.setDate(((this.data.getDate()) + (1))); + })); + return; + }), "child increase the day by 1", undefined, undefined); + })); + } + + public constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct Parent extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_newData = STATE_MGMT_FACTORY.makeState(this, "newData", ((({let gensym___225289068 = initializers; + (((gensym___225289068) == (null)) ? undefined : gensym___225289068.newData)})) ?? (new NewDate(new DateClass("2023-1-1"))))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_newData?: IStateDecoratedVariable; + + public get newData(): NewDate { + return this.__backing_newData!.get(); + } + + public set newData(value: NewDate) { + this.__backing_newData!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + label: "date", + data: this.newData.data, + }, undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.newData.data = new DateClass("2023-07-07"); + })); + return; + }), "parent update the new date", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.newData = new NewDate(new DateClass("2023-08-20")); + })); + return; + }), "ViewB: this.newData = new NewDate(new DateClass('2023-08-20'))", undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_Child { + set label(label: (string | undefined)) + + get label(): (string | undefined) + set data(data: (DateClass | undefined)) + + get data(): (DateClass | undefined) + set __backing_data(__backing_data: (IObjectLinkDecoratedVariable | undefined)) + + get __backing_data(): (IObjectLinkDecoratedVariable | undefined) + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_Parent { + set newData(newData: (NewDate | undefined)) + + get newData(): (NewDate | undefined) + set __backing_newData(__backing_newData: (IStateDecoratedVariable | undefined)) + + get __backing_newData(): (IStateDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + Parent._instantiateImpl(undefined, (() => { + return new Parent(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testObjectLinkTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test objectlink observed transform', + [objectlinkTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testObjectLinkTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonrename.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonrename.test.ts new file mode 100644 index 000000000..ce55069e0 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonrename.test.ts @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observed-track'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'observed-jsonrename.ets'), +]; + +const observedJsonRenameTransform: Plugins = { + name: 'observedJsonRename', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test observed class transform with JsonRename annotation', buildConfig); + +const expectedScript: string = ` + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Observed as Observed, Track as Track } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@Retention({policy:"SOURCE"}) @interface TestDecor {} + +@Observed() class testJsonRename implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + public var1: number = 1; + + @JSONRename({newName:"var2"}) private __backing_var2: number = 2; + + @JSONStringifyIgnore() private __meta_var2: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({value:"name3"}) public var3: number = 3; + + @TestDecor() public var4: number = 4; + + @JSONRename({value:"name5"}) private __backing_var5: number = 5; + + @JSONStringifyIgnore() private __meta_var5: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({value:"name6"}) @TestDecor() public var6: number = 6; + + @TestDecor() @JSONRename({newName:"var7"}) private __backing_var7: number = 7; + + @JSONStringifyIgnore() private __meta_var7: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({value:"name8"}) @TestDecor() private __backing_var8: number = 8; + + @JSONStringifyIgnore() private __meta_var8: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + + public get var2(): number { + this.conditionalAddRef(this.__meta_var2); + return this.__backing_var2; + } + + public set var2(newValue: number) { + if (((this.__backing_var2) !== (newValue))) { + this.__backing_var2 = newValue; + this.__meta_var2.fireChange(); + this.executeOnSubscribingWatches("var2"); + } + } + + public get var5(): number { + this.conditionalAddRef(this.__meta_var5); + return this.__backing_var5; + } + + public set var5(newValue: number) { + if (((this.__backing_var5) !== (newValue))) { + this.__backing_var5 = newValue; + this.__meta_var5.fireChange(); + this.executeOnSubscribingWatches("var5"); + } + } + + public get var7(): number { + this.conditionalAddRef(this.__meta_var7); + return this.__backing_var7; + } + + public set var7(newValue: number) { + if (((this.__backing_var7) !== (newValue))) { + this.__backing_var7 = newValue; + this.__meta_var7.fireChange(); + this.executeOnSubscribingWatches("var7"); + } + } + + public get var8(): number { + this.conditionalAddRef(this.__meta_var8); + return this.__backing_var8; + } + + public set var8(newValue: number) { + if (((this.__backing_var8) !== (newValue))) { + this.__backing_var8 = newValue; + this.__meta_var8.fireChange(); + this.executeOnSubscribingWatches("var8"); + } + } + +} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + +} +`; + +function testObservedJsonRenameTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test observed only transform', + [observedJsonRenameTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testObservedJsonRenameTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonstringifyignore.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonstringifyignore.test.ts new file mode 100644 index 000000000..9c398f8af --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonstringifyignore.test.ts @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observed-track'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'observed-jsonstringifyignore.ets'), +]; + +const observedJsonStringifyIgnoreTransform: Plugins = { + name: 'observedJsonStringifyIgnore', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test observed class transform with JsonStringifyIgnore annotation', buildConfig); + +const expectedScript: string = ` + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Observed as Observed, Track as Track } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@Retention({policy:"SOURCE"}) @interface TestDecor {} + +@Observed() class testJSONStringifyIgnore implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + public var1: number = 1; + + @JSONRename({newName:"var2"}) private __backing_var2: number = 2; + + @JSONStringifyIgnore() private __meta_var2: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONStringifyIgnore() public var3: number = 3; + + @TestDecor() public var4: number = 4; + + @JSONStringifyIgnore() private __backing_var5: number = 5; + + @JSONStringifyIgnore() private __meta_var5: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONStringifyIgnore() @TestDecor() public var6: number = 6; + + @TestDecor() @JSONRename({newName:"var7"}) private __backing_var7: number = 7; + + @JSONStringifyIgnore() private __meta_var7: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONStringifyIgnore() @TestDecor() private __backing_var8: number = 8; + + @JSONStringifyIgnore() private __meta_var8: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + + public get var2(): number { + this.conditionalAddRef(this.__meta_var2); + return this.__backing_var2; + } + + public set var2(newValue: number) { + if (((this.__backing_var2) !== (newValue))) { + this.__backing_var2 = newValue; + this.__meta_var2.fireChange(); + this.executeOnSubscribingWatches("var2"); + } + } + + public get var5(): number { + this.conditionalAddRef(this.__meta_var5); + return this.__backing_var5; + } + + public set var5(newValue: number) { + if (((this.__backing_var5) !== (newValue))) { + this.__backing_var5 = newValue; + this.__meta_var5.fireChange(); + this.executeOnSubscribingWatches("var5"); + } + } + + public get var7(): number { + this.conditionalAddRef(this.__meta_var7); + return this.__backing_var7; + } + + public set var7(newValue: number) { + if (((this.__backing_var7) !== (newValue))) { + this.__backing_var7 = newValue; + this.__meta_var7.fireChange(); + this.executeOnSubscribingWatches("var7"); + } + } + + public get var8(): number { + this.conditionalAddRef(this.__meta_var8); + return this.__backing_var8; + } + + public set var8(newValue: number) { + if (((this.__backing_var8) !== (newValue))) { + this.__backing_var8 = newValue; + this.__meta_var8.fireChange(); + this.executeOnSubscribingWatches("var8"); + } + } + +} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + +} +`; + +function testObservedJsonStringifyIgnoreTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test observed only transform', + [observedJsonStringifyIgnoreTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testObservedJsonStringifyIgnoreTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-only.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-only.test.ts index 08e0ec65b..c5f0105b6 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-only.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-only.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,22 @@ const observedTrackTransform: Plugins = { const pluginTester = new PluginTester('test observed only transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; @@ -67,8 +65,8 @@ function main() {} -@Observed() class A implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class A implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -82,63 +80,68 @@ function main() {} this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } - private __meta: MutableStateMeta = new MutableStateMeta("@Observe properties (no @Track)"); + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } - private __backing_propA: number = 1; + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __backing_trackA: number = 2; + @JSONRename({newName:"propA"}) private __backing_propA: number = 1; + + @JSONRename({newName:"trackA"}) private __backing_trackA: number = 2; public constructor() {} public get propA(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_propA; } public set propA(newValue: number) { if (((this.__backing_propA) !== (newValue))) { this.__backing_propA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("propA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("propA"); } } public get trackA(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_trackA; } public set trackA(newValue: number) { if (((this.__backing_trackA) !== (newValue))) { this.__backing_trackA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("trackA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("trackA"); } } } -@Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Component() export interface __Options_MyStateSample { } - `; function testObservedOnlyTransformer(this: PluginTestContext): void { @@ -147,9 +150,9 @@ function testObservedOnlyTransformer(this: PluginTestContext): void { pluginTester.run( 'test observed only transform', - [observedTrackTransform, uiNoRecheck], + [observedTrackTransform, uiNoRecheck, recheck], { - checked: [testObservedOnlyTransformer], + 'checked:ui-no-recheck': [testObservedOnlyTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-class-property.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-class-property.test.ts index 5bd7f7551..61ae1b6c8 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-class-property.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-class-property.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,22 @@ const observedTrackTransform: Plugins = { const pluginTester = new PluginTester('test observed track transform with class property', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; @@ -72,8 +70,8 @@ class Info { } -class E implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +class E implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -87,36 +85,43 @@ class E implements IObservedObject { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } public propE: Info = new Info(); - private __backing_trackE: BackingValue = new BackingValue(new Info()); + @JSONRename({newName:"trackE"}) private __backing_trackE: Info = new Info(); - private __meta_trackE: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONStringifyIgnore() private __meta_trackE: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); public constructor() {} public get trackE(): Info { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_trackE.addRef(); - } - setObservationDepth(this.__backing_trackE.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_trackE.value; + this.conditionalAddRef(this.__meta_trackE); + return this.__backing_trackE; } public set trackE(newValue: Info) { - if (((this.__backing_trackE.value) !== (newValue))) { - this.__backing_trackE.value = newValue; - this.__meta_trackE.fireChange(); - this.executeOnSubscribingWatches("trackE"); + if (((this.__backing_trackE) !== (newValue))) { + this.__backing_trackE = newValue; + this.__meta_trackE.fireChange(); + this.executeOnSubscribingWatches("trackE"); } } } -@Observed() class E1 implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class E1 implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -130,62 +135,66 @@ class E implements IObservedObject { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; - private __meta: MutableStateMeta = new MutableStateMeta("@Observe properties (no @Track)"); + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } - private __backing_propE1: BackingValue = new BackingValue(new Info()); + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __backing_trackE1: BackingValue = new BackingValue(new Info()); + @JSONRename({newName:"propE1"}) private __backing_propE1: Info = new Info(); + + @JSONRename({newName:"trackE1"}) private __backing_trackE1: Info = new Info(); public constructor() {} public get propE1(): Info { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_propE1.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_propE1.value; + this.conditionalAddRef(this.__meta); + return this.__backing_propE1; } public set propE1(newValue: Info) { - if (((this.__backing_propE1.value) !== (newValue))) { - this.__backing_propE1.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("propE1"); + if (((this.__backing_propE1) !== (newValue))) { + this.__backing_propE1 = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("propE1"); } } public get trackE1(): Info { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_trackE1.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_trackE1.value; + this.conditionalAddRef(this.__meta); + return this.__backing_trackE1; } public set trackE1(newValue: Info) { - if (((this.__backing_trackE1.value) !== (newValue))) { - this.__backing_trackE1.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("trackE1"); + if (((this.__backing_trackE1) !== (newValue))) { + this.__backing_trackE1 = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("trackE1"); } } } -@Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Component() export interface __Options_MyStateSample { } `; @@ -196,9 +205,9 @@ function testObservedOnlyTransformer(this: PluginTestContext): void { pluginTester.run( 'test observed track transform with class property', - [observedTrackTransform, uiNoRecheck], + [observedTrackTransform, uiNoRecheck, recheck], { - checked: [testObservedOnlyTransformer], + 'checked:ui-no-recheck': [testObservedOnlyTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-complex-type.test.ts index 371c48a6c..e1ae3d003 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-complex-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,25 @@ const observedTrackTransform: Plugins = { const pluginTester = new PluginTester('test observed track transform with complex type', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { memo as memo } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { memo as memo } from "arkui.stateManagement.runtime"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { NavInterface as NavInterface } from "arkui.UserView"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; @@ -67,7 +68,13 @@ import { Observed as Observed, Track as Track } from "@ohos.arkui.stateManagemen function main() {} - +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/observed-track/observed-track-complex-type", + pageFullPath: "test/demo/mock/decorators/observed-track/observed-track-complex-type", + integratedHsp: "false", + } as NavInterface)); class Person { public constructor() {} @@ -142,8 +149,8 @@ final class Status extends BaseEnum { } -@Observed() class mixed1 implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class mixed1 implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -157,51 +164,61 @@ final class Status extends BaseEnum { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; - private __backing_numA: number = 33; + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } - private __meta_numA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"numA"}) private __backing_numA: number = 33; - private __backing_stringA: string = "AA"; + @JSONStringifyIgnore() private __meta_numA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_stringA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"stringA"}) private __backing_stringA: string = "AA"; - private __backing_booleanA: boolean = true; + @JSONStringifyIgnore() private __meta_stringA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_booleanA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"booleanA"}) private __backing_booleanA: boolean = true; - private __backing_arrayA: BackingValue> = new BackingValue>([1, 2, 3]); + @JSONStringifyIgnore() private __meta_booleanA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_arrayA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"arrayA"}) private __backing_arrayA: Array = [1, 2, 3]; - private __backing_objectA: BackingValue = new BackingValue({}); + @JSONStringifyIgnore() private __meta_arrayA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_objectA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"objectA"}) private __backing_objectA: Object = {}; - private __backing_dateA: BackingValue = new BackingValue(new Date("2021-08-08")); + @JSONStringifyIgnore() private __meta_objectA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_dateA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"dateA"}) private __backing_dateA: Date = new Date("2021-08-08"); - private __backing_setA: BackingValue> = new BackingValue>(new Set()); + @JSONStringifyIgnore() private __meta_dateA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_setA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"setA"}) private __backing_setA: Set = new Set(); - private __backing_mapA: BackingValue> = new BackingValue>(new Map()); + @JSONStringifyIgnore() private __meta_setA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_mapA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"mapA"}) private __backing_mapA: Map = new Map(); - private __backing_unionA: string | undefined = ""; + @JSONStringifyIgnore() private __meta_mapA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_unionA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"unionA"}) private __backing_unionA: (string | undefined) = ""; - private __backing_classA: BackingValue = new BackingValue(new Person()); + @JSONStringifyIgnore() private __meta_unionA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_classA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"classA"}) private __backing_classA: Person = new Person(); - private __backing_enumA: BackingValue = new BackingValue(Status.NotFound); + @JSONStringifyIgnore() private __meta_classA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_enumA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"enumA"}) private __backing_enumA: Status = Status.NotFound; + + @JSONStringifyIgnore() private __meta_enumA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); public numB: number = 33; @@ -219,7 +236,7 @@ final class Status extends BaseEnum { public mapB: Map = new Map(); - public unionB: string | undefined = ""; + public unionB: (string | undefined) = ""; public classB: Person = new Person(); @@ -228,181 +245,152 @@ final class Status extends BaseEnum { public constructor() {} public get numA(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_numA.addRef(); - } + this.conditionalAddRef(this.__meta_numA); return this.__backing_numA; } public set numA(newValue: number) { if (((this.__backing_numA) !== (newValue))) { this.__backing_numA = newValue; - this.__meta_numA.fireChange(); - this.executeOnSubscribingWatches("numA"); + this.__meta_numA.fireChange(); + this.executeOnSubscribingWatches("numA"); } } public get stringA(): string { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_stringA.addRef(); - } + this.conditionalAddRef(this.__meta_stringA); return this.__backing_stringA; } public set stringA(newValue: string) { if (((this.__backing_stringA) !== (newValue))) { this.__backing_stringA = newValue; - this.__meta_stringA.fireChange(); - this.executeOnSubscribingWatches("stringA"); + this.__meta_stringA.fireChange(); + this.executeOnSubscribingWatches("stringA"); } } public get booleanA(): boolean { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_booleanA.addRef(); - } + this.conditionalAddRef(this.__meta_booleanA); return this.__backing_booleanA; } public set booleanA(newValue: boolean) { if (((this.__backing_booleanA) !== (newValue))) { this.__backing_booleanA = newValue; - this.__meta_booleanA.fireChange(); - this.executeOnSubscribingWatches("booleanA"); + this.__meta_booleanA.fireChange(); + this.executeOnSubscribingWatches("booleanA"); } } public get arrayA(): Array { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_arrayA.addRef(); - } - setObservationDepth(this.__backing_arrayA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_arrayA.value; + this.conditionalAddRef(this.__meta_arrayA); + return this.__backing_arrayA; } public set arrayA(newValue: Array) { - if (((this.__backing_arrayA.value) !== (newValue))) { - this.__backing_arrayA.value = newValue; - this.__meta_arrayA.fireChange(); - this.executeOnSubscribingWatches("arrayA"); + if (((this.__backing_arrayA) !== (newValue))) { + this.__backing_arrayA = newValue; + this.__meta_arrayA.fireChange(); + this.executeOnSubscribingWatches("arrayA"); } } public get objectA(): Object { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_objectA.addRef(); - } - setObservationDepth(this.__backing_objectA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_objectA.value; + this.conditionalAddRef(this.__meta_objectA); + return this.__backing_objectA; } public set objectA(newValue: Object) { - if (((this.__backing_objectA.value) !== (newValue))) { - this.__backing_objectA.value = newValue; - this.__meta_objectA.fireChange(); - this.executeOnSubscribingWatches("objectA"); + if (((this.__backing_objectA) !== (newValue))) { + this.__backing_objectA = newValue; + this.__meta_objectA.fireChange(); + this.executeOnSubscribingWatches("objectA"); } } public get dateA(): Date { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_dateA.addRef(); - } - setObservationDepth(this.__backing_dateA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_dateA.value; + this.conditionalAddRef(this.__meta_dateA); + return this.__backing_dateA; } public set dateA(newValue: Date) { - if (((this.__backing_dateA.value) !== (newValue))) { - this.__backing_dateA.value = newValue; - this.__meta_dateA.fireChange(); - this.executeOnSubscribingWatches("dateA"); + if (((this.__backing_dateA) !== (newValue))) { + this.__backing_dateA = newValue; + this.__meta_dateA.fireChange(); + this.executeOnSubscribingWatches("dateA"); } } public get setA(): Set { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_setA.addRef(); - } - setObservationDepth(this.__backing_setA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_setA.value; + this.conditionalAddRef(this.__meta_setA); + return this.__backing_setA; } public set setA(newValue: Set) { - if (((this.__backing_setA.value) !== (newValue))) { - this.__backing_setA.value = newValue; - this.__meta_setA.fireChange(); - this.executeOnSubscribingWatches("setA"); + if (((this.__backing_setA) !== (newValue))) { + this.__backing_setA = newValue; + this.__meta_setA.fireChange(); + this.executeOnSubscribingWatches("setA"); } } public get mapA(): Map { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_mapA.addRef(); - } - setObservationDepth(this.__backing_mapA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_mapA.value; + this.conditionalAddRef(this.__meta_mapA); + return this.__backing_mapA; } public set mapA(newValue: Map) { - if (((this.__backing_mapA.value) !== (newValue))) { - this.__backing_mapA.value = newValue; - this.__meta_mapA.fireChange(); - this.executeOnSubscribingWatches("mapA"); + if (((this.__backing_mapA) !== (newValue))) { + this.__backing_mapA = newValue; + this.__meta_mapA.fireChange(); + this.executeOnSubscribingWatches("mapA"); } } - public get unionA(): string | undefined { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_unionA.addRef(); - } + public get unionA(): (string | undefined) { + this.conditionalAddRef(this.__meta_unionA); return this.__backing_unionA; } - public set unionA(newValue: string | undefined) { + public set unionA(newValue: (string | undefined)) { if (((this.__backing_unionA) !== (newValue))) { this.__backing_unionA = newValue; - this.__meta_unionA.fireChange(); - this.executeOnSubscribingWatches("unionA"); + this.__meta_unionA.fireChange(); + this.executeOnSubscribingWatches("unionA"); } } public get classA(): Person { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_classA.addRef(); - } - setObservationDepth(this.__backing_classA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_classA.value; + this.conditionalAddRef(this.__meta_classA); + return this.__backing_classA; } public set classA(newValue: Person) { - if (((this.__backing_classA.value) !== (newValue))) { - this.__backing_classA.value = newValue; - this.__meta_classA.fireChange(); - this.executeOnSubscribingWatches("classA"); + if (((this.__backing_classA) !== (newValue))) { + this.__backing_classA = newValue; + this.__meta_classA.fireChange(); + this.executeOnSubscribingWatches("classA"); } } public get enumA(): Status { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_enumA.addRef(); - } - setObservationDepth(this.__backing_enumA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_enumA.value; + this.conditionalAddRef(this.__meta_enumA); + return this.__backing_enumA; } public set enumA(newValue: Status) { - if (((this.__backing_enumA.value) !== (newValue))) { - this.__backing_enumA.value = newValue; - this.__meta_enumA.fireChange(); - this.executeOnSubscribingWatches("enumA"); + if (((this.__backing_enumA) !== (newValue))) { + this.__backing_enumA = newValue; + this.__meta_enumA.fireChange(); + this.executeOnSubscribingWatches("enumA"); } } } -@Observed() class mixed2 implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class mixed2 implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -416,210 +404,191 @@ final class Status extends BaseEnum { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; - private __meta: MutableStateMeta = new MutableStateMeta("@Observe properties (no @Track)"); + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } - private __backing_numA: number = 33; + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __backing_stringA: string = "AA"; + @JSONRename({newName:"numA"}) private __backing_numA: number = 33; - private __backing_booleanA: boolean = true; + @JSONRename({newName:"stringA"}) private __backing_stringA: string = "AA"; - private __backing_arrayA: BackingValue> = new BackingValue>([1, 2, 3]); + @JSONRename({newName:"booleanA"}) private __backing_booleanA: boolean = true; - private __backing_objectA: BackingValue = new BackingValue({}); + @JSONRename({newName:"arrayA"}) private __backing_arrayA: Array = [1, 2, 3]; - private __backing_dateA: BackingValue = new BackingValue(new Date("2021-08-08")); + @JSONRename({newName:"objectA"}) private __backing_objectA: Object = {}; - private __backing_setA: BackingValue> = new BackingValue>(new Set()); + @JSONRename({newName:"dateA"}) private __backing_dateA: Date = new Date("2021-08-08"); - private __backing_mapA: BackingValue> = new BackingValue>(new Map()); + @JSONRename({newName:"setA"}) private __backing_setA: Set = new Set(); - private __backing_unionA: string | undefined = ""; + @JSONRename({newName:"mapA"}) private __backing_mapA: Map = new Map(); - private __backing_classA: BackingValue = new BackingValue(new Person()); + @JSONRename({newName:"unionA"}) private __backing_unionA: (string | undefined) = ""; - private __backing_enumA: BackingValue = new BackingValue(Status.NotFound); + @JSONRename({newName:"classA"}) private __backing_classA: Person = new Person(); + + @JSONRename({newName:"enumA"}) private __backing_enumA: Status = Status.NotFound; public constructor() {} public get numA(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_numA; } public set numA(newValue: number) { if (((this.__backing_numA) !== (newValue))) { this.__backing_numA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("numA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("numA"); } } public get stringA(): string { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_stringA; } public set stringA(newValue: string) { if (((this.__backing_stringA) !== (newValue))) { this.__backing_stringA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("stringA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("stringA"); } } public get booleanA(): boolean { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_booleanA; } public set booleanA(newValue: boolean) { if (((this.__backing_booleanA) !== (newValue))) { this.__backing_booleanA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("booleanA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("booleanA"); } } public get arrayA(): Array { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_arrayA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_arrayA.value; + this.conditionalAddRef(this.__meta); + return this.__backing_arrayA; } public set arrayA(newValue: Array) { - if (((this.__backing_arrayA.value) !== (newValue))) { - this.__backing_arrayA.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("arrayA"); + if (((this.__backing_arrayA) !== (newValue))) { + this.__backing_arrayA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("arrayA"); } } public get objectA(): Object { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_objectA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_objectA.value; + this.conditionalAddRef(this.__meta); + return this.__backing_objectA; } public set objectA(newValue: Object) { - if (((this.__backing_objectA.value) !== (newValue))) { - this.__backing_objectA.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("objectA"); + if (((this.__backing_objectA) !== (newValue))) { + this.__backing_objectA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("objectA"); } } public get dateA(): Date { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_dateA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_dateA.value; + this.conditionalAddRef(this.__meta); + return this.__backing_dateA; } public set dateA(newValue: Date) { - if (((this.__backing_dateA.value) !== (newValue))) { - this.__backing_dateA.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("dateA"); + if (((this.__backing_dateA) !== (newValue))) { + this.__backing_dateA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("dateA"); } } public get setA(): Set { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_setA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_setA.value; + this.conditionalAddRef(this.__meta); + return this.__backing_setA; } public set setA(newValue: Set) { - if (((this.__backing_setA.value) !== (newValue))) { - this.__backing_setA.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("setA"); + if (((this.__backing_setA) !== (newValue))) { + this.__backing_setA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("setA"); } } public get mapA(): Map { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_mapA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_mapA.value; + this.conditionalAddRef(this.__meta); + return this.__backing_mapA; } public set mapA(newValue: Map) { - if (((this.__backing_mapA.value) !== (newValue))) { - this.__backing_mapA.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("mapA"); + if (((this.__backing_mapA) !== (newValue))) { + this.__backing_mapA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("mapA"); } } - public get unionA(): string | undefined { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + public get unionA(): (string | undefined) { + this.conditionalAddRef(this.__meta); return this.__backing_unionA; } - public set unionA(newValue: string | undefined) { + public set unionA(newValue: (string | undefined)) { if (((this.__backing_unionA) !== (newValue))) { this.__backing_unionA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("unionA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("unionA"); } } public get classA(): Person { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_classA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_classA.value; + this.conditionalAddRef(this.__meta); + return this.__backing_classA; } public set classA(newValue: Person) { - if (((this.__backing_classA.value) !== (newValue))) { - this.__backing_classA.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("classA"); + if (((this.__backing_classA) !== (newValue))) { + this.__backing_classA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("classA"); } } public get enumA(): Status { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_enumA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_enumA.value; + this.conditionalAddRef(this.__meta); + return this.__backing_enumA; } public set enumA(newValue: Status) { - if (((this.__backing_enumA.value) !== (newValue))) { - this.__backing_enumA.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("enumA"); + if (((this.__backing_enumA) !== (newValue))) { + this.__backing_enumA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("enumA"); } } } -class mixed3 implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +class mixed3 implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -633,240 +602,221 @@ class mixed3 implements IObservedObject { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } - private __backing_numA: number = 33; + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } - private __meta_numA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"numA"}) private __backing_numA: number = 33; - private __backing_stringA: string = "AA"; + @JSONStringifyIgnore() private __meta_numA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_stringA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"stringA"}) private __backing_stringA: string = "AA"; - private __backing_booleanA: boolean = true; + @JSONStringifyIgnore() private __meta_stringA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_booleanA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"booleanA"}) private __backing_booleanA: boolean = true; - private __backing_arrayA: BackingValue> = new BackingValue>([1, 2, 3]); + @JSONStringifyIgnore() private __meta_booleanA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_arrayA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"arrayA"}) private __backing_arrayA: Array = [1, 2, 3]; - private __backing_objectA: BackingValue = new BackingValue({}); + @JSONStringifyIgnore() private __meta_arrayA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_objectA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"objectA"}) private __backing_objectA: Object = {}; - private __backing_dateA: BackingValue = new BackingValue(new Date("2021-08-08")); + @JSONStringifyIgnore() private __meta_objectA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_dateA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"dateA"}) private __backing_dateA: Date = new Date("2021-08-08"); - private __backing_setA: BackingValue> = new BackingValue>(new Set()); + @JSONStringifyIgnore() private __meta_dateA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_setA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"setA"}) private __backing_setA: Set = new Set(); - private __backing_mapA: BackingValue> = new BackingValue>(new Map()); + @JSONStringifyIgnore() private __meta_setA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_mapA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"mapA"}) private __backing_mapA: Map = new Map(); - private __backing_unionA: string | undefined = ""; + @JSONStringifyIgnore() private __meta_mapA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_unionA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"unionA"}) private __backing_unionA: (string | undefined) = ""; - private __backing_classA: BackingValue = new BackingValue(new Person()); + @JSONStringifyIgnore() private __meta_unionA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_classA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"classA"}) private __backing_classA: Person = new Person(); - private __backing_enumA: BackingValue = new BackingValue(Status.NotFound); + @JSONStringifyIgnore() private __meta_classA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_enumA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"enumA"}) private __backing_enumA: Status = Status.NotFound; + + @JSONStringifyIgnore() private __meta_enumA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); public constructor() {} public get numA(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_numA.addRef(); - } + this.conditionalAddRef(this.__meta_numA); return this.__backing_numA; } public set numA(newValue: number) { if (((this.__backing_numA) !== (newValue))) { this.__backing_numA = newValue; - this.__meta_numA.fireChange(); - this.executeOnSubscribingWatches("numA"); + this.__meta_numA.fireChange(); + this.executeOnSubscribingWatches("numA"); } } public get stringA(): string { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_stringA.addRef(); - } + this.conditionalAddRef(this.__meta_stringA); return this.__backing_stringA; } public set stringA(newValue: string) { if (((this.__backing_stringA) !== (newValue))) { this.__backing_stringA = newValue; - this.__meta_stringA.fireChange(); - this.executeOnSubscribingWatches("stringA"); + this.__meta_stringA.fireChange(); + this.executeOnSubscribingWatches("stringA"); } } public get booleanA(): boolean { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_booleanA.addRef(); - } + this.conditionalAddRef(this.__meta_booleanA); return this.__backing_booleanA; } public set booleanA(newValue: boolean) { if (((this.__backing_booleanA) !== (newValue))) { this.__backing_booleanA = newValue; - this.__meta_booleanA.fireChange(); - this.executeOnSubscribingWatches("booleanA"); + this.__meta_booleanA.fireChange(); + this.executeOnSubscribingWatches("booleanA"); } } public get arrayA(): Array { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_arrayA.addRef(); - } - setObservationDepth(this.__backing_arrayA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_arrayA.value; + this.conditionalAddRef(this.__meta_arrayA); + return this.__backing_arrayA; } public set arrayA(newValue: Array) { - if (((this.__backing_arrayA.value) !== (newValue))) { - this.__backing_arrayA.value = newValue; - this.__meta_arrayA.fireChange(); - this.executeOnSubscribingWatches("arrayA"); + if (((this.__backing_arrayA) !== (newValue))) { + this.__backing_arrayA = newValue; + this.__meta_arrayA.fireChange(); + this.executeOnSubscribingWatches("arrayA"); } } public get objectA(): Object { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_objectA.addRef(); - } - setObservationDepth(this.__backing_objectA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_objectA.value; + this.conditionalAddRef(this.__meta_objectA); + return this.__backing_objectA; } public set objectA(newValue: Object) { - if (((this.__backing_objectA.value) !== (newValue))) { - this.__backing_objectA.value = newValue; - this.__meta_objectA.fireChange(); - this.executeOnSubscribingWatches("objectA"); + if (((this.__backing_objectA) !== (newValue))) { + this.__backing_objectA = newValue; + this.__meta_objectA.fireChange(); + this.executeOnSubscribingWatches("objectA"); } } public get dateA(): Date { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_dateA.addRef(); - } - setObservationDepth(this.__backing_dateA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_dateA.value; + this.conditionalAddRef(this.__meta_dateA); + return this.__backing_dateA; } public set dateA(newValue: Date) { - if (((this.__backing_dateA.value) !== (newValue))) { - this.__backing_dateA.value = newValue; - this.__meta_dateA.fireChange(); - this.executeOnSubscribingWatches("dateA"); + if (((this.__backing_dateA) !== (newValue))) { + this.__backing_dateA = newValue; + this.__meta_dateA.fireChange(); + this.executeOnSubscribingWatches("dateA"); } } public get setA(): Set { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_setA.addRef(); - } - setObservationDepth(this.__backing_setA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_setA.value; + this.conditionalAddRef(this.__meta_setA); + return this.__backing_setA; } public set setA(newValue: Set) { - if (((this.__backing_setA.value) !== (newValue))) { - this.__backing_setA.value = newValue; - this.__meta_setA.fireChange(); - this.executeOnSubscribingWatches("setA"); + if (((this.__backing_setA) !== (newValue))) { + this.__backing_setA = newValue; + this.__meta_setA.fireChange(); + this.executeOnSubscribingWatches("setA"); } } public get mapA(): Map { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_mapA.addRef(); - } - setObservationDepth(this.__backing_mapA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_mapA.value; + this.conditionalAddRef(this.__meta_mapA); + return this.__backing_mapA; } public set mapA(newValue: Map) { - if (((this.__backing_mapA.value) !== (newValue))) { - this.__backing_mapA.value = newValue; - this.__meta_mapA.fireChange(); - this.executeOnSubscribingWatches("mapA"); + if (((this.__backing_mapA) !== (newValue))) { + this.__backing_mapA = newValue; + this.__meta_mapA.fireChange(); + this.executeOnSubscribingWatches("mapA"); } } - public get unionA(): string | undefined { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_unionA.addRef(); - } + public get unionA(): (string | undefined) { + this.conditionalAddRef(this.__meta_unionA); return this.__backing_unionA; } - public set unionA(newValue: string | undefined) { + public set unionA(newValue: (string | undefined)) { if (((this.__backing_unionA) !== (newValue))) { this.__backing_unionA = newValue; - this.__meta_unionA.fireChange(); - this.executeOnSubscribingWatches("unionA"); + this.__meta_unionA.fireChange(); + this.executeOnSubscribingWatches("unionA"); } } public get classA(): Person { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_classA.addRef(); - } - setObservationDepth(this.__backing_classA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_classA.value; + this.conditionalAddRef(this.__meta_classA); + return this.__backing_classA; } public set classA(newValue: Person) { - if (((this.__backing_classA.value) !== (newValue))) { - this.__backing_classA.value = newValue; - this.__meta_classA.fireChange(); - this.executeOnSubscribingWatches("classA"); + if (((this.__backing_classA) !== (newValue))) { + this.__backing_classA = newValue; + this.__meta_classA.fireChange(); + this.executeOnSubscribingWatches("classA"); } } public get enumA(): Status { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_enumA.addRef(); - } - setObservationDepth(this.__backing_enumA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_enumA.value; + this.conditionalAddRef(this.__meta_enumA); + return this.__backing_enumA; } public set enumA(newValue: Status) { - if (((this.__backing_enumA.value) !== (newValue))) { - this.__backing_enumA.value = newValue; - this.__meta_enumA.fireChange(); - this.executeOnSubscribingWatches("enumA"); + if (((this.__backing_enumA) !== (newValue))) { + this.__backing_enumA = newValue; + this.__meta_enumA.fireChange(); + this.executeOnSubscribingWatches("enumA"); } } } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { } @@ -888,9 +838,9 @@ function testObservedOnlyTransformer(this: PluginTestContext): void { pluginTester.run( 'test observed track transform with complex type', - [observedTrackTransform, uiNoRecheck], + [observedTrackTransform, uiNoRecheck, recheck], { - checked: [testObservedOnlyTransformer], + 'checked:ui-no-recheck': [testObservedOnlyTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-extends.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-extends.test.ts index f3f3a001e..90d7b3ebb 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-extends.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-extends.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,22 @@ const observedTrackTransform: Plugins = { const pluginTester = new PluginTester('test observed track transform with extends', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; @@ -67,8 +65,8 @@ function main() {} -@Observed() class A implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class A implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -82,43 +80,49 @@ function main() {} this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } - private __meta: MutableStateMeta = new MutableStateMeta("@Observe properties (no @Track)"); + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __backing_propA: number = 1; + @JSONRename({newName:"propA"}) private __backing_propA: number = 1; - private __backing_trackA: number = 2; + @JSONRename({newName:"trackA"}) private __backing_trackA: number = 2; public constructor() {} public get propA(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_propA; } public set propA(newValue: number) { if (((this.__backing_propA) !== (newValue))) { this.__backing_propA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("propA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("propA"); } } public get trackA(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_trackA; } public set trackA(newValue: number) { if (((this.__backing_trackA) !== (newValue))) { this.__backing_trackA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("trackA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("trackA"); } } @@ -131,8 +135,8 @@ class G extends A { } -@Observed() class H extends G implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class H extends G implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -146,46 +150,53 @@ class G extends A { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; - private __backing_propG: number = 1; + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } - private __meta_propG: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"propG"}) private __backing_propG: number = 1; + + @JSONStringifyIgnore() private __meta_propG: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); public constructor() {} public get propG(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_propG.addRef(); - } + this.conditionalAddRef(this.__meta_propG); return this.__backing_propG; } public set propG(newValue: number) { if (((this.__backing_propG) !== (newValue))) { this.__backing_propG = newValue; - this.__meta_propG.fireChange(); - this.executeOnSubscribingWatches("propG"); + this.__meta_propG.fireChange(); + this.executeOnSubscribingWatches("propG"); } } } -@Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Component() export interface __Options_MyStateSample { } - `; function testObservedOnlyTransformer(this: PluginTestContext): void { @@ -194,9 +205,9 @@ function testObservedOnlyTransformer(this: PluginTestContext): void { pluginTester.run( 'test observed track transform with extends', - [observedTrackTransform, uiNoRecheck], + [observedTrackTransform, uiNoRecheck, recheck], { - checked: [testObservedOnlyTransformer], + 'checked:ui-no-recheck': [testObservedOnlyTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-implements.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-implements.test.ts index f71a5e922..067c357e0 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-implements.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-implements.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,22 @@ const observedTrackTransform: Plugins = { const pluginTester = new PluginTester('test observed track transform with implements', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; @@ -81,8 +79,8 @@ interface trackInterface { } -@Observed() class F implements PropInterface, trackInterface, IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class F implements PropInterface, trackInterface, IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -96,63 +94,68 @@ interface trackInterface { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } - private __meta: MutableStateMeta = new MutableStateMeta("@Observe properties (no @Track)"); + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } - private __backing_propF: number = 1; + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __backing_trackF: number = 2; + @JSONRename({newName:"propF"}) private __backing_propF: number = 1; + + @JSONRename({newName:"trackF"}) private __backing_trackF: number = 2; public constructor() {} - public set propF(newValue: number) { + set propF(newValue: number) { if (((this.__backing_propF) !== (newValue))) { this.__backing_propF = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("propF"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("propF"); } } public get propF(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_propF; } - public set trackF(newValue: number) { + set trackF(newValue: number) { if (((this.__backing_trackF) !== (newValue))) { this.__backing_trackF = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("trackF"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("trackF"); } } public get trackF(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_trackF; } } -@Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Component() export interface __Options_MyStateSample { } - `; function testObservedOnlyTransformer(this: PluginTestContext): void { @@ -161,9 +164,9 @@ function testObservedOnlyTransformer(this: PluginTestContext): void { pluginTester.run( 'test observed track transform with implements', - [observedTrackTransform, uiNoRecheck], + [observedTrackTransform, uiNoRecheck, recheck], { - checked: [testObservedOnlyTransformer], + 'checked:ui-no-recheck': [testObservedOnlyTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track.test.ts index 9f28609dc..15e871f58 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,22 @@ const observedTrackTransform: Plugins = { const pluginTester = new PluginTester('test observed with track transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; @@ -65,10 +63,8 @@ import { Observed as Observed, Track as Track } from "@ohos.arkui.stateManagemen function main() {} - - -@Observed() class B implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class B implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -82,48 +78,74 @@ function main() {} this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } public propB: number = 1; - private __backing_trackB: number = 2; + @JSONRename({newName:"trackB"}) private __backing_trackB: number = 2; - private __meta_trackB: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONStringifyIgnore() private __meta_trackB: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - public constructor() {} + @JSONRename({newName:"newProp"}) private __backing_newProp?: boolean; + + @JSONStringifyIgnore() private __meta_newProp: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor(newProp: boolean) { + this.newProp = newProp; + } public get trackB(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_trackB.addRef(); - } + this.conditionalAddRef(this.__meta_trackB); return this.__backing_trackB; } public set trackB(newValue: number) { if (((this.__backing_trackB) !== (newValue))) { this.__backing_trackB = newValue; - this.__meta_trackB.fireChange(); - this.executeOnSubscribingWatches("trackB"); + this.__meta_trackB.fireChange(); + this.executeOnSubscribingWatches("trackB"); + } + } + + public get newProp(): boolean { + this.conditionalAddRef(this.__meta_newProp); + return (this.__backing_newProp as boolean); + } + + public set newProp(newValue: boolean) { + if (((this.__backing_newProp) !== (newValue))) { + this.__backing_newProp = newValue; + this.__meta_newProp.fireChange(); + this.executeOnSubscribingWatches("newProp"); } } } -@Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Component() export interface __Options_MyStateSample { } - `; function testObservedOnlyTransformer(this: PluginTestContext): void { @@ -132,9 +154,9 @@ function testObservedOnlyTransformer(this: PluginTestContext): void { pluginTester.run( 'test observed with track transform', - [observedTrackTransform, uiNoRecheck], + [observedTrackTransform, uiNoRecheck, recheck], { - checked: [testObservedOnlyTransformer], + 'checked:ui-no-recheck': [testObservedOnlyTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/track-only.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/track-only.test.ts index a1e60b0fa..574046a3c 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/track-only.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/track-only.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,22 @@ const observedTrackTransform: Plugins = { const pluginTester = new PluginTester('test track only transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; @@ -67,8 +65,8 @@ function main() {} -class C implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +class C implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -82,48 +80,55 @@ class C implements IObservedObject { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } public propC: number = 1; - private __backing_trackC: number = 2; + @JSONRename({newName:"trackC"}) private __backing_trackC: number = 2; - private __meta_trackC: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONStringifyIgnore() private __meta_trackC: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); public constructor() {} public get trackC(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_trackC.addRef(); - } + this.conditionalAddRef(this.__meta_trackC); return this.__backing_trackC; } public set trackC(newValue: number) { if (((this.__backing_trackC) !== (newValue))) { this.__backing_trackC = newValue; - this.__meta_trackC.fireChange(); - this.executeOnSubscribingWatches("trackC"); + this.__meta_trackC.fireChange(); + this.executeOnSubscribingWatches("trackC"); } } } -@Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Component() export interface __Options_MyStateSample { } - `; function testObservedOnlyTransformer(this: PluginTestContext): void { @@ -132,9 +137,9 @@ function testObservedOnlyTransformer(this: PluginTestContext): void { pluginTester.run( 'test track only transform', - [observedTrackTransform, uiNoRecheck], + [observedTrackTransform, uiNoRecheck, recheck], { - checked: [testObservedOnlyTransformer], + 'checked:ui-no-recheck': [testObservedOnlyTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/base-observedv2-trace.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/base-observedv2-trace.test.ts new file mode 100644 index 000000000..e2ce9d195 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/base-observedv2-trace.test.ts @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observedv2-trace'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'base-observedv2-trace.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test basic @ObservedV2 and @Trace case', buildConfig); + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ObservedV2() class CC implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public propB: number = 1; + + @JSONRename({newName:"traceB"}) private __backing_traceB: number = 2; + + @JSONStringifyIgnore() private __meta_traceB: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get traceB(): number { + this.conditionalAddRef(this.__meta_traceB); + return UIUtils.makeObserved(this.__backing_traceB); + } + + public set traceB(newValue: number) { + if (((this.__backing_traceB) !== (newValue))) { + this.__backing_traceB = newValue; + this.__meta_traceB.fireChange(); + this.executeOnSubscribingWatches("traceB"); + } + } + + public constructor() {} + +} + +@ObservedV2() class DD implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public propB: number = 1; + + @JSONRename({newName:"traceE"}) private __backing_traceE: number = 2; + + @JSONStringifyIgnore() private __meta_traceE: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"tracef"}) private __backing_tracef: number = 2; + + @JSONStringifyIgnore() private __meta_tracef: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public vv: string; + + public get traceE(): number { + this.conditionalAddRef(this.__meta_traceE); + return UIUtils.makeObserved(this.__backing_traceE); + } + + public set traceE(newValue: number) { + if (((this.__backing_traceE) !== (newValue))) { + this.__backing_traceE = newValue; + this.__meta_traceE.fireChange(); + this.executeOnSubscribingWatches("traceE"); + } + } + + public get tracef(): number { + this.conditionalAddRef(this.__meta_tracef); + return UIUtils.makeObserved(this.__backing_tracef); + } + + public set tracef(newValue: number) { + if (((this.__backing_tracef) !== (newValue))) { + this.__backing_tracef = newValue; + this.__meta_tracef.fireChange(); + this.executeOnSubscribingWatches("tracef"); + } + } + + public constructor(vv1: string) { + this.vv = vv1; + } + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic @ObservedV2 and @Trace case', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observed-serialization.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observed-serialization.test.ts new file mode 100644 index 000000000..f4091bd26 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observed-serialization.test.ts @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observedv2-trace'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'observedv2-serialization.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @ObservedV2 class serialization', buildConfig); + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Retention({policy:"SOURCE"}) @interface TestDecor {} + +@ObservedV2() class testJSONStringifyIgnore implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public var1: number = 1; + + @JSONRename({newName:"var2"}) private __backing_var2: number = 2; + + @JSONStringifyIgnore() private __meta_var2: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONStringifyIgnore() public var3: number = 3; + + @TestDecor() public var4: number = 4; + + @JSONStringifyIgnore() private __backing_var5: number = 5; + + @JSONStringifyIgnore() private __meta_var5: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONStringifyIgnore() @TestDecor() public var6: number = 6; + + @TestDecor() @JSONRename({newName:"var7"}) private __backing_var7: number = 7; + + @JSONStringifyIgnore() private __meta_var7: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONStringifyIgnore() @TestDecor() private __backing_var8: number = 8; + + @JSONStringifyIgnore() private __meta_var8: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get var2(): number { + this.conditionalAddRef(this.__meta_var2); + return UIUtils.makeObserved(this.__backing_var2); + } + + public set var2(newValue: number) { + if (((this.__backing_var2) !== (newValue))) { + this.__backing_var2 = newValue; + this.__meta_var2.fireChange(); + this.executeOnSubscribingWatches("var2"); + } + } + + public get var5(): number { + this.conditionalAddRef(this.__meta_var5); + return UIUtils.makeObserved(this.__backing_var5); + } + + public set var5(newValue: number) { + if (((this.__backing_var5) !== (newValue))) { + this.__backing_var5 = newValue; + this.__meta_var5.fireChange(); + this.executeOnSubscribingWatches("var5"); + } + } + + public get var7(): number { + this.conditionalAddRef(this.__meta_var7); + return UIUtils.makeObserved(this.__backing_var7); + } + + public set var7(newValue: number) { + if (((this.__backing_var7) !== (newValue))) { + this.__backing_var7 = newValue; + this.__meta_var7.fireChange(); + this.executeOnSubscribingWatches("var7"); + } + } + + public get var8(): number { + this.conditionalAddRef(this.__meta_var8); + return UIUtils.makeObserved(this.__backing_var8); + } + + public set var8(newValue: number) { + if (((this.__backing_var8) !== (newValue))) { + this.__backing_var8 = newValue; + this.__meta_var8.fireChange(); + this.executeOnSubscribingWatches("var8"); + } + } + + public constructor() {} + +} + +@ObservedV2() class testJsonRename implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public var1: number = 1; + + @JSONRename({newName:"var2"}) private __backing_var2: number = 2; + + @JSONStringifyIgnore() private __meta_var2: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({value:"name3"}) public var3: number = 3; + + @TestDecor() public var4: number = 4; + + @JSONRename({value:"name5"}) private __backing_var5: number = 5; + + @JSONStringifyIgnore() private __meta_var5: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({value:"name6"}) @TestDecor() public var6: number = 6; + + @TestDecor() @JSONRename({newName:"var7"}) private __backing_var7: number = 7; + + @JSONStringifyIgnore() private __meta_var7: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({value:"name8"}) @TestDecor() private __backing_var8: number = 8; + + @JSONStringifyIgnore() private __meta_var8: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get var2(): number { + this.conditionalAddRef(this.__meta_var2); + return UIUtils.makeObserved(this.__backing_var2); + } + + public set var2(newValue: number) { + if (((this.__backing_var2) !== (newValue))) { + this.__backing_var2 = newValue; + this.__meta_var2.fireChange(); + this.executeOnSubscribingWatches("var2"); + } + } + + public get var5(): number { + this.conditionalAddRef(this.__meta_var5); + return UIUtils.makeObserved(this.__backing_var5); + } + + public set var5(newValue: number) { + if (((this.__backing_var5) !== (newValue))) { + this.__backing_var5 = newValue; + this.__meta_var5.fireChange(); + this.executeOnSubscribingWatches("var5"); + } + } + + public get var7(): number { + this.conditionalAddRef(this.__meta_var7); + return UIUtils.makeObserved(this.__backing_var7); + } + + public set var7(newValue: number) { + if (((this.__backing_var7) !== (newValue))) { + this.__backing_var7 = newValue; + this.__meta_var7.fireChange(); + this.executeOnSubscribingWatches("var7"); + } + } + + public get var8(): number { + this.conditionalAddRef(this.__meta_var8); + return UIUtils.makeObserved(this.__backing_var8); + } + + public set var8(newValue: number) { + if (((this.__backing_var8) !== (newValue))) { + this.__backing_var8 = newValue; + this.__meta_var8.fireChange(); + this.executeOnSubscribingWatches("var8"); + } + } + + public constructor() {} + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @ObservedV2 class serialization', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-extends.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-extends.test.ts new file mode 100644 index 000000000..f77d52bc0 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-extends.test.ts @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observedv2-trace'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'observedv2-trace-extends.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @ObservedV2 extends class', buildConfig); + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ObservedV2() class A implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public propA: number = 1; + + @JSONRename({newName:"traceA"}) private __backing_traceA: number = 2; + + @JSONStringifyIgnore() private __meta_traceA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get traceA(): number { + this.conditionalAddRef(this.__meta_traceA); + return UIUtils.makeObserved(this.__backing_traceA); + } + + public set traceA(newValue: number) { + if (((this.__backing_traceA) !== (newValue))) { + this.__backing_traceA = newValue; + this.__meta_traceA.fireChange(); + this.executeOnSubscribingWatches("traceA"); + } + } + + public constructor() {} + +} + +class G extends A { + public propG: number = 1; + + public constructor() {} + +} + +@ObservedV2() class H extends G implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"propG"}) private __backing_propG: number = 1; + + @JSONStringifyIgnore() private __meta_propG: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get propG(): number { + this.conditionalAddRef(this.__meta_propG); + return UIUtils.makeObserved(this.__backing_propG); + } + + public set propG(newValue: number) { + if (((this.__backing_propG) !== (newValue))) { + this.__backing_propG = newValue; + this.__meta_propG.fireChange(); + this.executeOnSubscribingWatches("propG"); + } + } + + public constructor() {} + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @ObservedV2 extends class', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-implements.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-implements.test.ts new file mode 100644 index 000000000..c1e2137ce --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-implements.test.ts @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observedv2-trace'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'observedv2-trace-implements.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @ObservedV2 implements interface', buildConfig); + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + +interface PropInterface { + set propF(propF: number) + + get propF(): number + +} + +interface trackInterface { + set trackF(trackF: number) + + get trackF(): number + +} + +@ObservedV2() class F implements PropInterface, trackInterface, IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"bb"}) private __backing_bb?: boolean; + + @JSONStringifyIgnore() private __meta_bb: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"propF"}) private __backing_propF: number = 1; + + @JSONStringifyIgnore() private __meta_propF: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + private _$property$_trackF: number = 2; + + set propF(newValue: number) { + if (((this.__backing_propF) !== (newValue))) { + this.__backing_propF = newValue; + this.__meta_propF.fireChange(); + this.executeOnSubscribingWatches("propF"); + } + } + + public get propF(): number { + this.conditionalAddRef(this.__meta_propF); + return UIUtils.makeObserved(this.__backing_propF); + } + + set trackF(_$property$_trackF: number) { + this._$property$_trackF = _$property$_trackF; + return; + } + + public get trackF(): number { + return this._$property$_trackF; + } + + public get bb(): boolean { + this.conditionalAddRef(this.__meta_bb); + return UIUtils.makeObserved((this.__backing_bb as boolean)); + } + + public set bb(newValue: boolean) { + if (((this.__backing_bb) !== (newValue))) { + this.__backing_bb = newValue; + this.__meta_bb.fireChange(); + this.executeOnSubscribingWatches("bb"); + } + } + + public constructor(bb: boolean) { + this.bb = bb; + } + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @ObservedV2 implements interface', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-types.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-types.test.ts new file mode 100644 index 000000000..11b567e4f --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-types.test.ts @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observedv2-trace'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'observedv2-trace-types.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @ObservedV2 class with different types class property', buildConfig); + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + +class Person { + public constructor() {} + +} + +final class Status extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly Success: Status = new Status(0, 200); + + public static readonly NotFound: Status = new Status(1, 404); + + public static readonly ServerError: Status = new Status(2, 500); + + private static readonly #NamesArray: String[] = ["Success", "NotFound", "ServerError"]; + + private static readonly #ValuesArray: int[] = [200, 404, 500]; + + private static readonly #StringValuesArray: String[] = ["200", "404", "500"]; + + private static readonly #ItemsArray: Status[] = [Status.Success, Status.NotFound, Status.ServerError]; + + public getName(): String { + return Status.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): Status { + for (let i = 0;((i) < (Status.#NamesArray.length));(++i)) { + if (((name) == (Status.#NamesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum constant Status.") + (name))); + } + + public static fromValue(value: int): Status { + for (let i = 0;((i) < (Status.#ValuesArray.length));(++i)) { + if (((value) == (Status.#ValuesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum Status with value ") + (value))); + } + + public valueOf(): int { + return Status.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return Status.#StringValuesArray[this.#ordinal]; + } + + public static values(): Status[] { + return Status.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: Status): String { + return e.getName(); + } + +} + +@ObservedV2() class mixed1 implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"numA"}) private __backing_numA: number = 33; + + @JSONStringifyIgnore() private __meta_numA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"stringA"}) private __backing_stringA: string = "AA"; + + @JSONStringifyIgnore() private __meta_stringA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"booleanA"}) private __backing_booleanA: boolean = true; + + @JSONStringifyIgnore() private __meta_booleanA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"arrayA"}) private __backing_arrayA: Array = [1, 2, 3]; + + @JSONStringifyIgnore() private __meta_arrayA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"objectA"}) private __backing_objectA: Object = {}; + + @JSONStringifyIgnore() private __meta_objectA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"dateA"}) private __backing_dateA: Date = new Date("2021-08-08"); + + @JSONStringifyIgnore() private __meta_dateA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"setA"}) private __backing_setA: Set = new Set(); + + @JSONStringifyIgnore() private __meta_setA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"mapA"}) private __backing_mapA: Map = new Map(); + + @JSONStringifyIgnore() private __meta_mapA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"unionA"}) private __backing_unionA: (string | undefined) = ""; + + @JSONStringifyIgnore() private __meta_unionA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"classA"}) private __backing_classA: Person = new Person(); + + @JSONStringifyIgnore() private __meta_classA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"enumA"}) private __backing_enumA: Status = Status.NotFound; + + @JSONStringifyIgnore() private __meta_enumA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public numB: number = 33; + + public stringB: string = "AA"; + + public booleanB: boolean = true; + + public arrayB: Array = [1, 2, 3]; + + public objectB: Object = {}; + + public dateB: Date = new Date("2021-08-08"); + + public setB: Set = new Set(); + + public mapB: Map = new Map(); + + public unionB: (string | undefined) = ""; + + public classB: Person = new Person(); + + public enumB: Status = Status.NotFound; + + public get numA(): number { + this.conditionalAddRef(this.__meta_numA); + return UIUtils.makeObserved(this.__backing_numA); + } + + public set numA(newValue: number) { + if (((this.__backing_numA) !== (newValue))) { + this.__backing_numA = newValue; + this.__meta_numA.fireChange(); + this.executeOnSubscribingWatches("numA"); + } + } + + public get stringA(): string { + this.conditionalAddRef(this.__meta_stringA); + return UIUtils.makeObserved(this.__backing_stringA); + } + + public set stringA(newValue: string) { + if (((this.__backing_stringA) !== (newValue))) { + this.__backing_stringA = newValue; + this.__meta_stringA.fireChange(); + this.executeOnSubscribingWatches("stringA"); + } + } + + public get booleanA(): boolean { + this.conditionalAddRef(this.__meta_booleanA); + return UIUtils.makeObserved(this.__backing_booleanA); + } + + public set booleanA(newValue: boolean) { + if (((this.__backing_booleanA) !== (newValue))) { + this.__backing_booleanA = newValue; + this.__meta_booleanA.fireChange(); + this.executeOnSubscribingWatches("booleanA"); + } + } + + public get arrayA(): Array { + this.conditionalAddRef(this.__meta_arrayA); + return UIUtils.makeObserved(this.__backing_arrayA); + } + + public set arrayA(newValue: Array) { + if (((this.__backing_arrayA) !== (newValue))) { + this.__backing_arrayA = newValue; + this.__meta_arrayA.fireChange(); + this.executeOnSubscribingWatches("arrayA"); + } + } + + public get objectA(): Object { + this.conditionalAddRef(this.__meta_objectA); + return UIUtils.makeObserved(this.__backing_objectA); + } + + public set objectA(newValue: Object) { + if (((this.__backing_objectA) !== (newValue))) { + this.__backing_objectA = newValue; + this.__meta_objectA.fireChange(); + this.executeOnSubscribingWatches("objectA"); + } + } + + public get dateA(): Date { + this.conditionalAddRef(this.__meta_dateA); + return UIUtils.makeObserved(this.__backing_dateA); + } + + public set dateA(newValue: Date) { + if (((this.__backing_dateA) !== (newValue))) { + this.__backing_dateA = newValue; + this.__meta_dateA.fireChange(); + this.executeOnSubscribingWatches("dateA"); + } + } + + public get setA(): Set { + this.conditionalAddRef(this.__meta_setA); + return UIUtils.makeObserved(this.__backing_setA); + } + + public set setA(newValue: Set) { + if (((this.__backing_setA) !== (newValue))) { + this.__backing_setA = newValue; + this.__meta_setA.fireChange(); + this.executeOnSubscribingWatches("setA"); + } + } + + public get mapA(): Map { + this.conditionalAddRef(this.__meta_mapA); + return UIUtils.makeObserved(this.__backing_mapA); + } + + public set mapA(newValue: Map) { + if (((this.__backing_mapA) !== (newValue))) { + this.__backing_mapA = newValue; + this.__meta_mapA.fireChange(); + this.executeOnSubscribingWatches("mapA"); + } + } + + public get unionA(): (string | undefined) { + this.conditionalAddRef(this.__meta_unionA); + return UIUtils.makeObserved(this.__backing_unionA); + } + + public set unionA(newValue: (string | undefined)) { + if (((this.__backing_unionA) !== (newValue))) { + this.__backing_unionA = newValue; + this.__meta_unionA.fireChange(); + this.executeOnSubscribingWatches("unionA"); + } + } + + public get classA(): Person { + this.conditionalAddRef(this.__meta_classA); + return UIUtils.makeObserved(this.__backing_classA); + } + + public set classA(newValue: Person) { + if (((this.__backing_classA) !== (newValue))) { + this.__backing_classA = newValue; + this.__meta_classA.fireChange(); + this.executeOnSubscribingWatches("classA"); + } + } + + public get enumA(): Status { + this.conditionalAddRef(this.__meta_enumA); + return UIUtils.makeObserved(this.__backing_enumA); + } + + public set enumA(newValue: Status) { + if (((this.__backing_enumA) !== (newValue))) { + this.__backing_enumA = newValue; + this.__meta_enumA.fireChange(); + this.executeOnSubscribingWatches("enumA"); + } + } + + public constructor() {} + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @ObservedV2 class with different types class property', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/static-observedv2-trace.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/static-observedv2-trace.test.ts new file mode 100644 index 000000000..5205a28ae --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/static-observedv2-trace.test.ts @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observedv2-trace'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'static-observedv2-trace.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test static @Trace variable case', buildConfig); + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ObservedV2() class CC implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public propB: number = 1; + + public static propA: number = 1; + + @JSONRename({newName:"traceB"}) public static __backing_traceB: number = 2; + + @JSONStringifyIgnore() public static __meta_traceB: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + static { + + } + public static get traceB(): number { + CC.__meta_traceB.addRef(); + return UIUtils.makeObserved(CC.__backing_traceB); + } + + public static set traceB(newValue: number) { + if (((CC.__backing_traceB) !== (newValue))) { + CC.__backing_traceB = newValue; + CC.__meta_traceB.fireChange(); + } + } + + public constructor() {} + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test static @Trace variable case', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/trace-with-constructor.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/trace-with-constructor.test.ts new file mode 100644 index 000000000..facbf02cf --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/trace-with-constructor.test.ts @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observedv2-trace'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'trace-with-constructor.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test basic @Trace decorated variable initialized with constuctor', buildConfig); + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ObservedV2() class FF implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public propB: number = 1; + + @JSONRename({newName:"traceE"}) private __backing_traceE: number = 2; + + @JSONStringifyIgnore() private __meta_traceE: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"vv"}) private __backing_vv?: string; + + @JSONStringifyIgnore() private __meta_vv: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get traceE(): number { + this.conditionalAddRef(this.__meta_traceE); + return UIUtils.makeObserved(this.__backing_traceE); + } + + public set traceE(newValue: number) { + if (((this.__backing_traceE) !== (newValue))) { + this.__backing_traceE = newValue; + this.__meta_traceE.fireChange(); + this.executeOnSubscribingWatches("traceE"); + } + } + + public get vv(): string { + this.conditionalAddRef(this.__meta_vv); + return UIUtils.makeObserved((this.__backing_vv as string)); + } + + public set vv(newValue: string) { + if (((this.__backing_vv) !== (newValue))) { + this.__backing_vv = newValue; + this.__meta_vv.fireChange(); + this.executeOnSubscribingWatches("vv"); + } + } + + public constructor(vv1: string) { + this.vv = vv1; + } + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic @Trace decorated variable initialized with constuctor', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/once/once-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-basic-type.test.ts new file mode 100644 index 000000000..9b2e68cca --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-basic-type.test.ts @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/once'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'once-basic-type.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test basic type @Once decorated variables transformation', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IParamOnceDecoratedVariable as IParamOnceDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Param as Param, Once as Once } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_onceVar1 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar1", ((({let gensym___35048285 = initializers; + (((gensym___35048285) == (null)) ? undefined : gensym___35048285.onceVar1)})) ?? ("stateVar1"))); + this.__backing_onceVar2 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar2", ((({let gensym___241100287 = initializers; + (((gensym___241100287) == (null)) ? undefined : gensym___241100287.onceVar2)})) ?? (50))); + this.__backing_onceVar3 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar3", ((({let gensym___114153851 = initializers; + (((gensym___114153851) == (null)) ? undefined : gensym___114153851.onceVar3)})) ?? (true))); + this.__backing_onceVar4 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar4", ((({let gensym___18636164 = initializers; + (((gensym___18636164) == (null)) ? undefined : gensym___18636164.onceVar4)})) ?? (undefined))); + this.__backing_onceVar5 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar5", ((({let gensym___60128231 = initializers; + (((gensym___60128231) == (null)) ? undefined : gensym___60128231.onceVar5)})) ?? (null))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_onceVar1?: IParamOnceDecoratedVariable; + + public get onceVar1(): string { + return this.__backing_onceVar1!.get(); + } + + public set onceVar1(value: string) { + this.__backing_onceVar1!.set(value); + } + + private __backing_onceVar2?: IParamOnceDecoratedVariable; + + public get onceVar2(): number { + return this.__backing_onceVar2!.get(); + } + + public set onceVar2(value: number) { + this.__backing_onceVar2!.set(value); + } + + private __backing_onceVar3?: IParamOnceDecoratedVariable; + + public get onceVar3(): boolean { + return this.__backing_onceVar3!.get(); + } + + public set onceVar3(value: boolean) { + this.__backing_onceVar3!.set(value); + } + + private __backing_onceVar4?: IParamOnceDecoratedVariable; + + public get onceVar4(): undefined { + return this.__backing_onceVar4!.get(); + } + + public set onceVar4(value: undefined) { + this.__backing_onceVar4!.set(value); + } + + private __backing_onceVar5?: IParamOnceDecoratedVariable; + + public get onceVar5(): null { + return this.__backing_onceVar5!.get(); + } + + public set onceVar5(value: null) { + this.__backing_onceVar5!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set onceVar1(onceVar1: (string | undefined)) + + get onceVar1(): (string | undefined) + @Param() set __backing_onceVar1(__backing_onceVar1: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar1(): (IParamOnceDecoratedVariable | undefined) + set onceVar2(onceVar2: (number | undefined)) + + get onceVar2(): (number | undefined) + @Param() set __backing_onceVar2(__backing_onceVar2: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar2(): (IParamOnceDecoratedVariable | undefined) + set onceVar3(onceVar3: (boolean | undefined)) + + get onceVar3(): (boolean | undefined) + @Param() set __backing_onceVar3(__backing_onceVar3: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar3(): (IParamOnceDecoratedVariable | undefined) + set onceVar4(onceVar4: (undefined | undefined)) + + get onceVar4(): (undefined | undefined) + @Param() set __backing_onceVar4(__backing_onceVar4: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar4(): (IParamOnceDecoratedVariable | undefined) + set onceVar5(onceVar5: (null | undefined)) + + get onceVar5(): (null | undefined) + @Param() set __backing_onceVar5(__backing_onceVar5: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar5(): (IParamOnceDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic type @Once decorated variables transformation', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/once/once-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-complex-type.test.ts new file mode 100644 index 000000000..e044f9ed1 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-complex-type.test.ts @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/once'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'once-complex-type.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test complex type @Once decorated variables transformation', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IParamOnceDecoratedVariable as IParamOnceDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Param as Param, Once as Once } from "@ohos.arkui.stateManagement"; + +function main() {} + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class StateType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: StateType = new StateType(0, 0); + + public static readonly TYPE2: StateType = new StateType(1, 1); + + public static readonly TYPE3: StateType = new StateType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: StateType[] = [StateType.TYPE1, StateType.TYPE2, StateType.TYPE3]; + + public getName(): String { + return StateType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): StateType { + for (let i = 0;((i) < (StateType.#NamesArray.length));(++i)) { + if (((name) == (StateType.#NamesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant StateType.") + (name))); + } + + public static fromValue(value: int): StateType { + for (let i = 0;((i) < (StateType.#ValuesArray.length));(++i)) { + if (((value) == (StateType.#ValuesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum StateType with value ") + (value))); + } + + public valueOf(): int { + return StateType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return StateType.#StringValuesArray[this.#ordinal]; + } + + public static values(): StateType[] { + return StateType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: StateType): String { + return e.getName(); + } + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_onceVar1 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar1", ((({let gensym___35048285 = initializers; + (((gensym___35048285) == (null)) ? undefined : gensym___35048285.onceVar1)})) ?? (new Per(6)))); + this.__backing_onceVar2 = STATE_MGMT_FACTORY.makeParamOnce>(this, "onceVar2", ((({let gensym___241100287 = initializers; + (((gensym___241100287) == (null)) ? undefined : gensym___241100287.onceVar2)})) ?? (new Array(3, 6, 8)))); + this.__backing_onceVar3 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar3", ((({let gensym___114153851 = initializers; + (((gensym___114153851) == (null)) ? undefined : gensym___114153851.onceVar3)})) ?? (StateType.TYPE3))); + this.__backing_onceVar4 = STATE_MGMT_FACTORY.makeParamOnce>(this, "onceVar4", ((({let gensym___18636164 = initializers; + (((gensym___18636164) == (null)) ? undefined : gensym___18636164.onceVar4)})) ?? (new Set(new Array("aa", "bb"))))); + this.__backing_onceVar5 = STATE_MGMT_FACTORY.makeParamOnce>(this, "onceVar5", ((({let gensym___60128231 = initializers; + (((gensym___60128231) == (null)) ? undefined : gensym___60128231.onceVar5)})) ?? ([true, false]))); + this.__backing_onceVar6 = STATE_MGMT_FACTORY.makeParamOnce>(this, "onceVar6", ((({let gensym___266500361 = initializers; + (((gensym___266500361) == (null)) ? undefined : gensym___266500361.onceVar6)})) ?? (new Array(new Per(7), new Per(11))))); + this.__backing_onceVar7 = STATE_MGMT_FACTORY.makeParamOnce>(this, "onceVar7", ((({let gensym___265659510 = initializers; + (((gensym___265659510) == (null)) ? undefined : gensym___265659510.onceVar7)})) ?? ([new Per(7), new Per(11)]))); + this.__backing_onceVar8 = STATE_MGMT_FACTORY.makeParamOnce<((sr: string)=> void)>(this, "onceVar8", ((({let gensym___258402603 = initializers; + (((gensym___258402603) == (null)) ? undefined : gensym___258402603.onceVar8)})) ?? (((sr: string) => {})))); + this.__backing_onceVar9 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar9", ((({let gensym___90206878 = initializers; + (((gensym___90206878) == (null)) ? undefined : gensym___90206878.onceVar9)})) ?? (new Date("2025-4-23")))); + this.__backing_onceVar10 = STATE_MGMT_FACTORY.makeParamOnce>(this, "onceVar10", ((({let gensym___76706777 = initializers; + (((gensym___76706777) == (null)) ? undefined : gensym___76706777.onceVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]])))); + this.__backing_onceVar11 = STATE_MGMT_FACTORY.makeParamOnce<(string | number)>(this, "onceVar11", ((({let gensym___188373395 = initializers; + (((gensym___188373395) == (null)) ? undefined : gensym___188373395.onceVar11)})) ?? (0.0))); + this.__backing_onceVar12 = STATE_MGMT_FACTORY.makeParamOnce<(Set | Per)>(this, "onceVar12", ((({let gensym___207702762 = initializers; + (((gensym___207702762) == (null)) ? undefined : gensym___207702762.onceVar12)})) ?? (new Per(6)))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_onceVar1?: IParamOnceDecoratedVariable; + + public get onceVar1(): Per { + return this.__backing_onceVar1!.get(); + } + + public set onceVar1(value: Per) { + this.__backing_onceVar1!.set(value); + } + + private __backing_onceVar2?: IParamOnceDecoratedVariable>; + + public get onceVar2(): Array { + return this.__backing_onceVar2!.get(); + } + + public set onceVar2(value: Array) { + this.__backing_onceVar2!.set(value); + } + + private __backing_onceVar3?: IParamOnceDecoratedVariable; + + public get onceVar3(): StateType { + return this.__backing_onceVar3!.get(); + } + + public set onceVar3(value: StateType) { + this.__backing_onceVar3!.set(value); + } + + private __backing_onceVar4?: IParamOnceDecoratedVariable>; + + public get onceVar4(): Set { + return this.__backing_onceVar4!.get(); + } + + public set onceVar4(value: Set) { + this.__backing_onceVar4!.set(value); + } + + private __backing_onceVar5?: IParamOnceDecoratedVariable>; + + public get onceVar5(): Array { + return this.__backing_onceVar5!.get(); + } + + public set onceVar5(value: Array) { + this.__backing_onceVar5!.set(value); + } + + private __backing_onceVar6?: IParamOnceDecoratedVariable>; + + public get onceVar6(): Array { + return this.__backing_onceVar6!.get(); + } + + public set onceVar6(value: Array) { + this.__backing_onceVar6!.set(value); + } + + private __backing_onceVar7?: IParamOnceDecoratedVariable>; + + public get onceVar7(): Array { + return this.__backing_onceVar7!.get(); + } + + public set onceVar7(value: Array) { + this.__backing_onceVar7!.set(value); + } + + private __backing_onceVar8?: IParamOnceDecoratedVariable<((sr: string)=> void)>; + + public get onceVar8(): ((sr: string)=> void) { + return this.__backing_onceVar8!.get(); + } + + public set onceVar8(value: ((sr: string)=> void)) { + this.__backing_onceVar8!.set(value); + } + + private __backing_onceVar9?: IParamOnceDecoratedVariable; + + public get onceVar9(): Date { + return this.__backing_onceVar9!.get(); + } + + public set onceVar9(value: Date) { + this.__backing_onceVar9!.set(value); + } + + private __backing_onceVar10?: IParamOnceDecoratedVariable>; + + public get onceVar10(): Map { + return this.__backing_onceVar10!.get(); + } + + public set onceVar10(value: Map) { + this.__backing_onceVar10!.set(value); + } + + private __backing_onceVar11?: IParamOnceDecoratedVariable<(string | number)>; + + public get onceVar11(): (string | number) { + return this.__backing_onceVar11!.get(); + } + + public set onceVar11(value: (string | number)) { + this.__backing_onceVar11!.set(value); + } + + private __backing_onceVar12?: IParamOnceDecoratedVariable<(Set | Per)>; + + public get onceVar12(): (Set | Per) { + return this.__backing_onceVar12!.get(); + } + + public set onceVar12(value: (Set | Per)) { + this.__backing_onceVar12!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set onceVar1(onceVar1: (Per | undefined)) + + get onceVar1(): (Per | undefined) + @Param() set __backing_onceVar1(__backing_onceVar1: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar1(): (IParamOnceDecoratedVariable | undefined) + set onceVar2(onceVar2: (Array | undefined)) + + get onceVar2(): (Array | undefined) + @Param() set __backing_onceVar2(__backing_onceVar2: (IParamOnceDecoratedVariable> | undefined)) + + @Param() get __backing_onceVar2(): (IParamOnceDecoratedVariable> | undefined) + set onceVar3(onceVar3: (StateType | undefined)) + + get onceVar3(): (StateType | undefined) + @Param() set __backing_onceVar3(__backing_onceVar3: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar3(): (IParamOnceDecoratedVariable | undefined) + set onceVar4(onceVar4: (Set | undefined)) + + get onceVar4(): (Set | undefined) + @Param() set __backing_onceVar4(__backing_onceVar4: (IParamOnceDecoratedVariable> | undefined)) + + @Param() get __backing_onceVar4(): (IParamOnceDecoratedVariable> | undefined) + set onceVar5(onceVar5: (Array | undefined)) + + get onceVar5(): (Array | undefined) + @Param() set __backing_onceVar5(__backing_onceVar5: (IParamOnceDecoratedVariable> | undefined)) + + @Param() get __backing_onceVar5(): (IParamOnceDecoratedVariable> | undefined) + set onceVar6(onceVar6: (Array | undefined)) + + get onceVar6(): (Array | undefined) + @Param() set __backing_onceVar6(__backing_onceVar6: (IParamOnceDecoratedVariable> | undefined)) + + @Param() get __backing_onceVar6(): (IParamOnceDecoratedVariable> | undefined) + set onceVar7(onceVar7: (Array | undefined)) + + get onceVar7(): (Array | undefined) + @Param() set __backing_onceVar7(__backing_onceVar7: (IParamOnceDecoratedVariable> | undefined)) + + @Param() get __backing_onceVar7(): (IParamOnceDecoratedVariable> | undefined) + set onceVar8(onceVar8: (((sr: string)=> void) | undefined)) + + get onceVar8(): (((sr: string)=> void) | undefined) + @Param() set __backing_onceVar8(__backing_onceVar8: (IParamOnceDecoratedVariable<((sr: string)=> void)> | undefined)) + + @Param() get __backing_onceVar8(): (IParamOnceDecoratedVariable<((sr: string)=> void)> | undefined) + set onceVar9(onceVar9: (Date | undefined)) + + get onceVar9(): (Date | undefined) + @Param() set __backing_onceVar9(__backing_onceVar9: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar9(): (IParamOnceDecoratedVariable | undefined) + set onceVar10(onceVar10: (Map | undefined)) + + get onceVar10(): (Map | undefined) + @Param() set __backing_onceVar10(__backing_onceVar10: (IParamOnceDecoratedVariable> | undefined)) + + @Param() get __backing_onceVar10(): (IParamOnceDecoratedVariable> | undefined) + set onceVar11(onceVar11: ((string | number) | undefined)) + + get onceVar11(): ((string | number) | undefined) + @Param() set __backing_onceVar11(__backing_onceVar11: (IParamOnceDecoratedVariable<(string | number)> | undefined)) + + @Param() get __backing_onceVar11(): (IParamOnceDecoratedVariable<(string | number)> | undefined) + set onceVar12(onceVar12: ((Set | Per) | undefined)) + + get onceVar12(): ((Set | Per) | undefined) + @Param() set __backing_onceVar12(__backing_onceVar12: (IParamOnceDecoratedVariable<(Set | Per)> | undefined)) + + @Param() get __backing_onceVar12(): (IParamOnceDecoratedVariable<(Set | Per)> | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test complex type @Once decorated variables transformation', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/once/once-only.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-only.test.ts new file mode 100644 index 000000000..db04b7b65 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-only.test.ts @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/once'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'once-only.ets'), +]; + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test only @Once decorated variables transformation', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; +import { IParamOnceDecoratedVariable as IParamOnceDecoratedVariable } from "arkui.stateManagement.decorator"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; +import { Once as Once } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ComponentV2() final struct Child extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_onceParamNum = STATE_MGMT_FACTORY.makeParamOnce(this, "onceParamNum", ((({let gensym___118919021 = initializers; + (((gensym___118919021) == (null)) ? undefined : gensym___118919021.onceParamNum)})) ?? (0))); + this.__backing_onceVar4 = STATE_MGMT_FACTORY.makeParamOnce>(this, "onceVar4", ((({let gensym___71001521 = initializers; + (((gensym___71001521) == (null)) ? undefined : gensym___71001521.onceVar4)})) ?? (new Set(new Array("aa", "bb"))))); + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_onceParamNum?: IParamOnceDecoratedVariable; + + public get onceParamNum(): number { + return this.__backing_onceParamNum!.get(); + } + + public set onceParamNum(value: number) { + this.__backing_onceParamNum!.set(value); + } + + private __backing_onceVar4?: IParamOnceDecoratedVariable>; + + public get onceVar4(): Set { + return this.__backing_onceVar4!.get(); + } + + public set onceVar4(value: Set) { + this.__backing_onceVar4!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Child { + set onceParamNum(onceParamNum: (number | undefined)) + + get onceParamNum(): (number | undefined) + set __backing_onceParamNum(__backing_onceParamNum: (IParamOnceDecoratedVariable | undefined)) + + get __backing_onceParamNum(): (IParamOnceDecoratedVariable | undefined) + set onceVar4(onceVar4: (Set | undefined)) + + get onceVar4(): (Set | undefined) + set __backing_onceVar4(__backing_onceVar4: (IParamOnceDecoratedVariable> | undefined)) + + get __backing_onceVar4(): (IParamOnceDecoratedVariable> | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test only @Once decorated variables transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/once/once-with-require.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-with-require.test.ts new file mode 100644 index 000000000..9dee701f4 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-with-require.test.ts @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/once'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'once-with-require.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @Once Decorator with @Require', buildConfig); + +const expectedParsedScript: string = ` +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Text as Text, Button as Button } from "@ohos.arkui.component"; + +import { Param as Param, Once as Once, ObservedV2 as ObservedV2, Trace as Trace, Require as Require, Local as Local } from "@ohos.arkui.stateManagement"; + +@ObservedV2() class Info { + @Trace() public name: string = "info"; + + public constructor() {} + +} + +@ComponentV2() final struct Child extends CustomComponentV2 { + @Param() @Once() public onceParamNum: number = 0; + + @Param() @Once() @Require() public onceParamInfo!: Info; + + public build() { + Column(){ + Text(\`Child onceParamNum: \${this.onceParamNum}\`); + Text(\`Child onceParamInfo: \${this.onceParamInfo.name}\`); + Button("changeOnceParamNum").onClick(((e) => { + (this.onceParamNum++); + })); + }; + } + + public constructor() {} + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + @Local() public localNum: number = 10; + + @Local() public localInfo: Info = new Info(); + + public build() { + Column(){ + Text(\`Parent localNum: \${this.localNum}\`); + Text(\`Parent localInfo: \${this.localInfo.name}\`); + Child({ + onceParamNum: this.localNum, + onceParamInfo: this.localInfo, + }); + }; + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Child { + onceParamNum?: number; + @Param() @Once() __backing_onceParamNum?: number; + onceParamInfo?: Info; + @Param() @Once() @Require() __backing_onceParamInfo?: Info; + +} + +@ComponentV2() export interface __Options_Index { + localNum?: number; + @Local() __backing_localNum?: number; + localInfo?: Info; + @Local() __backing_localInfo?: Info; + +} +`; + +const expectedCheckedScript: string = ` +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IParamOnceDecoratedVariable as IParamOnceDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Text as Text, Button as Button } from "@ohos.arkui.component"; + +import { Param as Param, Once as Once, ObservedV2 as ObservedV2, Trace as Trace, Require as Require, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ObservedV2() class Info implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"name"}) private __backing_name: string = "info"; + + @JSONStringifyIgnore() private __meta_name: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get name(): string { + this.conditionalAddRef(this.__meta_name); + return UIUtils.makeObserved(this.__backing_name); + } + + public set name(newValue: string) { + if (((this.__backing_name) !== (newValue))) { + this.__backing_name = newValue; + this.__meta_name.fireChange(); + this.executeOnSubscribingWatches("name"); + } + } + + public constructor() {} + +} + +@ComponentV2() final struct Child extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_onceParamNum = STATE_MGMT_FACTORY.makeParamOnce(this, "onceParamNum", ((({let gensym___118919021 = initializers; + (((gensym___118919021) == (null)) ? undefined : gensym___118919021.onceParamNum)})) ?? (0))); + this.__backing_onceParamInfo = STATE_MGMT_FACTORY.makeParamOnce(this, "onceParamInfo", (initializers!.onceParamInfo as Info)); + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_onceParamNum?: IParamOnceDecoratedVariable; + + public get onceParamNum(): number { + return this.__backing_onceParamNum!.get(); + } + + public set onceParamNum(value: number) { + this.__backing_onceParamNum!.set(value); + } + + private __backing_onceParamInfo?: IParamOnceDecoratedVariable; + + public get onceParamInfo(): Info { + return this.__backing_onceParamInfo!.get(); + } + + public set onceParamInfo(value: Info) { + this.__backing_onceParamInfo!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, \`Child onceParamNum: \${this.onceParamNum}\`, undefined, undefined); + Text(undefined, \`Child onceParamInfo: \${this.onceParamInfo.name}\`, undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + (this.onceParamNum++); + })); + return; + }), "changeOnceParamNum", undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_localNum = STATE_MGMT_FACTORY.makeLocal(this, "localNum", 10); + this.__backing_localInfo = STATE_MGMT_FACTORY.makeLocal(this, "localInfo", new Info()); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_localNum?: ILocalDecoratedVariable; + + public get localNum(): number { + return this.__backing_localNum!.get(); + } + + public set localNum(value: number) { + this.__backing_localNum!.set(value); + } + + private __backing_localInfo?: ILocalDecoratedVariable; + + public get localInfo(): Info { + return this.__backing_localInfo!.get(); + } + + public set localInfo(value: Info) { + this.__backing_localInfo!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, \`Parent localNum: \${this.localNum}\`, undefined, undefined); + Text(undefined, \`Parent localInfo: \${this.localInfo.name}\`, undefined, undefined); + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + onceParamNum: this.localNum, + onceParamInfo: this.localInfo, + }, undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Child { + set onceParamNum(onceParamNum: (number | undefined)) + + get onceParamNum(): (number | undefined) + @Param() set __backing_onceParamNum(__backing_onceParamNum: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceParamNum(): (IParamOnceDecoratedVariable | undefined) + set onceParamInfo(onceParamInfo: (Info | undefined)) + + get onceParamInfo(): (Info | undefined) + @Param() @Require() set __backing_onceParamInfo(__backing_onceParamInfo: (IParamOnceDecoratedVariable | undefined)) + + @Param() @Require() get __backing_onceParamInfo(): (IParamOnceDecoratedVariable | undefined) + +} + +@ComponentV2() export interface __Options_Index { + set localNum(localNum: (number | undefined)) + + get localNum(): (number | undefined) + set __backing_localNum(__backing_localNum: (ILocalDecoratedVariable | undefined)) + + get __backing_localNum(): (ILocalDecoratedVariable | undefined) + set localInfo(localInfo: (Info | undefined)) + + get localInfo(): (Info | undefined) + set __backing_localInfo(__backing_localInfo: (ILocalDecoratedVariable | undefined)) + + get __backing_localInfo(): (ILocalDecoratedVariable | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test @Once Decorator with @Require', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/param/param-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/param/param-basic-type.test.ts new file mode 100644 index 000000000..c02112576 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/param/param-basic-type.test.ts @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/param'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'param-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @Param decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IParamDecoratedVariable as IParamDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Param as Param } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_paramVar1 = STATE_MGMT_FACTORY.makeParam(this, "paramVar1", ((({let gensym___264789668 = initializers; + (((gensym___264789668) == (null)) ? undefined : gensym___264789668.paramVar1)})) ?? ("stateVar1"))); + this.__backing_paramVar2 = STATE_MGMT_FACTORY.makeParam(this, "paramVar2", ((({let gensym___171906071 = initializers; + (((gensym___171906071) == (null)) ? undefined : gensym___171906071.paramVar2)})) ?? (50))); + this.__backing_paramVar3 = STATE_MGMT_FACTORY.makeParam(this, "paramVar3", ((({let gensym___241535547 = initializers; + (((gensym___241535547) == (null)) ? undefined : gensym___241535547.paramVar3)})) ?? (true))); + this.__backing_paramVar4 = STATE_MGMT_FACTORY.makeParam(this, "paramVar4", ((({let gensym___49490075 = initializers; + (((gensym___49490075) == (null)) ? undefined : gensym___49490075.paramVar4)})) ?? (undefined))); + this.__backing_paramVar5 = STATE_MGMT_FACTORY.makeParam(this, "paramVar5", ((({let gensym___17164613 = initializers; + (((gensym___17164613) == (null)) ? undefined : gensym___17164613.paramVar5)})) ?? (null))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void { + if (((({let gensym___99629634 = initializers; + (((gensym___99629634) == (null)) ? undefined : gensym___99629634.paramVar1)})) !== (undefined))) { + this.__backing_paramVar1!.update((initializers!.paramVar1 as string)); + } + if (((({let gensym___68516859 = initializers; + (((gensym___68516859) == (null)) ? undefined : gensym___68516859.paramVar2)})) !== (undefined))) { + this.__backing_paramVar2!.update((initializers!.paramVar2 as number)); + } + if (((({let gensym___96937083 = initializers; + (((gensym___96937083) == (null)) ? undefined : gensym___96937083.paramVar3)})) !== (undefined))) { + this.__backing_paramVar3!.update((initializers!.paramVar3 as boolean)); + } + if (((({let gensym___151087626 = initializers; + (((gensym___151087626) == (null)) ? undefined : gensym___151087626.paramVar4)})) !== (undefined))) { + this.__backing_paramVar4!.update((initializers!.paramVar4 as undefined)); + } + if (((({let gensym___127163363 = initializers; + (((gensym___127163363) == (null)) ? undefined : gensym___127163363.paramVar5)})) !== (undefined))) { + this.__backing_paramVar5!.update((initializers!.paramVar5 as null)); + } + } + + private __backing_paramVar1?: IParamDecoratedVariable; + + public get paramVar1(): string { + return this.__backing_paramVar1!.get(); + } + + private __backing_paramVar2?: IParamDecoratedVariable; + + public get paramVar2(): number { + return this.__backing_paramVar2!.get(); + } + + private __backing_paramVar3?: IParamDecoratedVariable; + + public get paramVar3(): boolean { + return this.__backing_paramVar3!.get(); + } + + private __backing_paramVar4?: IParamDecoratedVariable; + + public get paramVar4(): undefined { + return this.__backing_paramVar4!.get(); + } + + private __backing_paramVar5?: IParamDecoratedVariable; + + public get paramVar5(): null { + return this.__backing_paramVar5!.get(); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set paramVar1(paramVar1: (string | undefined)) + + get paramVar1(): (string | undefined) + set __backing_paramVar1(__backing_paramVar1: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar1(): (IParamDecoratedVariable | undefined) + set paramVar2(paramVar2: (number | undefined)) + + get paramVar2(): (number | undefined) + set __backing_paramVar2(__backing_paramVar2: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar2(): (IParamDecoratedVariable | undefined) + set paramVar3(paramVar3: (boolean | undefined)) + + get paramVar3(): (boolean | undefined) + set __backing_paramVar3(__backing_paramVar3: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar3(): (IParamDecoratedVariable | undefined) + set paramVar4(paramVar4: (undefined | undefined)) + + get paramVar4(): (undefined | undefined) + set __backing_paramVar4(__backing_paramVar4: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar4(): (IParamDecoratedVariable | undefined) + set paramVar5(paramVar5: (null | undefined)) + + get paramVar5(): (null | undefined) + set __backing_paramVar5(__backing_paramVar5: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar5(): (IParamDecoratedVariable | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test basic type @Param decorated variables transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/param/param-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/param/param-complex-type.test.ts new file mode 100644 index 000000000..3639b1253 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/param/param-complex-type.test.ts @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/param'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'param-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @Param decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IParamDecoratedVariable as IParamDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Param as Param } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class StateType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: StateType = new StateType(0, 0); + + public static readonly TYPE2: StateType = new StateType(1, 1); + + public static readonly TYPE3: StateType = new StateType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: StateType[] = [StateType.TYPE1, StateType.TYPE2, StateType.TYPE3]; + + public getName(): String { + return StateType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): StateType { + for (let i = 0;((i) < (StateType.#NamesArray.length));(++i)) { + if (((name) == (StateType.#NamesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant StateType.") + (name))); + } + + public static fromValue(value: int): StateType { + for (let i = 0;((i) < (StateType.#ValuesArray.length));(++i)) { + if (((value) == (StateType.#ValuesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum StateType with value ") + (value))); + } + + public valueOf(): int { + return StateType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return StateType.#StringValuesArray[this.#ordinal]; + } + + public static values(): StateType[] { + return StateType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: StateType): String { + return e.getName(); + } + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_paramVar1 = STATE_MGMT_FACTORY.makeParam(this, "paramVar1", ((({let gensym___264789668 = initializers; + (((gensym___264789668) == (null)) ? undefined : gensym___264789668.paramVar1)})) ?? (new Per(6)))); + this.__backing_paramVar2 = STATE_MGMT_FACTORY.makeParam>(this, "paramVar2", ((({let gensym___171906071 = initializers; + (((gensym___171906071) == (null)) ? undefined : gensym___171906071.paramVar2)})) ?? (new Array(3, 6, 8)))); + this.__backing_paramVar3 = STATE_MGMT_FACTORY.makeParam(this, "paramVar3", ((({let gensym___241535547 = initializers; + (((gensym___241535547) == (null)) ? undefined : gensym___241535547.paramVar3)})) ?? (StateType.TYPE3))); + this.__backing_paramVar4 = STATE_MGMT_FACTORY.makeParam>(this, "paramVar4", ((({let gensym___49490075 = initializers; + (((gensym___49490075) == (null)) ? undefined : gensym___49490075.paramVar4)})) ?? (new Set(new Array("aa", "bb"))))); + this.__backing_paramVar5 = STATE_MGMT_FACTORY.makeParam>(this, "paramVar5", ((({let gensym___17164613 = initializers; + (((gensym___17164613) == (null)) ? undefined : gensym___17164613.paramVar5)})) ?? ([true, false]))); + this.__backing_paramVar6 = STATE_MGMT_FACTORY.makeParam>(this, "paramVar6", ((({let gensym___84871771 = initializers; + (((gensym___84871771) == (null)) ? undefined : gensym___84871771.paramVar6)})) ?? (new Array(new Per(7), new Per(11))))); + this.__backing_paramVar7 = STATE_MGMT_FACTORY.makeParam>(this, "paramVar7", ((({let gensym___263986833 = initializers; + (((gensym___263986833) == (null)) ? undefined : gensym___263986833.paramVar7)})) ?? ([new Per(7), new Per(11)]))); + this.__backing_paramVar8 = STATE_MGMT_FACTORY.makeParam<((sr: string)=> void)>(this, "paramVar8", ((({let gensym___6968121 = initializers; + (((gensym___6968121) == (null)) ? undefined : gensym___6968121.paramVar8)})) ?? (((sr: string) => {})))); + this.__backing_paramVar9 = STATE_MGMT_FACTORY.makeParam(this, "paramVar9", ((({let gensym___63984493 = initializers; + (((gensym___63984493) == (null)) ? undefined : gensym___63984493.paramVar9)})) ?? (new Date("2025-4-23")))); + this.__backing_paramVar10 = STATE_MGMT_FACTORY.makeParam>(this, "paramVar10", ((({let gensym___260234648 = initializers; + (((gensym___260234648) == (null)) ? undefined : gensym___260234648.paramVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]])))); + this.__backing_paramVar11 = STATE_MGMT_FACTORY.makeParam<(string | number)>(this, "paramVar11", ((({let gensym___144998584 = initializers; + (((gensym___144998584) == (null)) ? undefined : gensym___144998584.paramVar11)})) ?? (0.0))); + this.__backing_paramVar12 = STATE_MGMT_FACTORY.makeParam<(Set | Per)>(this, "paramVar12", ((({let gensym___237878674 = initializers; + (((gensym___237878674) == (null)) ? undefined : gensym___237878674.paramVar12)})) ?? (new Per(6)))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void { + if (((({let gensym___99629634 = initializers; + (((gensym___99629634) == (null)) ? undefined : gensym___99629634.paramVar1)})) !== (undefined))) { + this.__backing_paramVar1!.update((initializers!.paramVar1 as Per)); + } + if (((({let gensym___68516859 = initializers; + (((gensym___68516859) == (null)) ? undefined : gensym___68516859.paramVar2)})) !== (undefined))) { + this.__backing_paramVar2!.update((initializers!.paramVar2 as Array)); + } + if (((({let gensym___96937083 = initializers; + (((gensym___96937083) == (null)) ? undefined : gensym___96937083.paramVar3)})) !== (undefined))) { + this.__backing_paramVar3!.update((initializers!.paramVar3 as StateType)); + } + if (((({let gensym___151087626 = initializers; + (((gensym___151087626) == (null)) ? undefined : gensym___151087626.paramVar4)})) !== (undefined))) { + this.__backing_paramVar4!.update((initializers!.paramVar4 as Set)); + } + if (((({let gensym___127163363 = initializers; + (((gensym___127163363) == (null)) ? undefined : gensym___127163363.paramVar5)})) !== (undefined))) { + this.__backing_paramVar5!.update((initializers!.paramVar5 as Array)); + } + if (((({let gensym___67758341 = initializers; + (((gensym___67758341) == (null)) ? undefined : gensym___67758341.paramVar6)})) !== (undefined))) { + this.__backing_paramVar6!.update((initializers!.paramVar6 as Array)); + } + if (((({let gensym___248313276 = initializers; + (((gensym___248313276) == (null)) ? undefined : gensym___248313276.paramVar7)})) !== (undefined))) { + this.__backing_paramVar7!.update((initializers!.paramVar7 as Array)); + } + if (((({let gensym___215432014 = initializers; + (((gensym___215432014) == (null)) ? undefined : gensym___215432014.paramVar8)})) !== (undefined))) { + this.__backing_paramVar8!.update((initializers!.paramVar8 as ((sr: string)=> void))); + } + if (((({let gensym___69570417 = initializers; + (((gensym___69570417) == (null)) ? undefined : gensym___69570417.paramVar9)})) !== (undefined))) { + this.__backing_paramVar9!.update((initializers!.paramVar9 as Date)); + } + if (((({let gensym___121882834 = initializers; + (((gensym___121882834) == (null)) ? undefined : gensym___121882834.paramVar10)})) !== (undefined))) { + this.__backing_paramVar10!.update((initializers!.paramVar10 as Map)); + } + if (((({let gensym___56641620 = initializers; + (((gensym___56641620) == (null)) ? undefined : gensym___56641620.paramVar11)})) !== (undefined))) { + this.__backing_paramVar11!.update((initializers!.paramVar11 as (string | number))); + } + if (((({let gensym___51325313 = initializers; + (((gensym___51325313) == (null)) ? undefined : gensym___51325313.paramVar12)})) !== (undefined))) { + this.__backing_paramVar12!.update((initializers!.paramVar12 as (Set | Per))); + } + } + + private __backing_paramVar1?: IParamDecoratedVariable; + + public get paramVar1(): Per { + return this.__backing_paramVar1!.get(); + } + + private __backing_paramVar2?: IParamDecoratedVariable>; + + public get paramVar2(): Array { + return this.__backing_paramVar2!.get(); + } + + private __backing_paramVar3?: IParamDecoratedVariable; + + public get paramVar3(): StateType { + return this.__backing_paramVar3!.get(); + } + + private __backing_paramVar4?: IParamDecoratedVariable>; + + public get paramVar4(): Set { + return this.__backing_paramVar4!.get(); + } + + private __backing_paramVar5?: IParamDecoratedVariable>; + + public get paramVar5(): Array { + return this.__backing_paramVar5!.get(); + } + + private __backing_paramVar6?: IParamDecoratedVariable>; + + public get paramVar6(): Array { + return this.__backing_paramVar6!.get(); + } + + private __backing_paramVar7?: IParamDecoratedVariable>; + + public get paramVar7(): Array { + return this.__backing_paramVar7!.get(); + } + + private __backing_paramVar8?: IParamDecoratedVariable<((sr: string)=> void)>; + + public get paramVar8(): ((sr: string)=> void) { + return this.__backing_paramVar8!.get(); + } + + private __backing_paramVar9?: IParamDecoratedVariable; + + public get paramVar9(): Date { + return this.__backing_paramVar9!.get(); + } + + private __backing_paramVar10?: IParamDecoratedVariable>; + + public get paramVar10(): Map { + return this.__backing_paramVar10!.get(); + } + + private __backing_paramVar11?: IParamDecoratedVariable<(string | number)>; + + public get paramVar11(): (string | number) { + return this.__backing_paramVar11!.get(); + } + + private __backing_paramVar12?: IParamDecoratedVariable<(Set | Per)>; + + public get paramVar12(): (Set | Per) { + return this.__backing_paramVar12!.get(); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set paramVar1(paramVar1: (Per | undefined)) + + get paramVar1(): (Per | undefined) + set __backing_paramVar1(__backing_paramVar1: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar1(): (IParamDecoratedVariable | undefined) + set paramVar2(paramVar2: (Array | undefined)) + + get paramVar2(): (Array | undefined) + set __backing_paramVar2(__backing_paramVar2: (IParamDecoratedVariable> | undefined)) + + get __backing_paramVar2(): (IParamDecoratedVariable> | undefined) + set paramVar3(paramVar3: (StateType | undefined)) + + get paramVar3(): (StateType | undefined) + set __backing_paramVar3(__backing_paramVar3: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar3(): (IParamDecoratedVariable | undefined) + set paramVar4(paramVar4: (Set | undefined)) + + get paramVar4(): (Set | undefined) + set __backing_paramVar4(__backing_paramVar4: (IParamDecoratedVariable> | undefined)) + + get __backing_paramVar4(): (IParamDecoratedVariable> | undefined) + set paramVar5(paramVar5: (Array | undefined)) + + get paramVar5(): (Array | undefined) + set __backing_paramVar5(__backing_paramVar5: (IParamDecoratedVariable> | undefined)) + + get __backing_paramVar5(): (IParamDecoratedVariable> | undefined) + set paramVar6(paramVar6: (Array | undefined)) + + get paramVar6(): (Array | undefined) + set __backing_paramVar6(__backing_paramVar6: (IParamDecoratedVariable> | undefined)) + + get __backing_paramVar6(): (IParamDecoratedVariable> | undefined) + set paramVar7(paramVar7: (Array | undefined)) + + get paramVar7(): (Array | undefined) + set __backing_paramVar7(__backing_paramVar7: (IParamDecoratedVariable> | undefined)) + + get __backing_paramVar7(): (IParamDecoratedVariable> | undefined) + set paramVar8(paramVar8: (((sr: string)=> void) | undefined)) + + get paramVar8(): (((sr: string)=> void) | undefined) + set __backing_paramVar8(__backing_paramVar8: (IParamDecoratedVariable<((sr: string)=> void)> | undefined)) + + get __backing_paramVar8(): (IParamDecoratedVariable<((sr: string)=> void)> | undefined) + set paramVar9(paramVar9: (Date | undefined)) + + get paramVar9(): (Date | undefined) + set __backing_paramVar9(__backing_paramVar9: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar9(): (IParamDecoratedVariable | undefined) + set paramVar10(paramVar10: (Map | undefined)) + + get paramVar10(): (Map | undefined) + set __backing_paramVar10(__backing_paramVar10: (IParamDecoratedVariable> | undefined)) + + get __backing_paramVar10(): (IParamDecoratedVariable> | undefined) + set paramVar11(paramVar11: ((string | number) | undefined)) + + get paramVar11(): ((string | number) | undefined) + set __backing_paramVar11(__backing_paramVar11: (IParamDecoratedVariable<(string | number)> | undefined)) + + get __backing_paramVar11(): (IParamDecoratedVariable<(string | number)> | undefined) + set paramVar12(paramVar12: ((Set | Per) | undefined)) + + get paramVar12(): ((Set | Per) | undefined) + set __backing_paramVar12(__backing_paramVar12: (IParamDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_paramVar12(): (IParamDecoratedVariable<(Set | Per)> | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test complex type @Param decorated variables transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/param/param-with-require.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/param/param-with-require.test.ts new file mode 100644 index 000000000..3ea06e80e --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/param/param-with-require.test.ts @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/param'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'param-with-require.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @Param Decorator with @Require', buildConfig); + +const expectedParsedScript: string = ` +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, ForEach as ForEach, Button as Button, Text as Text } from "@ohos.arkui.component"; + +import { Param as Param, Require as Require, Local as Local } from "@ohos.arkui.stateManagement"; + +class Region { + public x: number; + + public y: number; + + public constructor(x: number, y: number) { + this.x = x; + this.y = y; + } + +} + +class Info { + public name: string; + + public age: number; + + public region: Region; + + public constructor(name: string, age: number, x: number, y: number) { + this.name = name; + this.age = age; + this.region = new Region(x, y); + } + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + @Local() public infoList: Info[] = [new Info("Alice", 8, 0, 0), new Info("Barry", 10, 1, 20), new Info("Cindy", 18, 24, 40)]; + + public build() { + Column(){ + ForEach(this.infoList, ((info: Info) => { + MiddleComponent({ + info: info, + }); + })); + Button("change").onClick(((e) => { + this.infoList[0] = new Info("Atom", 40, 27, 90); + this.infoList[1].name = "Bob"; + this.infoList[2].region = new Region(7, 9); + })); + }; + } + + public constructor() {} + +} + +@ComponentV2() final struct MiddleComponent extends CustomComponentV2 { + @Require() @Param() public info!: Info; + + public build() { + Column(){ + Text(\`name: \${this.info.name}\`); + Text(\`age: \${this.info.age}\`); + SubComponent({ + region: this.info.region, + }); + }; + } + + public constructor() {} + +} + +@ComponentV2() final struct SubComponent extends CustomComponentV2 { + @Require() @Param() public region!: Region; + + public build() { + Column(){ + Text(\`region: \${this.region.x}-\${this.region.y}\`); + }; + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + infoList?: Info[]; + @Local() __backing_infoList?: Info[]; + +} + +@ComponentV2() export interface __Options_MiddleComponent { + info?: Info; + @Require() @Param() __backing_info?: Info; + +} + +@ComponentV2() export interface __Options_SubComponent { + region?: Region; + @Require() @Param() __backing_region?: Region; + +} +`; + +const expectedCheckedScript: string = ` +import { IParamDecoratedVariable as IParamDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, ForEach as ForEach, Button as Button, Text as Text } from "@ohos.arkui.component"; + +import { Param as Param, Require as Require, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class Region { + public x: number; + + public y: number; + + public constructor(x: number, y: number) { + this.x = x; + this.y = y; + } + +} + +class Info { + public name: string; + + public age: number; + + public region: Region; + + public constructor(name: string, age: number, x: number, y: number) { + this.name = name; + this.age = age; + this.region = new Region(x, y); + } + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_infoList = STATE_MGMT_FACTORY.makeLocal>(this, "infoList", [new Info("Alice", 8, 0, 0), new Info("Barry", 10, 1, 20), new Info("Cindy", 18, 24, 40)]); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_infoList?: ILocalDecoratedVariable>; + + public get infoList(): Array { + return this.__backing_infoList!.get(); + } + + public set infoList(value: Array) { + this.__backing_infoList!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ForEach(((): Array => { + return this.infoList; + }), ((info: Info) => { + MiddleComponent._instantiateImpl(undefined, (() => { + return new MiddleComponent(); + }), { + info: info, + }, undefined, undefined); + })); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.infoList[0] = new Info("Atom", 40, 27, 90); + this.infoList[1].name = "Bob"; + this.infoList[2].region = new Region(7, 9); + })); + return; + }), "change", undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() final struct MiddleComponent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_MiddleComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_info = STATE_MGMT_FACTORY.makeParam(this, "info", (initializers!.info as Info)); + } + + public __updateStruct(initializers: (__Options_MiddleComponent | undefined)): void { + if (((({let gensym___152615498 = initializers; + (((gensym___152615498) == (null)) ? undefined : gensym___152615498.info)})) !== (undefined))) { + this.__backing_info!.update((initializers!.info as Info)); + } + } + + private __backing_info?: IParamDecoratedVariable; + + public get info(): Info { + return this.__backing_info!.get(); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, \`name: \${this.info.name}\`, undefined, undefined); + Text(undefined, \`age: \${this.info.age}\`, undefined, undefined); + SubComponent._instantiateImpl(undefined, (() => { + return new SubComponent(); + }), { + region: this.info.region, + }, undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() final struct SubComponent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_SubComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_region = STATE_MGMT_FACTORY.makeParam(this, "region", (initializers!.region as Region)); + } + + public __updateStruct(initializers: (__Options_SubComponent | undefined)): void { + if (((({let gensym___240509478 = initializers; + (((gensym___240509478) == (null)) ? undefined : gensym___240509478.region)})) !== (undefined))) { + this.__backing_region!.update((initializers!.region as Region)); + } + } + + private __backing_region?: IParamDecoratedVariable; + + public get region(): Region { + return this.__backing_region!.get(); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, \`region: \${this.region.x}-\${this.region.y}\`, undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set infoList(infoList: (Array | undefined)) + + get infoList(): (Array | undefined) + set __backing_infoList(__backing_infoList: (ILocalDecoratedVariable> | undefined)) + + get __backing_infoList(): (ILocalDecoratedVariable> | undefined) + +} + +@ComponentV2() export interface __Options_MiddleComponent { + set info(info: (Info | undefined)) + + get info(): (Info | undefined) + @Require() set __backing_info(__backing_info: (IParamDecoratedVariable | undefined)) + + @Require() get __backing_info(): (IParamDecoratedVariable | undefined) + +} + +@ComponentV2() export interface __Options_SubComponent { + set region(region: (Region | undefined)) + + get region(): (Region | undefined) + @Require() set __backing_region(__backing_region: (IParamDecoratedVariable | undefined)) + + @Require() get __backing_region(): (IParamDecoratedVariable | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test @Param Decorator with @Require', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-basic-type.test.ts new file mode 100644 index 000000000..e3b5e8f98 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-basic-type.test.ts @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/prop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'prop-ref-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @PropRef decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'prop-ref-basic-type', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropRefDecoratedVariable as IPropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { PropRef as PropRef } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@Component() final struct PropParent extends CustomComponent { + public __initializeStruct(initializers: (__Options_PropParent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_propVar1 = STATE_MGMT_FACTORY.makePropRef(this, "propVar1", ((({let gensym___95172135 = initializers; + (((gensym___95172135) == (null)) ? undefined : gensym___95172135.propVar1)})) ?? ("propVar1"))); + this.__backing_propVar2 = STATE_MGMT_FACTORY.makePropRef(this, "propVar2", ((({let gensym___222490386 = initializers; + (((gensym___222490386) == (null)) ? undefined : gensym___222490386.propVar2)})) ?? (50))); + this.__backing_propVar3 = STATE_MGMT_FACTORY.makePropRef(this, "propVar3", ((({let gensym___201781257 = initializers; + (((gensym___201781257) == (null)) ? undefined : gensym___201781257.propVar3)})) ?? (true))); + this.__backing_propVar4 = STATE_MGMT_FACTORY.makePropRef(this, "propVar4", ((({let gensym___22028950 = initializers; + (((gensym___22028950) == (null)) ? undefined : gensym___22028950.propVar4)})) ?? (undefined))); + this.__backing_propVar5 = STATE_MGMT_FACTORY.makePropRef(this, "propVar5", ((({let gensym___54872258 = initializers; + (((gensym___54872258) == (null)) ? undefined : gensym___54872258.propVar5)})) ?? (null))); + } + + public __updateStruct(initializers: (__Options_PropParent | undefined)): void { + if (((({let gensym___67969738 = initializers; + (((gensym___67969738) == (null)) ? undefined : gensym___67969738.propVar1)})) !== (undefined))) { + this.__backing_propVar1!.update((initializers!.propVar1 as string)); + } + if (((({let gensym___52350476 = initializers; + (((gensym___52350476) == (null)) ? undefined : gensym___52350476.propVar2)})) !== (undefined))) { + this.__backing_propVar2!.update((initializers!.propVar2 as number)); + } + if (((({let gensym___103864283 = initializers; + (((gensym___103864283) == (null)) ? undefined : gensym___103864283.propVar3)})) !== (undefined))) { + this.__backing_propVar3!.update((initializers!.propVar3 as boolean)); + } + if (((({let gensym___175155715 = initializers; + (((gensym___175155715) == (null)) ? undefined : gensym___175155715.propVar4)})) !== (undefined))) { + this.__backing_propVar4!.update((initializers!.propVar4 as undefined)); + } + if (((({let gensym___134530703 = initializers; + (((gensym___134530703) == (null)) ? undefined : gensym___134530703.propVar5)})) !== (undefined))) { + this.__backing_propVar5!.update((initializers!.propVar5 as null)); + } + } + + private __backing_propVar1?: IPropRefDecoratedVariable; + + public get propVar1(): string { + return this.__backing_propVar1!.get(); + } + + public set propVar1(value: string) { + this.__backing_propVar1!.set(value); + } + + private __backing_propVar2?: IPropRefDecoratedVariable; + + public get propVar2(): number { + return this.__backing_propVar2!.get(); + } + + public set propVar2(value: number) { + this.__backing_propVar2!.set(value); + } + + private __backing_propVar3?: IPropRefDecoratedVariable; + + public get propVar3(): boolean { + return this.__backing_propVar3!.get(); + } + + public set propVar3(value: boolean) { + this.__backing_propVar3!.set(value); + } + + private __backing_propVar4?: IPropRefDecoratedVariable; + + public get propVar4(): undefined { + return this.__backing_propVar4!.get(); + } + + public set propVar4(value: undefined) { + this.__backing_propVar4!.set(value); + } + + private __backing_propVar5?: IPropRefDecoratedVariable; + + public get propVar5(): null { + return this.__backing_propVar5!.get(); + } + + public set propVar5(value: null) { + this.__backing_propVar5!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_PropParent { + set propVar1(propVar1: (string | undefined)) + + get propVar1(): (string | undefined) + set __backing_propVar1(__backing_propVar1: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar1(): (IPropRefDecoratedVariable | undefined) + set propVar2(propVar2: (number | undefined)) + + get propVar2(): (number | undefined) + set __backing_propVar2(__backing_propVar2: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar2(): (IPropRefDecoratedVariable | undefined) + set propVar3(propVar3: (boolean | undefined)) + + get propVar3(): (boolean | undefined) + set __backing_propVar3(__backing_propVar3: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar3(): (IPropRefDecoratedVariable | undefined) + set propVar4(propVar4: (undefined | undefined)) + + get propVar4(): (undefined | undefined) + set __backing_propVar4(__backing_propVar4: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar4(): (IPropRefDecoratedVariable | undefined) + set propVar5(propVar5: (null | undefined)) + + get propVar5(): (null | undefined) + set __backing_propVar5(__backing_propVar5: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar5(): (IPropRefDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic type @PropRef decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-complex-type.test.ts new file mode 100644 index 000000000..8dbc549f5 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-complex-type.test.ts @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/prop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'prop-ref-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @PropRef decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsed-trans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropRefDecoratedVariable as IPropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { PropRef as PropRef } from "@ohos.arkui.stateManagement"; + +function main() {} + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class PropType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: PropType = new PropType(0, 0); + + public static readonly TYPE2: PropType = new PropType(1, 1); + + public static readonly TYPE3: PropType = new PropType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: PropType[] = [PropType.TYPE1, PropType.TYPE2, PropType.TYPE3]; + + public getName(): String { + return PropType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): PropType { + for (let i = 0;((i) < (PropType.#NamesArray.length));(++i)) { + if (((name) == (PropType.#NamesArray[i]))) { + return PropType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant PropType.") + (name))); + } + + public static fromValue(value: int): PropType { + for (let i = 0;((i) < (PropType.#ValuesArray.length));(++i)) { + if (((value) == (PropType.#ValuesArray[i]))) { + return PropType.#ItemsArray[i]; + } + } + throw new Error((("No enum PropType with value ") + (value))); + } + + public valueOf(): int { + return PropType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return PropType.#StringValuesArray[this.#ordinal]; + } + + public static values(): PropType[] { + return PropType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: PropType): String { + return e.getName(); + } + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_propVar1 = STATE_MGMT_FACTORY.makePropRef(this, "propVar1", ((({let gensym___95172135 = initializers; + (((gensym___95172135) == (null)) ? undefined : gensym___95172135.propVar1)})) ?? (new Per(6)))); + this.__backing_propVar2 = STATE_MGMT_FACTORY.makePropRef>(this, "propVar2", ((({let gensym___222490386 = initializers; + (((gensym___222490386) == (null)) ? undefined : gensym___222490386.propVar2)})) ?? (new Array(3, 6, 8)))); + this.__backing_propVar3 = STATE_MGMT_FACTORY.makePropRef(this, "propVar3", ((({let gensym___201781257 = initializers; + (((gensym___201781257) == (null)) ? undefined : gensym___201781257.propVar3)})) ?? (PropType.TYPE3))); + this.__backing_propVar4 = STATE_MGMT_FACTORY.makePropRef>(this, "propVar4", ((({let gensym___22028950 = initializers; + (((gensym___22028950) == (null)) ? undefined : gensym___22028950.propVar4)})) ?? (new Set(new Array("aa", "bb"))))); + this.__backing_propVar5 = STATE_MGMT_FACTORY.makePropRef>(this, "propVar5", ((({let gensym___54872258 = initializers; + (((gensym___54872258) == (null)) ? undefined : gensym___54872258.propVar5)})) ?? ([true, false]))); + this.__backing_propVar6 = STATE_MGMT_FACTORY.makePropRef>(this, "propVar6", ((({let gensym___128760941 = initializers; + (((gensym___128760941) == (null)) ? undefined : gensym___128760941.propVar6)})) ?? (new Array(new Per(7), new Per(11))))); + this.__backing_propVar7 = STATE_MGMT_FACTORY.makePropRef>(this, "propVar7", ((({let gensym___30534085 = initializers; + (((gensym___30534085) == (null)) ? undefined : gensym___30534085.propVar7)})) ?? ([new Per(7), new Per(11)]))); + this.__backing_propVar8 = STATE_MGMT_FACTORY.makePropRef<((sr: string)=> void)>(this, "propVar8", ((({let gensym___12471776 = initializers; + (((gensym___12471776) == (null)) ? undefined : gensym___12471776.propVar8)})) ?? (((sr: string) => {})))); + this.__backing_propVar9 = STATE_MGMT_FACTORY.makePropRef(this, "propVar9", ((({let gensym___123472108 = initializers; + (((gensym___123472108) == (null)) ? undefined : gensym___123472108.propVar9)})) ?? (new Date("2025-4-23")))); + this.__backing_propVar10 = STATE_MGMT_FACTORY.makePropRef>(this, "propVar10", ((({let gensym___147847012 = initializers; + (((gensym___147847012) == (null)) ? undefined : gensym___147847012.propVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]])))); + this.__backing_propVar11 = STATE_MGMT_FACTORY.makePropRef<(string | number)>(this, "propVar11", ((({let gensym___117026760 = initializers; + (((gensym___117026760) == (null)) ? undefined : gensym___117026760.propVar11)})) ?? (0.0))); + this.__backing_propVar12 = STATE_MGMT_FACTORY.makePropRef<(Set | Per)>(this, "propVar12", ((({let gensym___220245132 = initializers; + (((gensym___220245132) == (null)) ? undefined : gensym___220245132.propVar12)})) ?? (new Per(6)))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void { + if (((({let gensym___67969738 = initializers; + (((gensym___67969738) == (null)) ? undefined : gensym___67969738.propVar1)})) !== (undefined))) { + this.__backing_propVar1!.update((initializers!.propVar1 as Per)); + } + if (((({let gensym___52350476 = initializers; + (((gensym___52350476) == (null)) ? undefined : gensym___52350476.propVar2)})) !== (undefined))) { + this.__backing_propVar2!.update((initializers!.propVar2 as Array)); + } + if (((({let gensym___103864283 = initializers; + (((gensym___103864283) == (null)) ? undefined : gensym___103864283.propVar3)})) !== (undefined))) { + this.__backing_propVar3!.update((initializers!.propVar3 as PropType)); + } + if (((({let gensym___175155715 = initializers; + (((gensym___175155715) == (null)) ? undefined : gensym___175155715.propVar4)})) !== (undefined))) { + this.__backing_propVar4!.update((initializers!.propVar4 as Set)); + } + if (((({let gensym___134530703 = initializers; + (((gensym___134530703) == (null)) ? undefined : gensym___134530703.propVar5)})) !== (undefined))) { + this.__backing_propVar5!.update((initializers!.propVar5 as Array)); + } + if (((({let gensym___211600890 = initializers; + (((gensym___211600890) == (null)) ? undefined : gensym___211600890.propVar6)})) !== (undefined))) { + this.__backing_propVar6!.update((initializers!.propVar6 as Array)); + } + if (((({let gensym___124229427 = initializers; + (((gensym___124229427) == (null)) ? undefined : gensym___124229427.propVar7)})) !== (undefined))) { + this.__backing_propVar7!.update((initializers!.propVar7 as Array)); + } + if (((({let gensym___248056380 = initializers; + (((gensym___248056380) == (null)) ? undefined : gensym___248056380.propVar8)})) !== (undefined))) { + this.__backing_propVar8!.update((initializers!.propVar8 as ((sr: string)=> void))); + } + if (((({let gensym___55399278 = initializers; + (((gensym___55399278) == (null)) ? undefined : gensym___55399278.propVar9)})) !== (undefined))) { + this.__backing_propVar9!.update((initializers!.propVar9 as Date)); + } + if (((({let gensym___125042885 = initializers; + (((gensym___125042885) == (null)) ? undefined : gensym___125042885.propVar10)})) !== (undefined))) { + this.__backing_propVar10!.update((initializers!.propVar10 as Map)); + } + if (((({let gensym___2015283 = initializers; + (((gensym___2015283) == (null)) ? undefined : gensym___2015283.propVar11)})) !== (undefined))) { + this.__backing_propVar11!.update((initializers!.propVar11 as (string | number))); + } + if (((({let gensym___39009414 = initializers; + (((gensym___39009414) == (null)) ? undefined : gensym___39009414.propVar12)})) !== (undefined))) { + this.__backing_propVar12!.update((initializers!.propVar12 as (Set | Per))); + } + } + + private __backing_propVar1?: IPropRefDecoratedVariable; + + public get propVar1(): Per { + return this.__backing_propVar1!.get(); + } + + public set propVar1(value: Per) { + this.__backing_propVar1!.set(value); + } + + private __backing_propVar2?: IPropRefDecoratedVariable>; + + public get propVar2(): Array { + return this.__backing_propVar2!.get(); + } + + public set propVar2(value: Array) { + this.__backing_propVar2!.set(value); + } + + private __backing_propVar3?: IPropRefDecoratedVariable; + + public get propVar3(): PropType { + return this.__backing_propVar3!.get(); + } + + public set propVar3(value: PropType) { + this.__backing_propVar3!.set(value); + } + + private __backing_propVar4?: IPropRefDecoratedVariable>; + + public get propVar4(): Set { + return this.__backing_propVar4!.get(); + } + + public set propVar4(value: Set) { + this.__backing_propVar4!.set(value); + } + + private __backing_propVar5?: IPropRefDecoratedVariable>; + + public get propVar5(): Array { + return this.__backing_propVar5!.get(); + } + + public set propVar5(value: Array) { + this.__backing_propVar5!.set(value); + } + + private __backing_propVar6?: IPropRefDecoratedVariable>; + + public get propVar6(): Array { + return this.__backing_propVar6!.get(); + } + + public set propVar6(value: Array) { + this.__backing_propVar6!.set(value); + } + + private __backing_propVar7?: IPropRefDecoratedVariable>; + + public get propVar7(): Array { + return this.__backing_propVar7!.get(); + } + + public set propVar7(value: Array) { + this.__backing_propVar7!.set(value); + } + + private __backing_propVar8?: IPropRefDecoratedVariable<((sr: string)=> void)>; + + public get propVar8(): ((sr: string)=> void) { + return this.__backing_propVar8!.get(); + } + + public set propVar8(value: ((sr: string)=> void)) { + this.__backing_propVar8!.set(value); + } + + private __backing_propVar9?: IPropRefDecoratedVariable; + + public get propVar9(): Date { + return this.__backing_propVar9!.get(); + } + + public set propVar9(value: Date) { + this.__backing_propVar9!.set(value); + } + + private __backing_propVar10?: IPropRefDecoratedVariable>; + + public get propVar10(): Map { + return this.__backing_propVar10!.get(); + } + + public set propVar10(value: Map) { + this.__backing_propVar10!.set(value); + } + + private __backing_propVar11?: IPropRefDecoratedVariable<(string | number)>; + + public get propVar11(): (string | number) { + return this.__backing_propVar11!.get(); + } + + public set propVar11(value: (string | number)) { + this.__backing_propVar11!.set(value); + } + + private __backing_propVar12?: IPropRefDecoratedVariable<(Set | Per)>; + + public get propVar12(): (Set | Per) { + return this.__backing_propVar12!.get(); + } + + public set propVar12(value: (Set | Per)) { + this.__backing_propVar12!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_Parent { + set propVar1(propVar1: (Per | undefined)) + + get propVar1(): (Per | undefined) + set __backing_propVar1(__backing_propVar1: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar1(): (IPropRefDecoratedVariable | undefined) + set propVar2(propVar2: (Array | undefined)) + + get propVar2(): (Array | undefined) + set __backing_propVar2(__backing_propVar2: (IPropRefDecoratedVariable> | undefined)) + + get __backing_propVar2(): (IPropRefDecoratedVariable> | undefined) + set propVar3(propVar3: (PropType | undefined)) + + get propVar3(): (PropType | undefined) + set __backing_propVar3(__backing_propVar3: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar3(): (IPropRefDecoratedVariable | undefined) + set propVar4(propVar4: (Set | undefined)) + + get propVar4(): (Set | undefined) + set __backing_propVar4(__backing_propVar4: (IPropRefDecoratedVariable> | undefined)) + + get __backing_propVar4(): (IPropRefDecoratedVariable> | undefined) + set propVar5(propVar5: (Array | undefined)) + + get propVar5(): (Array | undefined) + set __backing_propVar5(__backing_propVar5: (IPropRefDecoratedVariable> | undefined)) + + get __backing_propVar5(): (IPropRefDecoratedVariable> | undefined) + set propVar6(propVar6: (Array | undefined)) + + get propVar6(): (Array | undefined) + set __backing_propVar6(__backing_propVar6: (IPropRefDecoratedVariable> | undefined)) + + get __backing_propVar6(): (IPropRefDecoratedVariable> | undefined) + set propVar7(propVar7: (Array | undefined)) + + get propVar7(): (Array | undefined) + set __backing_propVar7(__backing_propVar7: (IPropRefDecoratedVariable> | undefined)) + + get __backing_propVar7(): (IPropRefDecoratedVariable> | undefined) + set propVar8(propVar8: (((sr: string)=> void) | undefined)) + + get propVar8(): (((sr: string)=> void) | undefined) + set __backing_propVar8(__backing_propVar8: (IPropRefDecoratedVariable<((sr: string)=> void)> | undefined)) + + get __backing_propVar8(): (IPropRefDecoratedVariable<((sr: string)=> void)> | undefined) + set propVar9(propVar9: (Date | undefined)) + + get propVar9(): (Date | undefined) + set __backing_propVar9(__backing_propVar9: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar9(): (IPropRefDecoratedVariable | undefined) + set propVar10(propVar10: (Map | undefined)) + + get propVar10(): (Map | undefined) + set __backing_propVar10(__backing_propVar10: (IPropRefDecoratedVariable> | undefined)) + + get __backing_propVar10(): (IPropRefDecoratedVariable> | undefined) + set propVar11(propVar11: ((string | number) | undefined)) + + get propVar11(): ((string | number) | undefined) + set __backing_propVar11(__backing_propVar11: (IPropRefDecoratedVariable<(string | number)> | undefined)) + + get __backing_propVar11(): (IPropRefDecoratedVariable<(string | number)> | undefined) + set propVar12(propVar12: ((Set | Per) | undefined)) + + get propVar12(): ((Set | Per) | undefined) + set __backing_propVar12(__backing_propVar12: (IPropRefDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_propVar12(): (IPropRefDecoratedVariable<(Set | Per)> | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test complex type @PropRef decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-without-initialization.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-without-initialization.test.ts new file mode 100644 index 000000000..e176c96e9 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-without-initialization.test.ts @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/prop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'prop-ref-without-initialization.ets'), +]; + +const pluginTester = new PluginTester('test @PropRef decorated variables transformation without initialization', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsed-trans', + parsed: uiTransform().parsed +}; + +const expectedParsedScript: string = ` +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { PropRef as PropRef } from "@ohos.arkui.stateManagement"; + +@Component() final struct PropParent extends CustomComponent { + @PropRef() public propVar1!: string; + + @PropRef() public propVar2?: (number | undefined); + + @PropRef() public propVar3!: boolean; + + @PropRef() public propVar4?: undefined; + + @PropRef() public propVar5?: null; + + @PropRef() public propVar6?: (Array | null); + + @PropRef() public propVar7?: (Map | undefined); + + public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_PropParent { + propVar1?: string; + @PropRef() __backing_propVar1?: string; + propVar2?: (number | undefined); + @PropRef() __backing_propVar2?: (number | undefined); + propVar3?: boolean; + @PropRef() __backing_propVar3?: boolean; + propVar4?: undefined; + @PropRef() __backing_propVar4?: undefined; + propVar5?: null; + @PropRef() __backing_propVar5?: null; + propVar6?: (Array | null); + @PropRef() __backing_propVar6?: (Array | null); + propVar7?: (Map | undefined); + @PropRef() __backing_propVar7?: (Map | undefined); + +} +`; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropRefDecoratedVariable as IPropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { PropRef as PropRef } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Component() final struct PropParent extends CustomComponent { + public __initializeStruct(initializers: (__Options_PropParent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_propVar1 = STATE_MGMT_FACTORY.makePropRef(this, "propVar1", (initializers!.propVar1 as string)); + this.__backing_propVar2 = STATE_MGMT_FACTORY.makePropRef<(number | undefined)>(this, "propVar2", (initializers!.propVar2 as (number | undefined))); + this.__backing_propVar3 = STATE_MGMT_FACTORY.makePropRef(this, "propVar3", (initializers!.propVar3 as boolean)); + this.__backing_propVar4 = STATE_MGMT_FACTORY.makePropRef(this, "propVar4", (initializers!.propVar4 as undefined)); + this.__backing_propVar5 = STATE_MGMT_FACTORY.makePropRef(this, "propVar5", (initializers!.propVar5 as null)); + this.__backing_propVar6 = STATE_MGMT_FACTORY.makePropRef<(Array | null)>(this, "propVar6", (initializers!.propVar6 as (Array | null))); + this.__backing_propVar7 = STATE_MGMT_FACTORY.makePropRef<(Map | undefined)>(this, "propVar7", (initializers!.propVar7 as (Map | undefined))); + } + + public __updateStruct(initializers: (__Options_PropParent | undefined)): void { + if (((({let gensym___67969738 = initializers; + (((gensym___67969738) == (null)) ? undefined : gensym___67969738.propVar1)})) !== (undefined))) { + this.__backing_propVar1!.update((initializers!.propVar1 as string)); + } + if (((({let gensym___52350476 = initializers; + (((gensym___52350476) == (null)) ? undefined : gensym___52350476.propVar2)})) !== (undefined))) { + this.__backing_propVar2!.update((initializers!.propVar2 as (number | undefined))); + } + if (((({let gensym___103864283 = initializers; + (((gensym___103864283) == (null)) ? undefined : gensym___103864283.propVar3)})) !== (undefined))) { + this.__backing_propVar3!.update((initializers!.propVar3 as boolean)); + } + if (((({let gensym___175155715 = initializers; + (((gensym___175155715) == (null)) ? undefined : gensym___175155715.propVar4)})) !== (undefined))) { + this.__backing_propVar4!.update((initializers!.propVar4 as undefined)); + } + if (((({let gensym___134530703 = initializers; + (((gensym___134530703) == (null)) ? undefined : gensym___134530703.propVar5)})) !== (undefined))) { + this.__backing_propVar5!.update((initializers!.propVar5 as null)); + } + if (((({let gensym___211600890 = initializers; + (((gensym___211600890) == (null)) ? undefined : gensym___211600890.propVar6)})) !== (undefined))) { + this.__backing_propVar6!.update((initializers!.propVar6 as (Array | null))); + } + if (((({let gensym___124229427 = initializers; + (((gensym___124229427) == (null)) ? undefined : gensym___124229427.propVar7)})) !== (undefined))) { + this.__backing_propVar7!.update((initializers!.propVar7 as (Map | undefined))); + } + } + + private __backing_propVar1?: IPropRefDecoratedVariable; + + public get propVar1(): string { + return this.__backing_propVar1!.get(); + } + + public set propVar1(value: string) { + this.__backing_propVar1!.set(value); + } + + private __backing_propVar2?: IPropRefDecoratedVariable<(number | undefined)>; + + public get propVar2(): (number | undefined) { + return this.__backing_propVar2!.get(); + } + + public set propVar2(value: (number | undefined)) { + this.__backing_propVar2!.set(value); + } + + private __backing_propVar3?: IPropRefDecoratedVariable; + + public get propVar3(): boolean { + return this.__backing_propVar3!.get(); + } + + public set propVar3(value: boolean) { + this.__backing_propVar3!.set(value); + } + + private __backing_propVar4?: IPropRefDecoratedVariable; + + public get propVar4(): undefined { + return this.__backing_propVar4!.get(); + } + + public set propVar4(value: undefined) { + this.__backing_propVar4!.set(value); + } + + private __backing_propVar5?: IPropRefDecoratedVariable; + + public get propVar5(): null { + return this.__backing_propVar5!.get(); + } + + public set propVar5(value: null) { + this.__backing_propVar5!.set(value); + } + + private __backing_propVar6?: IPropRefDecoratedVariable<(Array | null)>; + + public get propVar6(): (Array | null) { + return this.__backing_propVar6!.get(); + } + + public set propVar6(value: (Array | null)) { + this.__backing_propVar6!.set(value); + } + + private __backing_propVar7?: IPropRefDecoratedVariable<(Map | undefined)>; + + public get propVar7(): (Map | undefined) { + return this.__backing_propVar7!.get(); + } + + public set propVar7(value: (Map | undefined)) { + this.__backing_propVar7!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_PropParent { + set propVar1(propVar1: (string | undefined)) + + get propVar1(): (string | undefined) + set __backing_propVar1(__backing_propVar1: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar1(): (IPropRefDecoratedVariable | undefined) + set propVar2(propVar2: ((number | undefined) | undefined)) + + get propVar2(): ((number | undefined) | undefined) + set __backing_propVar2(__backing_propVar2: (IPropRefDecoratedVariable<(number | undefined)> | undefined)) + + get __backing_propVar2(): (IPropRefDecoratedVariable<(number | undefined)> | undefined) + set propVar3(propVar3: (boolean | undefined)) + + get propVar3(): (boolean | undefined) + set __backing_propVar3(__backing_propVar3: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar3(): (IPropRefDecoratedVariable | undefined) + set propVar4(propVar4: (undefined | undefined)) + + get propVar4(): (undefined | undefined) + set __backing_propVar4(__backing_propVar4: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar4(): (IPropRefDecoratedVariable | undefined) + set propVar5(propVar5: (null | undefined)) + + get propVar5(): (null | undefined) + set __backing_propVar5(__backing_propVar5: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar5(): (IPropRefDecoratedVariable | undefined) + set propVar6(propVar6: ((Array | null) | undefined)) + + get propVar6(): ((Array | null) | undefined) + set __backing_propVar6(__backing_propVar6: (IPropRefDecoratedVariable<(Array | null)> | undefined)) + + get __backing_propVar6(): (IPropRefDecoratedVariable<(Array | null)> | undefined) + set propVar7(propVar7: ((Map | undefined) | undefined)) + + get propVar7(): ((Map | undefined) | undefined) + set __backing_propVar7(__backing_propVar7: (IPropRefDecoratedVariable<(Map | undefined)> | undefined)) + + get __backing_propVar7(): (IPropRefDecoratedVariable<(Map | undefined)> | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test @PropRef decorated variables transformation without initialization', + [parsedTransform, structNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:struct-no-recheck': [testheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/state-to-propref.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/state-to-propref.test.ts new file mode 100644 index 000000000..93799cce2 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/state-to-propref.test.ts @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/prop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'state-to-prop-ref.ets'), +]; + +const pluginTester = new PluginTester('test @PropRef decorated variables passing', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropRefDecoratedVariable as IPropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { ConditionScope as ConditionScope } from "arkui.component.builder"; + +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text, Button as Button, Column as Column, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { PropRef as PropRef, State as State } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Component() final struct CountDownComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_CountDownComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_count = STATE_MGMT_FACTORY.makePropRef(this, "count", ((({let gensym___58710805 = initializers; + (((gensym___58710805) == (null)) ? undefined : gensym___58710805.count)})) ?? (0))); + this.__backing_costOfOneAttempt = ((({let gensym___88948111 = initializers; + (((gensym___88948111) == (null)) ? undefined : gensym___88948111.costOfOneAttempt)})) ?? (1)); + } + + public __updateStruct(initializers: (__Options_CountDownComponent | undefined)): void { + if (((({let gensym___188547633 = initializers; + (((gensym___188547633) == (null)) ? undefined : gensym___188547633.count)})) !== (undefined))) { + this.__backing_count!.update((initializers!.count as number)); + } + } + + private __backing_count?: IPropRefDecoratedVariable; + + public get count(): number { + return this.__backing_count!.get(); + } + + public set count(value: number) { + this.__backing_count!.set(value); + } + + private __backing_costOfOneAttempt?: number; + + public get costOfOneAttempt(): number { + return (this.__backing_costOfOneAttempt as number); + } + + public set costOfOneAttempt(value: number) { + this.__backing_costOfOneAttempt = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + if (((this.count) > (0))) { + ConditionBranch(@memo() (() => { + Text(undefined, (((("You have") + (this.count))) + ("Nuggets left")), undefined, undefined); + })); + } else { + ConditionBranch(@memo() (() => { + Text(undefined, "Game over!", undefined, undefined); + })); + } + })); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.count -= this.costOfOneAttempt; + })); + return; + }), "Try again", undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() final struct ParentComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_ParentComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_countDownStartValue = STATE_MGMT_FACTORY.makeState(this, "countDownStartValue", ((({let gensym___249912438 = initializers; + (((gensym___249912438) == (null)) ? undefined : gensym___249912438.countDownStartValue)})) ?? (10))); + } + + public __updateStruct(initializers: (__Options_ParentComponent | undefined)): void {} + + private __backing_countDownStartValue?: IStateDecoratedVariable; + + public get countDownStartValue(): number { + return this.__backing_countDownStartValue!.get(); + } + + public set countDownStartValue(value: number) { + this.__backing_countDownStartValue!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, (((("Grant") + (this.countDownStartValue))) + ("nuggets to play.")), undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.countDownStartValue += 1; + })); + return; + }), "+1 - Nuggets in New Game", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.countDownStartValue -= 1; + })); + return; + }), "-1 - Nuggets in New Game", undefined, undefined); + CountDownComponent._instantiateImpl(undefined, (() => { + return new CountDownComponent(); + }), { + count: this.countDownStartValue, + costOfOneAttempt: 2, + }, undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_CountDownComponent { + set count(count: (number | undefined)) + + get count(): (number | undefined) + set __backing_count(__backing_count: (IPropRefDecoratedVariable | undefined)) + + get __backing_count(): (IPropRefDecoratedVariable | undefined) + set costOfOneAttempt(costOfOneAttempt: (number | undefined)) + + get costOfOneAttempt(): (number | undefined) + +} + +@Component() export interface __Options_ParentComponent { + set countDownStartValue(countDownStartValue: (number | undefined)) + + get countDownStartValue(): (number | undefined) + set __backing_countDownStartValue(__backing_countDownStartValue: (IStateDecoratedVariable | undefined)) + + get __backing_countDownStartValue(): (IStateDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @PropRef decorated variables passing', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-basic-type.test.ts index 9ed163723..9b58c22be 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-basic-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-basic-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,30 +38,38 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { PropDecoratedVariable as PropDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { Prop as Prop } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class PropParent extends CustomComponent { - public __initializeStruct(initializers: __Options_PropParent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_propVar1 = new PropDecoratedVariable("propVar1", ((({let gensym___95172135 = initializers; + + +@Component() final struct PropParent extends CustomComponent { + public __initializeStruct(initializers: (__Options_PropParent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_propVar1 = STATE_MGMT_FACTORY.makeProp(this, "propVar1", ((({let gensym___95172135 = initializers; (((gensym___95172135) == (null)) ? undefined : gensym___95172135.propVar1)})) ?? ("propVar1"))); - this.__backing_propVar2 = new PropDecoratedVariable("propVar2", ((({let gensym___222490386 = initializers; + this.__backing_propVar2 = STATE_MGMT_FACTORY.makeProp(this, "propVar2", ((({let gensym___222490386 = initializers; (((gensym___222490386) == (null)) ? undefined : gensym___222490386.propVar2)})) ?? (50))); - this.__backing_propVar3 = new PropDecoratedVariable("propVar3", ((({let gensym___201781257 = initializers; + this.__backing_propVar3 = STATE_MGMT_FACTORY.makeProp(this, "propVar3", ((({let gensym___201781257 = initializers; (((gensym___201781257) == (null)) ? undefined : gensym___201781257.propVar3)})) ?? (true))); - this.__backing_propVar4 = new PropDecoratedVariable("propVar4", ((({let gensym___22028950 = initializers; + this.__backing_propVar4 = STATE_MGMT_FACTORY.makeProp(this, "propVar4", ((({let gensym___22028950 = initializers; (((gensym___22028950) == (null)) ? undefined : gensym___22028950.propVar4)})) ?? (undefined))); - this.__backing_propVar5 = new PropDecoratedVariable("propVar5", ((({let gensym___54872258 = initializers; + this.__backing_propVar5 = STATE_MGMT_FACTORY.makeProp(this, "propVar5", ((({let gensym___54872258 = initializers; (((gensym___54872258) == (null)) ? undefined : gensym___54872258.propVar5)})) ?? (null))); } - public __updateStruct(initializers: __Options_PropParent | undefined): void { + + public __updateStruct(initializers: (__Options_PropParent | undefined)): void { if (((({let gensym___67969738 = initializers; (((gensym___67969738) == (null)) ? undefined : gensym___67969738.propVar1)})) !== (undefined))) { this.__backing_propVar1!.update((initializers!.propVar1 as string)); @@ -82,66 +91,95 @@ function main() {} this.__backing_propVar5!.update((initializers!.propVar5 as null)); } } - private __backing_propVar1?: PropDecoratedVariable; + + private __backing_propVar1?: IPropDecoratedVariable; + public get propVar1(): string { return this.__backing_propVar1!.get(); } + public set propVar1(value: string) { this.__backing_propVar1!.set(value); } - private __backing_propVar2?: PropDecoratedVariable; + + private __backing_propVar2?: IPropDecoratedVariable; + public get propVar2(): number { return this.__backing_propVar2!.get(); } + public set propVar2(value: number) { this.__backing_propVar2!.set(value); } - private __backing_propVar3?: PropDecoratedVariable; + + private __backing_propVar3?: IPropDecoratedVariable; + public get propVar3(): boolean { return this.__backing_propVar3!.get(); } + public set propVar3(value: boolean) { this.__backing_propVar3!.set(value); } - private __backing_propVar4?: PropDecoratedVariable; + + private __backing_propVar4?: IPropDecoratedVariable; + public get propVar4(): undefined { return this.__backing_propVar4!.get(); } + public set propVar4(value: undefined) { this.__backing_propVar4!.set(value); } - private __backing_propVar5?: PropDecoratedVariable; + + private __backing_propVar5?: IPropDecoratedVariable; + public get propVar5(): null { return this.__backing_propVar5!.get(); } + public set propVar5(value: null) { this.__backing_propVar5!.set(value); } - @memo() public _build(@memo() style: ((instance: PropParent)=> PropParent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_PropParent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_PropParent { - set propVar1(propVar1: string | undefined) - get propVar1(): string | undefined - set __backing_propVar1(__backing_propVar1: PropDecoratedVariable | undefined) - get __backing_propVar1(): PropDecoratedVariable | undefined - set propVar2(propVar2: number | undefined) - get propVar2(): number | undefined - set __backing_propVar2(__backing_propVar2: PropDecoratedVariable | undefined) - get __backing_propVar2(): PropDecoratedVariable | undefined - set propVar3(propVar3: boolean | undefined) - get propVar3(): boolean | undefined - set __backing_propVar3(__backing_propVar3: PropDecoratedVariable | undefined) - get __backing_propVar3(): PropDecoratedVariable | undefined - set propVar4(propVar4: undefined | undefined) - get propVar4(): undefined | undefined - set __backing_propVar4(__backing_propVar4: PropDecoratedVariable | undefined) - get __backing_propVar4(): PropDecoratedVariable | undefined - set propVar5(propVar5: null | undefined) - get propVar5(): null | undefined - set __backing_propVar5(__backing_propVar5: PropDecoratedVariable | undefined) - get __backing_propVar5(): PropDecoratedVariable | undefined +@Component() export interface __Options_PropParent { + set propVar1(propVar1: (string | undefined)) + + get propVar1(): (string | undefined) + set __backing_propVar1(__backing_propVar1: (IPropDecoratedVariable | undefined)) + + get __backing_propVar1(): (IPropDecoratedVariable | undefined) + set propVar2(propVar2: (number | undefined)) + + get propVar2(): (number | undefined) + set __backing_propVar2(__backing_propVar2: (IPropDecoratedVariable | undefined)) + + get __backing_propVar2(): (IPropDecoratedVariable | undefined) + set propVar3(propVar3: (boolean | undefined)) + + get propVar3(): (boolean | undefined) + set __backing_propVar3(__backing_propVar3: (IPropDecoratedVariable | undefined)) + + get __backing_propVar3(): (IPropDecoratedVariable | undefined) + set propVar4(propVar4: (undefined | undefined)) + + get propVar4(): (undefined | undefined) + set __backing_propVar4(__backing_propVar4: (IPropDecoratedVariable | undefined)) + + get __backing_propVar4(): (IPropDecoratedVariable | undefined) + set propVar5(propVar5: (null | undefined)) + + get propVar5(): (null | undefined) + set __backing_propVar5(__backing_propVar5: (IPropDecoratedVariable | undefined)) + + get __backing_propVar5(): (IPropDecoratedVariable | undefined) + } `; @@ -151,9 +189,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test basic type @Prop decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-complex-type.test.ts index 7db445781..c619e10ce 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-complex-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,40 +38,58 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { PropDecoratedVariable as PropDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { Prop as Prop } from "@ohos.arkui.stateManagement"; function main() {} class Per { public num: number; + public constructor(num: number) { this.num = num; } + } final class PropType extends BaseEnum { private readonly #ordinal: int; + private static () {} + public constructor(ordinal: int, value: int) { super(value); this.#ordinal = ordinal; } + public static readonly TYPE1: PropType = new PropType(0, 0); + public static readonly TYPE2: PropType = new PropType(1, 1); + public static readonly TYPE3: PropType = new PropType(2, 3); + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + private static readonly #ValuesArray: int[] = [0, 1, 3]; + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + private static readonly #ItemsArray: PropType[] = [PropType.TYPE1, PropType.TYPE2, PropType.TYPE3]; + public getName(): String { return PropType.#NamesArray[this.#ordinal]; } + public static getValueOf(name: String): PropType { for (let i = 0;((i) < (PropType.#NamesArray.length));(++i)) { if (((name) == (PropType.#NamesArray[i]))) { @@ -79,6 +98,7 @@ final class PropType extends BaseEnum { } throw new Error((("No enum constant PropType.") + (name))); } + public static fromValue(value: int): PropType { for (let i = 0;((i) < (PropType.#ValuesArray.length));(++i)) { if (((value) == (PropType.#ValuesArray[i]))) { @@ -87,51 +107,58 @@ final class PropType extends BaseEnum { } throw new Error((("No enum PropType with value ") + (value))); } + public valueOf(): int { return PropType.#ValuesArray[this.#ordinal]; } + public toString(): String { return PropType.#StringValuesArray[this.#ordinal]; } + public static values(): PropType[] { return PropType.#ItemsArray; } + public getOrdinal(): int { return this.#ordinal; } + public static $_get(e: PropType): String { return e.getName(); } + } -@Component({freezeWhenInactive:false}) final class Parent extends CustomComponent { - public __initializeStruct(initializers: __Options_Parent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_propVar1 = new PropDecoratedVariable("propVar1", ((({let gensym___95172135 = initializers; +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_propVar1 = STATE_MGMT_FACTORY.makeProp(this, "propVar1", ((({let gensym___95172135 = initializers; (((gensym___95172135) == (null)) ? undefined : gensym___95172135.propVar1)})) ?? (new Per(6)))); - this.__backing_propVar2 = new PropDecoratedVariable>("propVar2", ((({let gensym___222490386 = initializers; + this.__backing_propVar2 = STATE_MGMT_FACTORY.makeProp>(this, "propVar2", ((({let gensym___222490386 = initializers; (((gensym___222490386) == (null)) ? undefined : gensym___222490386.propVar2)})) ?? (new Array(3, 6, 8)))); - this.__backing_propVar3 = new PropDecoratedVariable("propVar3", ((({let gensym___201781257 = initializers; + this.__backing_propVar3 = STATE_MGMT_FACTORY.makeProp(this, "propVar3", ((({let gensym___201781257 = initializers; (((gensym___201781257) == (null)) ? undefined : gensym___201781257.propVar3)})) ?? (PropType.TYPE3))); - this.__backing_propVar4 = new PropDecoratedVariable>("propVar4", ((({let gensym___22028950 = initializers; + this.__backing_propVar4 = STATE_MGMT_FACTORY.makeProp>(this, "propVar4", ((({let gensym___22028950 = initializers; (((gensym___22028950) == (null)) ? undefined : gensym___22028950.propVar4)})) ?? (new Set(new Array("aa", "bb"))))); - this.__backing_propVar5 = new PropDecoratedVariable>("propVar5", ((({let gensym___54872258 = initializers; + this.__backing_propVar5 = STATE_MGMT_FACTORY.makeProp>(this, "propVar5", ((({let gensym___54872258 = initializers; (((gensym___54872258) == (null)) ? undefined : gensym___54872258.propVar5)})) ?? ([true, false]))); - this.__backing_propVar6 = new PropDecoratedVariable>("propVar6", ((({let gensym___128760941 = initializers; + this.__backing_propVar6 = STATE_MGMT_FACTORY.makeProp>(this, "propVar6", ((({let gensym___128760941 = initializers; (((gensym___128760941) == (null)) ? undefined : gensym___128760941.propVar6)})) ?? (new Array(new Per(7), new Per(11))))); - this.__backing_propVar7 = new PropDecoratedVariable>("propVar7", ((({let gensym___30534085 = initializers; + this.__backing_propVar7 = STATE_MGMT_FACTORY.makeProp>(this, "propVar7", ((({let gensym___30534085 = initializers; (((gensym___30534085) == (null)) ? undefined : gensym___30534085.propVar7)})) ?? ([new Per(7), new Per(11)]))); - this.__backing_propVar8 = new PropDecoratedVariable<((sr: string)=> void)>("propVar8", ((({let gensym___12471776 = initializers; + this.__backing_propVar8 = STATE_MGMT_FACTORY.makeProp<((sr: string)=> void)>(this, "propVar8", ((({let gensym___12471776 = initializers; (((gensym___12471776) == (null)) ? undefined : gensym___12471776.propVar8)})) ?? (((sr: string) => {})))); - this.__backing_propVar9 = new PropDecoratedVariable("propVar9", ((({let gensym___123472108 = initializers; + this.__backing_propVar9 = STATE_MGMT_FACTORY.makeProp(this, "propVar9", ((({let gensym___123472108 = initializers; (((gensym___123472108) == (null)) ? undefined : gensym___123472108.propVar9)})) ?? (new Date("2025-4-23")))); - this.__backing_propVar10 = new PropDecoratedVariable>("propVar10", ((({let gensym___147847012 = initializers; + this.__backing_propVar10 = STATE_MGMT_FACTORY.makeProp>(this, "propVar10", ((({let gensym___147847012 = initializers; (((gensym___147847012) == (null)) ? undefined : gensym___147847012.propVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]])))); - this.__backing_propVar11 = new PropDecoratedVariable("propVar11", ((({let gensym___117026760 = initializers; + this.__backing_propVar11 = STATE_MGMT_FACTORY.makeProp<(string | number)>(this, "propVar11", ((({let gensym___117026760 = initializers; (((gensym___117026760) == (null)) ? undefined : gensym___117026760.propVar11)})) ?? (0.0))); - this.__backing_propVar12 = new PropDecoratedVariable | Per>("propVar12", ((({let gensym___220245132 = initializers; + this.__backing_propVar12 = STATE_MGMT_FACTORY.makeProp<(Set | Per)>(this, "propVar12", ((({let gensym___220245132 = initializers; (((gensym___220245132) == (null)) ? undefined : gensym___220245132.propVar12)})) ?? (new Per(6)))); } - public __updateStruct(initializers: __Options_Parent | undefined): void { + + public __updateStruct(initializers: (__Options_Parent | undefined)): void { if (((({let gensym___67969738 = initializers; (((gensym___67969738) == (null)) ? undefined : gensym___67969738.propVar1)})) !== (undefined))) { this.__backing_propVar1!.update((initializers!.propVar1 as Per)); @@ -174,150 +201,214 @@ final class PropType extends BaseEnum { } if (((({let gensym___2015283 = initializers; (((gensym___2015283) == (null)) ? undefined : gensym___2015283.propVar11)})) !== (undefined))) { - this.__backing_propVar11!.update((initializers!.propVar11 as string | number)); + this.__backing_propVar11!.update((initializers!.propVar11 as (string | number))); } if (((({let gensym___39009414 = initializers; (((gensym___39009414) == (null)) ? undefined : gensym___39009414.propVar12)})) !== (undefined))) { - this.__backing_propVar12!.update((initializers!.propVar12 as Set | Per)); + this.__backing_propVar12!.update((initializers!.propVar12 as (Set | Per))); } } - private __backing_propVar1?: PropDecoratedVariable; + + private __backing_propVar1?: IPropDecoratedVariable; + public get propVar1(): Per { return this.__backing_propVar1!.get(); } + public set propVar1(value: Per) { this.__backing_propVar1!.set(value); } - private __backing_propVar2?: PropDecoratedVariable>; + + private __backing_propVar2?: IPropDecoratedVariable>; + public get propVar2(): Array { return this.__backing_propVar2!.get(); } + public set propVar2(value: Array) { this.__backing_propVar2!.set(value); } - private __backing_propVar3?: PropDecoratedVariable; + + private __backing_propVar3?: IPropDecoratedVariable; + public get propVar3(): PropType { return this.__backing_propVar3!.get(); } + public set propVar3(value: PropType) { this.__backing_propVar3!.set(value); } - private __backing_propVar4?: PropDecoratedVariable>; + + private __backing_propVar4?: IPropDecoratedVariable>; + public get propVar4(): Set { return this.__backing_propVar4!.get(); } + public set propVar4(value: Set) { this.__backing_propVar4!.set(value); } - private __backing_propVar5?: PropDecoratedVariable>; + + private __backing_propVar5?: IPropDecoratedVariable>; + public get propVar5(): Array { return this.__backing_propVar5!.get(); } + public set propVar5(value: Array) { this.__backing_propVar5!.set(value); } - private __backing_propVar6?: PropDecoratedVariable>; + + private __backing_propVar6?: IPropDecoratedVariable>; + public get propVar6(): Array { return this.__backing_propVar6!.get(); } + public set propVar6(value: Array) { this.__backing_propVar6!.set(value); } - private __backing_propVar7?: PropDecoratedVariable>; + + private __backing_propVar7?: IPropDecoratedVariable>; + public get propVar7(): Array { return this.__backing_propVar7!.get(); } + public set propVar7(value: Array) { this.__backing_propVar7!.set(value); } - private __backing_propVar8?: PropDecoratedVariable<((sr: string)=> void)>; + + private __backing_propVar8?: IPropDecoratedVariable<((sr: string)=> void)>; + public get propVar8(): ((sr: string)=> void) { return this.__backing_propVar8!.get(); } + public set propVar8(value: ((sr: string)=> void)) { this.__backing_propVar8!.set(value); } - private __backing_propVar9?: PropDecoratedVariable; + + private __backing_propVar9?: IPropDecoratedVariable; + public get propVar9(): Date { return this.__backing_propVar9!.get(); } + public set propVar9(value: Date) { this.__backing_propVar9!.set(value); } - private __backing_propVar10?: PropDecoratedVariable>; + + private __backing_propVar10?: IPropDecoratedVariable>; + public get propVar10(): Map { return this.__backing_propVar10!.get(); } + public set propVar10(value: Map) { this.__backing_propVar10!.set(value); } - private __backing_propVar11?: PropDecoratedVariable; - public get propVar11(): string | number { + + private __backing_propVar11?: IPropDecoratedVariable<(string | number)>; + + public get propVar11(): (string | number) { return this.__backing_propVar11!.get(); } - public set propVar11(value: string | number) { + + public set propVar11(value: (string | number)) { this.__backing_propVar11!.set(value); } - private __backing_propVar12?: PropDecoratedVariable | Per>; - public get propVar12(): Set | Per { + + private __backing_propVar12?: IPropDecoratedVariable<(Set | Per)>; + + public get propVar12(): (Set | Per) { return this.__backing_propVar12!.get(); } - public set propVar12(value: Set | Per) { + + public set propVar12(value: (Set | Per)) { this.__backing_propVar12!.set(value); } - @memo() public _build(@memo() style: ((instance: Parent)=> Parent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_Parent { - set propVar1(propVar1: Per | undefined) - get propVar1(): Per | undefined - set __backing_propVar1(__backing_propVar1: PropDecoratedVariable | undefined) - get __backing_propVar1(): PropDecoratedVariable | undefined - set propVar2(propVar2: Array | undefined) - get propVar2(): Array | undefined - set __backing_propVar2(__backing_propVar2: PropDecoratedVariable> | undefined) - get __backing_propVar2(): PropDecoratedVariable> | undefined - set propVar3(propVar3: PropType | undefined) - get propVar3(): PropType | undefined - set __backing_propVar3(__backing_propVar3: PropDecoratedVariable | undefined) - get __backing_propVar3(): PropDecoratedVariable | undefined - set propVar4(propVar4: Set | undefined) - get propVar4(): Set | undefined - set __backing_propVar4(__backing_propVar4: PropDecoratedVariable> | undefined) - get __backing_propVar4(): PropDecoratedVariable> | undefined - set propVar5(propVar5: Array | undefined) - get propVar5(): Array | undefined - set __backing_propVar5(__backing_propVar5: PropDecoratedVariable> | undefined) - get __backing_propVar5(): PropDecoratedVariable> | undefined - set propVar6(propVar6: Array | undefined) - get propVar6(): Array | undefined - set __backing_propVar6(__backing_propVar6: PropDecoratedVariable> | undefined) - get __backing_propVar6(): PropDecoratedVariable> | undefined - set propVar7(propVar7: Array | undefined) - get propVar7(): Array | undefined - set __backing_propVar7(__backing_propVar7: PropDecoratedVariable> | undefined) - get __backing_propVar7(): PropDecoratedVariable> | undefined - set propVar8(propVar8: ((sr: string)=> void) | undefined) - get propVar8(): ((sr: string)=> void) | undefined - set __backing_propVar8(__backing_propVar8: PropDecoratedVariable<((sr: string)=> void)> | undefined) - get __backing_propVar8(): PropDecoratedVariable<((sr: string)=> void)> | undefined - set propVar9(propVar9: Date | undefined) - get propVar9(): Date | undefined - set __backing_propVar9(__backing_propVar9: PropDecoratedVariable | undefined) - get __backing_propVar9(): PropDecoratedVariable | undefined - set propVar10(propVar10: Map | undefined) - get propVar10(): Map | undefined - set __backing_propVar10(__backing_propVar10: PropDecoratedVariable> | undefined) - get __backing_propVar10(): PropDecoratedVariable> | undefined - set propVar11(propVar11: string | number | undefined) - get propVar11(): string | number | undefined - set __backing_propVar11(__backing_propVar11: PropDecoratedVariable | undefined) - get __backing_propVar11(): PropDecoratedVariable | undefined - set propVar12(propVar12: Set | Per | undefined) - get propVar12(): Set | Per | undefined - set __backing_propVar12(__backing_propVar12: PropDecoratedVariable | Per> | undefined) - get __backing_propVar12(): PropDecoratedVariable | Per> | undefined +@Component() export interface __Options_Parent { + set propVar1(propVar1: (Per | undefined)) + + get propVar1(): (Per | undefined) + set __backing_propVar1(__backing_propVar1: (IPropDecoratedVariable | undefined)) + + get __backing_propVar1(): (IPropDecoratedVariable | undefined) + set propVar2(propVar2: (Array | undefined)) + + get propVar2(): (Array | undefined) + set __backing_propVar2(__backing_propVar2: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar2(): (IPropDecoratedVariable> | undefined) + set propVar3(propVar3: (PropType | undefined)) + + get propVar3(): (PropType | undefined) + set __backing_propVar3(__backing_propVar3: (IPropDecoratedVariable | undefined)) + + get __backing_propVar3(): (IPropDecoratedVariable | undefined) + set propVar4(propVar4: (Set | undefined)) + + get propVar4(): (Set | undefined) + set __backing_propVar4(__backing_propVar4: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar4(): (IPropDecoratedVariable> | undefined) + set propVar5(propVar5: (Array | undefined)) + + get propVar5(): (Array | undefined) + set __backing_propVar5(__backing_propVar5: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar5(): (IPropDecoratedVariable> | undefined) + set propVar6(propVar6: (Array | undefined)) + + get propVar6(): (Array | undefined) + set __backing_propVar6(__backing_propVar6: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar6(): (IPropDecoratedVariable> | undefined) + set propVar7(propVar7: (Array | undefined)) + + get propVar7(): (Array | undefined) + set __backing_propVar7(__backing_propVar7: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar7(): (IPropDecoratedVariable> | undefined) + set propVar8(propVar8: (((sr: string)=> void) | undefined)) + + get propVar8(): (((sr: string)=> void) | undefined) + set __backing_propVar8(__backing_propVar8: (IPropDecoratedVariable<((sr: string)=> void)> | undefined)) + + get __backing_propVar8(): (IPropDecoratedVariable<((sr: string)=> void)> | undefined) + set propVar9(propVar9: (Date | undefined)) + + get propVar9(): (Date | undefined) + set __backing_propVar9(__backing_propVar9: (IPropDecoratedVariable | undefined)) + + get __backing_propVar9(): (IPropDecoratedVariable | undefined) + set propVar10(propVar10: (Map | undefined)) + + get propVar10(): (Map | undefined) + set __backing_propVar10(__backing_propVar10: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar10(): (IPropDecoratedVariable> | undefined) + set propVar11(propVar11: ((string | number) | undefined)) + + get propVar11(): ((string | number) | undefined) + set __backing_propVar11(__backing_propVar11: (IPropDecoratedVariable<(string | number)> | undefined)) + + get __backing_propVar11(): (IPropDecoratedVariable<(string | number)> | undefined) + set propVar12(propVar12: ((Set | Per) | undefined)) + + get propVar12(): ((Set | Per) | undefined) + set __backing_propVar12(__backing_propVar12: (IPropDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_propVar12(): (IPropDecoratedVariable<(Set | Per)> | undefined) + } `; @@ -327,9 +418,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test complex type @Prop decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts index 0809c2d46..3166366c1 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,56 +38,77 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { ConditionScope as ConditionScope } from "arkui.component.builder"; + +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { PropDecoratedVariable as PropDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; -import { UIButtonAttribute as UIButtonAttribute } from "@ohos.arkui.component"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, Text as Text, Button as Button, Column as Column, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + import { Prop as Prop, State as State } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class CountDownComponent extends CustomComponent { - public __initializeStruct(initializers: __Options_CountDownComponent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_count = new PropDecoratedVariable("count", ((({let gensym___58710805 = initializers; +@Component() final struct CountDownComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_CountDownComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_count = STATE_MGMT_FACTORY.makeProp(this, "count", ((({let gensym___58710805 = initializers; (((gensym___58710805) == (null)) ? undefined : gensym___58710805.count)})) ?? (0))); this.__backing_costOfOneAttempt = ((({let gensym___88948111 = initializers; (((gensym___88948111) == (null)) ? undefined : gensym___88948111.costOfOneAttempt)})) ?? (1)); } - public __updateStruct(initializers: __Options_CountDownComponent | undefined): void { + + public __updateStruct(initializers: (__Options_CountDownComponent | undefined)): void { if (((({let gensym___188547633 = initializers; (((gensym___188547633) == (null)) ? undefined : gensym___188547633.count)})) !== (undefined))) { this.__backing_count!.update((initializers!.count as number)); } } - private __backing_count?: PropDecoratedVariable; + + private __backing_count?: IPropDecoratedVariable; + public get count(): number { return this.__backing_count!.get(); } + public set count(value: number) { this.__backing_count!.set(value); } + private __backing_costOfOneAttempt?: number; + public get costOfOneAttempt(): number { return (this.__backing_costOfOneAttempt as number); } + public set costOfOneAttempt(value: number) { this.__backing_costOfOneAttempt = value; } - @memo() public _build(@memo() style: ((instance: CountDownComponent)=> CountDownComponent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_CountDownComponent | undefined): void { - Column(undefined, undefined, (() => { - if (((this.count) > (0))) { - Text(undefined, (((("You have") + (this.count))) + ("Nuggets left")), undefined, undefined); - } else { - Text(undefined, "Game over!", undefined, undefined); - } - Button(@memo() ((instance: UIButtonAttribute): void => { + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + if (((this.count) > (0))) { + ConditionBranch(@memo() (() => { + Text(undefined, (((("You have") + (this.count))) + ("Nuggets left")), undefined, undefined); + })); + } else { + ConditionBranch(@memo() (() => { + Text(undefined, "Game over!", undefined, undefined); + })); + } + })); + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.count -= this.costOfOneAttempt; })); @@ -94,32 +116,39 @@ function main() {} }), "Try again", undefined, undefined); })); } + public constructor() {} + } -@Component({freezeWhenInactive:false}) final class ParentComponent extends CustomComponent { - public __initializeStruct(initializers: __Options_ParentComponent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_countDownStartValue = new StateDecoratedVariable("countDownStartValue", ((({let gensym___249912438 = initializers; +@Component() final struct ParentComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_ParentComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_countDownStartValue = STATE_MGMT_FACTORY.makeState(this, "countDownStartValue", ((({let gensym___249912438 = initializers; (((gensym___249912438) == (null)) ? undefined : gensym___249912438.countDownStartValue)})) ?? (10))); } - public __updateStruct(initializers: __Options_ParentComponent | undefined): void {} - private __backing_countDownStartValue?: StateDecoratedVariable; + + public __updateStruct(initializers: (__Options_ParentComponent | undefined)): void {} + + private __backing_countDownStartValue?: IStateDecoratedVariable; + public get countDownStartValue(): number { return this.__backing_countDownStartValue!.get(); } + public set countDownStartValue(value: number) { this.__backing_countDownStartValue!.set(value); } - @memo() public _build(@memo() style: ((instance: ParentComponent)=> ParentComponent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_ParentComponent | undefined): void { - Column(undefined, undefined, (() => { + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { Text(undefined, (((("Grant") + (this.countDownStartValue))) + ("nuggets to play.")), undefined, undefined); - Button(@memo() ((instance: UIButtonAttribute): void => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.countDownStartValue += 1; })); return; }), "+1 - Nuggets in New Game", undefined, undefined); - Button(@memo() ((instance: UIButtonAttribute): void => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.countDownStartValue -= 1; })); @@ -127,29 +156,38 @@ function main() {} }), "-1 - Nuggets in New Game", undefined, undefined); CountDownComponent._instantiateImpl(undefined, (() => { return new CountDownComponent(); - }), ({ + }), { count: this.countDownStartValue, costOfOneAttempt: 2, - } as __Options_CountDownComponent), undefined, undefined); + }, undefined, undefined); })); } + public constructor() {} + } -interface __Options_CountDownComponent { - set count(count: number | undefined) - get count(): number | undefined - set __backing_count(__backing_count: PropDecoratedVariable | undefined) - get __backing_count(): PropDecoratedVariable | undefined - set costOfOneAttempt(costOfOneAttempt: number | undefined) - get costOfOneAttempt(): number | undefined +@Component() export interface __Options_CountDownComponent { + set count(count: (number | undefined)) + + get count(): (number | undefined) + set __backing_count(__backing_count: (IPropDecoratedVariable | undefined)) + + get __backing_count(): (IPropDecoratedVariable | undefined) + set costOfOneAttempt(costOfOneAttempt: (number | undefined)) + + get costOfOneAttempt(): (number | undefined) + } -interface __Options_ParentComponent { - set countDownStartValue(countDownStartValue: number | undefined) - get countDownStartValue(): number | undefined - set __backing_countDownStartValue(__backing_countDownStartValue: StateDecoratedVariable | undefined) - get __backing_countDownStartValue(): StateDecoratedVariable | undefined +@Component() export interface __Options_ParentComponent { + set countDownStartValue(countDownStartValue: (number | undefined)) + + get countDownStartValue(): (number | undefined) + set __backing_countDownStartValue(__backing_countDownStartValue: (IStateDecoratedVariable | undefined)) + + get __backing_countDownStartValue(): (IStateDecoratedVariable | undefined) + } `; @@ -159,9 +197,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test @Prop decorated variables passing', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/consume-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/consume-basic-type.test.ts new file mode 100644 index 000000000..f0e7053e3 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/consume-basic-type.test.ts @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provide-and-consume'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'consume-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @Consume decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedParsedScript: string = ` +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Consume as Consume } from "@ohos.arkui.stateManagement"; + +@Component() final struct PropParent extends CustomComponent { + @Consume() public conVar1!: string; + + @Consume() public conVar2!: number; + + @Consume() public conVar3!: boolean; + + @Consume() public conVar4?: undefined; + + @Consume() public conVar5?: null; + + public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_PropParent { + conVar1?: string; + @Consume() __backing_conVar1?: string; + conVar2?: number; + @Consume() __backing_conVar2?: number; + conVar3?: boolean; + @Consume() __backing_conVar3?: boolean; + conVar4?: undefined; + @Consume() __backing_conVar4?: undefined; + conVar5?: null; + @Consume() __backing_conVar5?: null; + +} +`; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IConsumeDecoratedVariable as IConsumeDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Consume as Consume } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@Component() final struct PropParent extends CustomComponent { + public __initializeStruct(initializers: (__Options_PropParent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_conVar1 = STATE_MGMT_FACTORY.makeConsume(this, "conVar1", "conVar1"); + this.__backing_conVar2 = STATE_MGMT_FACTORY.makeConsume(this, "conVar2", "conVar2"); + this.__backing_conVar3 = STATE_MGMT_FACTORY.makeConsume(this, "conVar3", "conVar3"); + this.__backing_conVar4 = STATE_MGMT_FACTORY.makeConsume(this, "conVar4", "conVar4"); + this.__backing_conVar5 = STATE_MGMT_FACTORY.makeConsume(this, "conVar5", "conVar5"); + } + + public __updateStruct(initializers: (__Options_PropParent | undefined)): void {} + + private __backing_conVar1?: IConsumeDecoratedVariable; + + public get conVar1(): string { + return this.__backing_conVar1!.get(); + } + + public set conVar1(value: string) { + this.__backing_conVar1!.set(value); + } + + private __backing_conVar2?: IConsumeDecoratedVariable; + + public get conVar2(): number { + return this.__backing_conVar2!.get(); + } + + public set conVar2(value: number) { + this.__backing_conVar2!.set(value); + } + + private __backing_conVar3?: IConsumeDecoratedVariable; + + public get conVar3(): boolean { + return this.__backing_conVar3!.get(); + } + + public set conVar3(value: boolean) { + this.__backing_conVar3!.set(value); + } + + private __backing_conVar4?: IConsumeDecoratedVariable; + + public get conVar4(): undefined { + return this.__backing_conVar4!.get(); + } + + public set conVar4(value: undefined) { + this.__backing_conVar4!.set(value); + } + + private __backing_conVar5?: IConsumeDecoratedVariable; + + public get conVar5(): null { + return this.__backing_conVar5!.get(); + } + + public set conVar5(value: null) { + this.__backing_conVar5!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_PropParent { + set conVar1(conVar1: (string | undefined)) + + get conVar1(): (string | undefined) + set __backing_conVar1(__backing_conVar1: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar1(): (IConsumeDecoratedVariable | undefined) + set conVar2(conVar2: (number | undefined)) + + get conVar2(): (number | undefined) + set __backing_conVar2(__backing_conVar2: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar2(): (IConsumeDecoratedVariable | undefined) + set conVar3(conVar3: (boolean | undefined)) + + get conVar3(): (boolean | undefined) + set __backing_conVar3(__backing_conVar3: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar3(): (IConsumeDecoratedVariable | undefined) + set conVar4(conVar4: (undefined | undefined)) + + get conVar4(): (undefined | undefined) + set __backing_conVar4(__backing_conVar4: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar4(): (IConsumeDecoratedVariable | undefined) + set conVar5(conVar5: (null | undefined)) + + get conVar5(): (null | undefined) + set __backing_conVar5(__backing_conVar5: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar5(): (IConsumeDecoratedVariable | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test basic type @Consume decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:struct-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/consume-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/consume-complex-type.test.ts new file mode 100644 index 000000000..202edb7b0 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/consume-complex-type.test.ts @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provide-and-consume'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'consume-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @Consume decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedParsedScript: string = ` +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Consume as Consume } from "@ohos.arkui.stateManagement"; + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +enum PropType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@Component() final struct Parent extends CustomComponent { + @Consume() public conVar1!: Per; + + @Consume() public conVar2!: Array; + + @Consume() public conVar3!: PropType; + + @Consume() public conVar4!: Set; + + @Consume() public conVar5!: boolean[]; + + @Consume() public conVar6!: Array; + + @Consume() public conVar7!: Per[]; + + @Consume() public conVar8!: ((sr: string)=> void); + + @Consume() public conVar9!: Date; + + @Consume() public conVar10!: Map; + + @Consume() public conVar11!: (string | number); + + @Consume() public conVar12!: (Set | Per); + + @Consume() public conVar13?: (Set | null); + + public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_Parent { + conVar1?: Per; + @Consume() __backing_conVar1?: Per; + conVar2?: Array; + @Consume() __backing_conVar2?: Array; + conVar3?: PropType; + @Consume() __backing_conVar3?: PropType; + conVar4?: Set; + @Consume() __backing_conVar4?: Set; + conVar5?: boolean[]; + @Consume() __backing_conVar5?: boolean[]; + conVar6?: Array; + @Consume() __backing_conVar6?: Array; + conVar7?: Per[]; + @Consume() __backing_conVar7?: Per[]; + conVar8?: ((sr: string)=> void); + @Consume() __backing_conVar8?: ((sr: string)=> void); + conVar9?: Date; + @Consume() __backing_conVar9?: Date; + conVar10?: Map; + @Consume() __backing_conVar10?: Map; + conVar11?: (string | number); + @Consume() __backing_conVar11?: (string | number); + conVar12?: (Set | Per); + @Consume() __backing_conVar12?: (Set | Per); + conVar13?: (Set | null); + @Consume() __backing_conVar13?: (Set | null); + +} +`; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IConsumeDecoratedVariable as IConsumeDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Consume as Consume } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class PropType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: PropType = new PropType(0, 0); + + public static readonly TYPE2: PropType = new PropType(1, 1); + + public static readonly TYPE3: PropType = new PropType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: PropType[] = [PropType.TYPE1, PropType.TYPE2, PropType.TYPE3]; + + public getName(): String { + return PropType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): PropType { + for (let i = 0;((i) < (PropType.#NamesArray.length));(++i)) { + if (((name) == (PropType.#NamesArray[i]))) { + return PropType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant PropType.") + (name))); + } + + public static fromValue(value: int): PropType { + for (let i = 0;((i) < (PropType.#ValuesArray.length));(++i)) { + if (((value) == (PropType.#ValuesArray[i]))) { + return PropType.#ItemsArray[i]; + } + } + throw new Error((("No enum PropType with value ") + (value))); + } + + public valueOf(): int { + return PropType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return PropType.#StringValuesArray[this.#ordinal]; + } + + public static values(): PropType[] { + return PropType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: PropType): String { + return e.getName(); + } + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_conVar1 = STATE_MGMT_FACTORY.makeConsume(this, "conVar1", "conVar1"); + this.__backing_conVar2 = STATE_MGMT_FACTORY.makeConsume>(this, "conVar2", "conVar2"); + this.__backing_conVar3 = STATE_MGMT_FACTORY.makeConsume(this, "conVar3", "conVar3"); + this.__backing_conVar4 = STATE_MGMT_FACTORY.makeConsume>(this, "conVar4", "conVar4"); + this.__backing_conVar5 = STATE_MGMT_FACTORY.makeConsume>(this, "conVar5", "conVar5"); + this.__backing_conVar6 = STATE_MGMT_FACTORY.makeConsume>(this, "conVar6", "conVar6"); + this.__backing_conVar7 = STATE_MGMT_FACTORY.makeConsume>(this, "conVar7", "conVar7"); + this.__backing_conVar8 = STATE_MGMT_FACTORY.makeConsume<((sr: string)=> void)>(this, "conVar8", "conVar8"); + this.__backing_conVar9 = STATE_MGMT_FACTORY.makeConsume(this, "conVar9", "conVar9"); + this.__backing_conVar10 = STATE_MGMT_FACTORY.makeConsume>(this, "conVar10", "conVar10"); + this.__backing_conVar11 = STATE_MGMT_FACTORY.makeConsume<(string | number)>(this, "conVar11", "conVar11"); + this.__backing_conVar12 = STATE_MGMT_FACTORY.makeConsume<(Set | Per)>(this, "conVar12", "conVar12"); + this.__backing_conVar13 = STATE_MGMT_FACTORY.makeConsume<(Set | null)>(this, "conVar13", "conVar13"); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_conVar1?: IConsumeDecoratedVariable; + + public get conVar1(): Per { + return this.__backing_conVar1!.get(); + } + + public set conVar1(value: Per) { + this.__backing_conVar1!.set(value); + } + + private __backing_conVar2?: IConsumeDecoratedVariable>; + + public get conVar2(): Array { + return this.__backing_conVar2!.get(); + } + + public set conVar2(value: Array) { + this.__backing_conVar2!.set(value); + } + + private __backing_conVar3?: IConsumeDecoratedVariable; + + public get conVar3(): PropType { + return this.__backing_conVar3!.get(); + } + + public set conVar3(value: PropType) { + this.__backing_conVar3!.set(value); + } + + private __backing_conVar4?: IConsumeDecoratedVariable>; + + public get conVar4(): Set { + return this.__backing_conVar4!.get(); + } + + public set conVar4(value: Set) { + this.__backing_conVar4!.set(value); + } + + private __backing_conVar5?: IConsumeDecoratedVariable>; + + public get conVar5(): Array { + return this.__backing_conVar5!.get(); + } + + public set conVar5(value: Array) { + this.__backing_conVar5!.set(value); + } + + private __backing_conVar6?: IConsumeDecoratedVariable>; + + public get conVar6(): Array { + return this.__backing_conVar6!.get(); + } + + public set conVar6(value: Array) { + this.__backing_conVar6!.set(value); + } + + private __backing_conVar7?: IConsumeDecoratedVariable>; + + public get conVar7(): Array { + return this.__backing_conVar7!.get(); + } + + public set conVar7(value: Array) { + this.__backing_conVar7!.set(value); + } + + private __backing_conVar8?: IConsumeDecoratedVariable<((sr: string)=> void)>; + + public get conVar8(): ((sr: string)=> void) { + return this.__backing_conVar8!.get(); + } + + public set conVar8(value: ((sr: string)=> void)) { + this.__backing_conVar8!.set(value); + } + + private __backing_conVar9?: IConsumeDecoratedVariable; + + public get conVar9(): Date { + return this.__backing_conVar9!.get(); + } + + public set conVar9(value: Date) { + this.__backing_conVar9!.set(value); + } + + private __backing_conVar10?: IConsumeDecoratedVariable>; + + public get conVar10(): Map { + return this.__backing_conVar10!.get(); + } + + public set conVar10(value: Map) { + this.__backing_conVar10!.set(value); + } + + private __backing_conVar11?: IConsumeDecoratedVariable<(string | number)>; + + public get conVar11(): (string | number) { + return this.__backing_conVar11!.get(); + } + + public set conVar11(value: (string | number)) { + this.__backing_conVar11!.set(value); + } + + private __backing_conVar12?: IConsumeDecoratedVariable<(Set | Per)>; + + public get conVar12(): (Set | Per) { + return this.__backing_conVar12!.get(); + } + + public set conVar12(value: (Set | Per)) { + this.__backing_conVar12!.set(value); + } + + private __backing_conVar13?: IConsumeDecoratedVariable<(Set | null)>; + + public get conVar13(): (Set | null) { + return this.__backing_conVar13!.get(); + } + + public set conVar13(value: (Set | null)) { + this.__backing_conVar13!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_Parent { + set conVar1(conVar1: (Per | undefined)) + + get conVar1(): (Per | undefined) + set __backing_conVar1(__backing_conVar1: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar1(): (IConsumeDecoratedVariable | undefined) + set conVar2(conVar2: (Array | undefined)) + + get conVar2(): (Array | undefined) + set __backing_conVar2(__backing_conVar2: (IConsumeDecoratedVariable> | undefined)) + + get __backing_conVar2(): (IConsumeDecoratedVariable> | undefined) + set conVar3(conVar3: (PropType | undefined)) + + get conVar3(): (PropType | undefined) + set __backing_conVar3(__backing_conVar3: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar3(): (IConsumeDecoratedVariable | undefined) + set conVar4(conVar4: (Set | undefined)) + + get conVar4(): (Set | undefined) + set __backing_conVar4(__backing_conVar4: (IConsumeDecoratedVariable> | undefined)) + + get __backing_conVar4(): (IConsumeDecoratedVariable> | undefined) + set conVar5(conVar5: (Array | undefined)) + + get conVar5(): (Array | undefined) + set __backing_conVar5(__backing_conVar5: (IConsumeDecoratedVariable> | undefined)) + + get __backing_conVar5(): (IConsumeDecoratedVariable> | undefined) + set conVar6(conVar6: (Array | undefined)) + + get conVar6(): (Array | undefined) + set __backing_conVar6(__backing_conVar6: (IConsumeDecoratedVariable> | undefined)) + + get __backing_conVar6(): (IConsumeDecoratedVariable> | undefined) + set conVar7(conVar7: (Array | undefined)) + + get conVar7(): (Array | undefined) + set __backing_conVar7(__backing_conVar7: (IConsumeDecoratedVariable> | undefined)) + + get __backing_conVar7(): (IConsumeDecoratedVariable> | undefined) + set conVar8(conVar8: (((sr: string)=> void) | undefined)) + + get conVar8(): (((sr: string)=> void) | undefined) + set __backing_conVar8(__backing_conVar8: (IConsumeDecoratedVariable<((sr: string)=> void)> | undefined)) + + get __backing_conVar8(): (IConsumeDecoratedVariable<((sr: string)=> void)> | undefined) + set conVar9(conVar9: (Date | undefined)) + + get conVar9(): (Date | undefined) + set __backing_conVar9(__backing_conVar9: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar9(): (IConsumeDecoratedVariable | undefined) + set conVar10(conVar10: (Map | undefined)) + + get conVar10(): (Map | undefined) + set __backing_conVar10(__backing_conVar10: (IConsumeDecoratedVariable> | undefined)) + + get __backing_conVar10(): (IConsumeDecoratedVariable> | undefined) + set conVar11(conVar11: ((string | number) | undefined)) + + get conVar11(): ((string | number) | undefined) + set __backing_conVar11(__backing_conVar11: (IConsumeDecoratedVariable<(string | number)> | undefined)) + + get __backing_conVar11(): (IConsumeDecoratedVariable<(string | number)> | undefined) + set conVar12(conVar12: ((Set | Per) | undefined)) + + get conVar12(): ((Set | Per) | undefined) + set __backing_conVar12(__backing_conVar12: (IConsumeDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_conVar12(): (IConsumeDecoratedVariable<(Set | Per)> | undefined) + set conVar13(conVar13: ((Set | null) | undefined)) + + get conVar13(): ((Set | null) | undefined) + set __backing_conVar13(__backing_conVar13: (IConsumeDecoratedVariable<(Set | null)> | undefined)) + + get __backing_conVar13(): (IConsumeDecoratedVariable<(Set | null)> | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test complex type @Consume decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:struct-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-annotation-usage.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-annotation-usage.test.ts index 9f5787762..195bb2726 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-annotation-usage.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-annotation-usage.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,129 +38,179 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { ProvideDecoratedVariable as ProvideDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { Provide as Provide } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class Ancestors extends CustomComponent { - public __initializeStruct(initializers: __Options_Ancestors | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_count = this.addProvidedVar("count", "count", ((({let gensym___58710805 = initializers; +@Component() final struct Ancestors extends CustomComponent { + public __initializeStruct(initializers: (__Options_Ancestors | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_count = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count", "count", ((({let gensym___58710805 = initializers; (((gensym___58710805) == (null)) ? undefined : gensym___58710805.count)})) ?? ("Child0")), false); - this.__backing_count1 = this.addProvidedVar("count1", "prov1", ((({let gensym___84874570 = initializers; + this.__backing_count1 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count1", "prov1", ((({let gensym___84874570 = initializers; (((gensym___84874570) == (null)) ? undefined : gensym___84874570.count1)})) ?? ("Child1")), false); - this.__backing_count2 = this.addProvidedVar("count2", "prov2", ((({let gensym___124037738 = initializers; + this.__backing_count2 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count2", "prov2", ((({let gensym___124037738 = initializers; (((gensym___124037738) == (null)) ? undefined : gensym___124037738.count2)})) ?? ("Child2")), false); - this.__backing_count3 = this.addProvidedVar("count3", "prov3", ((({let gensym___199202238 = initializers; + this.__backing_count3 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count3", "prov3", ((({let gensym___199202238 = initializers; (((gensym___199202238) == (null)) ? undefined : gensym___199202238.count3)})) ?? ("Child3")), true); - this.__backing_count4 = this.addProvidedVar("count4", "count4", ((({let gensym___4359740 = initializers; + this.__backing_count4 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count4", "count4", ((({let gensym___4359740 = initializers; (((gensym___4359740) == (null)) ? undefined : gensym___4359740.count4)})) ?? ("Child4")), false); - this.__backing_count5 = this.addProvidedVar("count5", "count5", ((({let gensym___208755050 = initializers; + this.__backing_count5 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count5", "count5", ((({let gensym___208755050 = initializers; (((gensym___208755050) == (null)) ? undefined : gensym___208755050.count5)})) ?? ("Child5")), true); - this.__backing_count6 = this.addProvidedVar("count6", "", ((({let gensym___37571585 = initializers; + this.__backing_count6 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count6", "", ((({let gensym___37571585 = initializers; (((gensym___37571585) == (null)) ? undefined : gensym___37571585.count6)})) ?? ("Child6")), true); - this.__backing_count7 = this.addProvidedVar("count7", "", ((({let gensym___2162781 = initializers; + this.__backing_count7 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count7", "", ((({let gensym___2162781 = initializers; (((gensym___2162781) == (null)) ? undefined : gensym___2162781.count7)})) ?? ("Child7")), false); } - public __updateStruct(initializers: __Options_Ancestors | undefined): void {} - private __backing_count?: ProvideDecoratedVariable; - public get count(): string | undefined { + + public __updateStruct(initializers: (__Options_Ancestors | undefined)): void {} + + private __backing_count?: IProvideDecoratedVariable<(string | undefined)>; + + public get count(): (string | undefined) { return this.__backing_count!.get(); } - public set count(value: string | undefined) { + + public set count(value: (string | undefined)) { this.__backing_count!.set(value); } - private __backing_count1?: ProvideDecoratedVariable; - public get count1(): string | undefined { + + private __backing_count1?: IProvideDecoratedVariable<(string | undefined)>; + + public get count1(): (string | undefined) { return this.__backing_count1!.get(); } - public set count1(value: string | undefined) { + + public set count1(value: (string | undefined)) { this.__backing_count1!.set(value); } - private __backing_count2?: ProvideDecoratedVariable; - public get count2(): string | undefined { + + private __backing_count2?: IProvideDecoratedVariable<(string | undefined)>; + + public get count2(): (string | undefined) { return this.__backing_count2!.get(); } - public set count2(value: string | undefined) { + + public set count2(value: (string | undefined)) { this.__backing_count2!.set(value); } - private __backing_count3?: ProvideDecoratedVariable; - public get count3(): string | undefined { + + private __backing_count3?: IProvideDecoratedVariable<(string | undefined)>; + + public get count3(): (string | undefined) { return this.__backing_count3!.get(); } - public set count3(value: string | undefined) { + + public set count3(value: (string | undefined)) { this.__backing_count3!.set(value); } - private __backing_count4?: ProvideDecoratedVariable; - public get count4(): string | undefined { + + private __backing_count4?: IProvideDecoratedVariable<(string | undefined)>; + + public get count4(): (string | undefined) { return this.__backing_count4!.get(); } - public set count4(value: string | undefined) { + + public set count4(value: (string | undefined)) { this.__backing_count4!.set(value); } - private __backing_count5?: ProvideDecoratedVariable; - public get count5(): string | undefined { + + private __backing_count5?: IProvideDecoratedVariable<(string | undefined)>; + + public get count5(): (string | undefined) { return this.__backing_count5!.get(); } - public set count5(value: string | undefined) { + + public set count5(value: (string | undefined)) { this.__backing_count5!.set(value); } - private __backing_count6?: ProvideDecoratedVariable; - public get count6(): string | undefined { + + private __backing_count6?: IProvideDecoratedVariable<(string | undefined)>; + + public get count6(): (string | undefined) { return this.__backing_count6!.get(); } - public set count6(value: string | undefined) { + + public set count6(value: (string | undefined)) { this.__backing_count6!.set(value); } - private __backing_count7?: ProvideDecoratedVariable; - public get count7(): string | undefined { + + private __backing_count7?: IProvideDecoratedVariable<(string | undefined)>; + + public get count7(): (string | undefined) { return this.__backing_count7!.get(); } - public set count7(value: string | undefined) { + + public set count7(value: (string | undefined)) { this.__backing_count7!.set(value); } - @memo() public _build(@memo() style: ((instance: Ancestors)=> Ancestors) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Ancestors | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_Ancestors { - set count(count: string | undefined | undefined) - get count(): string | undefined | undefined - set __backing_count(__backing_count: ProvideDecoratedVariable | undefined) - get __backing_count(): ProvideDecoratedVariable | undefined - set count1(count1: string | undefined | undefined) - get count1(): string | undefined | undefined - set __backing_count1(__backing_count1: ProvideDecoratedVariable | undefined) - get __backing_count1(): ProvideDecoratedVariable | undefined - set count2(count2: string | undefined | undefined) - get count2(): string | undefined | undefined - set __backing_count2(__backing_count2: ProvideDecoratedVariable | undefined) - get __backing_count2(): ProvideDecoratedVariable | undefined - set count3(count3: string | undefined | undefined) - get count3(): string | undefined | undefined - set __backing_count3(__backing_count3: ProvideDecoratedVariable | undefined) - get __backing_count3(): ProvideDecoratedVariable | undefined - set count4(count4: string | undefined | undefined) - get count4(): string | undefined | undefined - set __backing_count4(__backing_count4: ProvideDecoratedVariable | undefined) - get __backing_count4(): ProvideDecoratedVariable | undefined - set count5(count5: string | undefined | undefined) - get count5(): string | undefined | undefined - set __backing_count5(__backing_count5: ProvideDecoratedVariable | undefined) - get __backing_count5(): ProvideDecoratedVariable | undefined - set count6(count6: string | undefined | undefined) - get count6(): string | undefined | undefined - set __backing_count6(__backing_count6: ProvideDecoratedVariable | undefined) - get __backing_count6(): ProvideDecoratedVariable | undefined - set count7(count7: string | undefined | undefined) - get count7(): string | undefined | undefined - set __backing_count7(__backing_count7: ProvideDecoratedVariable | undefined) - get __backing_count7(): ProvideDecoratedVariable | undefined +@Component() export interface __Options_Ancestors { + set count(count: ((string | undefined) | undefined)) + + get count(): ((string | undefined) | undefined) + set __backing_count(__backing_count: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + set count1(count1: ((string | undefined) | undefined)) + + get count1(): ((string | undefined) | undefined) + set __backing_count1(__backing_count1: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count1(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + set count2(count2: ((string | undefined) | undefined)) + + get count2(): ((string | undefined) | undefined) + set __backing_count2(__backing_count2: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count2(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + set count3(count3: ((string | undefined) | undefined)) + + get count3(): ((string | undefined) | undefined) + set __backing_count3(__backing_count3: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count3(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + set count4(count4: ((string | undefined) | undefined)) + + get count4(): ((string | undefined) | undefined) + set __backing_count4(__backing_count4: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count4(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + set count5(count5: ((string | undefined) | undefined)) + + get count5(): ((string | undefined) | undefined) + set __backing_count5(__backing_count5: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count5(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + set count6(count6: ((string | undefined) | undefined)) + + get count6(): ((string | undefined) | undefined) + set __backing_count6(__backing_count6: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count6(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + set count7(count7: ((string | undefined) | undefined)) + + get count7(): ((string | undefined) | undefined) + set __backing_count7(__backing_count7: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count7(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + } `; @@ -169,9 +220,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test different @Provide annotation usage transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-basic-type.test.ts index dffbb429f..f0fefa008 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-basic-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-basic-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,90 +38,125 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { ProvideDecoratedVariable as ProvideDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { Provide as Provide } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class PropParent extends CustomComponent { - public __initializeStruct(initializers: __Options_PropParent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_provideVar1 = this.addProvidedVar("provideVar1", "provideVar1", ((({let gensym___181030638 = initializers; +@Component() final struct PropParent extends CustomComponent { + public __initializeStruct(initializers: (__Options_PropParent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_provideVar1 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar1", "provideVar1", ((({let gensym___181030638 = initializers; (((gensym___181030638) == (null)) ? undefined : gensym___181030638.provideVar1)})) ?? ("propVar1")), false); - this.__backing_provideVar2 = this.addProvidedVar("provideVar2", "provideVar2", ((({let gensym___143944235 = initializers; + this.__backing_provideVar2 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar2", "provideVar2", ((({let gensym___143944235 = initializers; (((gensym___143944235) == (null)) ? undefined : gensym___143944235.provideVar2)})) ?? (50)), false); - this.__backing_provideVar3 = this.addProvidedVar("provideVar3", "provideVar3", ((({let gensym___262195977 = initializers; + this.__backing_provideVar3 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar3", "provideVar3", ((({let gensym___262195977 = initializers; (((gensym___262195977) == (null)) ? undefined : gensym___262195977.provideVar3)})) ?? (true)), false); - this.__backing_provideVar4 = this.addProvidedVar("provideVar4", "provideVar4", ((({let gensym___85711435 = initializers; + this.__backing_provideVar4 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar4", "provideVar4", ((({let gensym___85711435 = initializers; (((gensym___85711435) == (null)) ? undefined : gensym___85711435.provideVar4)})) ?? (undefined)), false); - this.__backing_provideVar5 = this.addProvidedVar("provideVar5", "provideVar5", ((({let gensym___139253630 = initializers; + this.__backing_provideVar5 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar5", "provideVar5", ((({let gensym___139253630 = initializers; (((gensym___139253630) == (null)) ? undefined : gensym___139253630.provideVar5)})) ?? (null)), false); } - public __updateStruct(initializers: __Options_PropParent | undefined): void {} - private __backing_provideVar1?: ProvideDecoratedVariable; + + public __updateStruct(initializers: (__Options_PropParent | undefined)): void {} + + private __backing_provideVar1?: IProvideDecoratedVariable; + public get provideVar1(): string { return this.__backing_provideVar1!.get(); } + public set provideVar1(value: string) { this.__backing_provideVar1!.set(value); } - private __backing_provideVar2?: ProvideDecoratedVariable; + + private __backing_provideVar2?: IProvideDecoratedVariable; + public get provideVar2(): number { return this.__backing_provideVar2!.get(); } + public set provideVar2(value: number) { this.__backing_provideVar2!.set(value); } - private __backing_provideVar3?: ProvideDecoratedVariable; + + private __backing_provideVar3?: IProvideDecoratedVariable; + public get provideVar3(): boolean { return this.__backing_provideVar3!.get(); } + public set provideVar3(value: boolean) { this.__backing_provideVar3!.set(value); } - private __backing_provideVar4?: ProvideDecoratedVariable; + + private __backing_provideVar4?: IProvideDecoratedVariable; + public get provideVar4(): undefined { return this.__backing_provideVar4!.get(); } + public set provideVar4(value: undefined) { this.__backing_provideVar4!.set(value); } - private __backing_provideVar5?: ProvideDecoratedVariable; + + private __backing_provideVar5?: IProvideDecoratedVariable; + public get provideVar5(): null { return this.__backing_provideVar5!.get(); } + public set provideVar5(value: null) { this.__backing_provideVar5!.set(value); } - @memo() public _build(@memo() style: ((instance: PropParent)=> PropParent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_PropParent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_PropParent { - set provideVar1(provideVar1: string | undefined) - get provideVar1(): string | undefined - set __backing_provideVar1(__backing_provideVar1: ProvideDecoratedVariable | undefined) - get __backing_provideVar1(): ProvideDecoratedVariable | undefined - set provideVar2(provideVar2: number | undefined) - get provideVar2(): number | undefined - set __backing_provideVar2(__backing_provideVar2: ProvideDecoratedVariable | undefined) - get __backing_provideVar2(): ProvideDecoratedVariable | undefined - set provideVar3(provideVar3: boolean | undefined) - get provideVar3(): boolean | undefined - set __backing_provideVar3(__backing_provideVar3: ProvideDecoratedVariable | undefined) - get __backing_provideVar3(): ProvideDecoratedVariable | undefined - set provideVar4(provideVar4: undefined | undefined) - get provideVar4(): undefined | undefined - set __backing_provideVar4(__backing_provideVar4: ProvideDecoratedVariable | undefined) - get __backing_provideVar4(): ProvideDecoratedVariable | undefined - set provideVar5(provideVar5: null | undefined) - get provideVar5(): null | undefined - set __backing_provideVar5(__backing_provideVar5: ProvideDecoratedVariable | undefined) - get __backing_provideVar5(): ProvideDecoratedVariable | undefined +@Component() export interface __Options_PropParent { + set provideVar1(provideVar1: (string | undefined)) + + get provideVar1(): (string | undefined) + set __backing_provideVar1(__backing_provideVar1: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar1(): (IProvideDecoratedVariable | undefined) + set provideVar2(provideVar2: (number | undefined)) + + get provideVar2(): (number | undefined) + set __backing_provideVar2(__backing_provideVar2: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar2(): (IProvideDecoratedVariable | undefined) + set provideVar3(provideVar3: (boolean | undefined)) + + get provideVar3(): (boolean | undefined) + set __backing_provideVar3(__backing_provideVar3: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar3(): (IProvideDecoratedVariable | undefined) + set provideVar4(provideVar4: (undefined | undefined)) + + get provideVar4(): (undefined | undefined) + set __backing_provideVar4(__backing_provideVar4: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar4(): (IProvideDecoratedVariable | undefined) + set provideVar5(provideVar5: (null | undefined)) + + get provideVar5(): (null | undefined) + set __backing_provideVar5(__backing_provideVar5: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar5(): (IProvideDecoratedVariable | undefined) + } `; @@ -130,9 +166,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test basic type @Provide decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-complex-type.test.ts index 6684a83fe..15967cd3b 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-complex-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,40 +38,58 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { ProvideDecoratedVariable as ProvideDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { Provide as Provide } from "@ohos.arkui.stateManagement"; function main() {} class Per { public num: number; + public constructor(num: number) { this.num = num; } + } final class PropType extends BaseEnum { private readonly #ordinal: int; + private static () {} + public constructor(ordinal: int, value: int) { super(value); this.#ordinal = ordinal; } + public static readonly TYPE1: PropType = new PropType(0, 0); + public static readonly TYPE2: PropType = new PropType(1, 1); + public static readonly TYPE3: PropType = new PropType(2, 3); + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + private static readonly #ValuesArray: int[] = [0, 1, 3]; + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + private static readonly #ItemsArray: PropType[] = [PropType.TYPE1, PropType.TYPE2, PropType.TYPE3]; + public getName(): String { return PropType.#NamesArray[this.#ordinal]; } + public static getValueOf(name: String): PropType { for (let i = 0;((i) < (PropType.#NamesArray.length));(++i)) { if (((name) == (PropType.#NamesArray[i]))) { @@ -79,6 +98,7 @@ final class PropType extends BaseEnum { } throw new Error((("No enum constant PropType.") + (name))); } + public static fromValue(value: int): PropType { for (let i = 0;((i) < (PropType.#ValuesArray.length));(++i)) { if (((value) == (PropType.#ValuesArray[i]))) { @@ -87,188 +107,259 @@ final class PropType extends BaseEnum { } throw new Error((("No enum PropType with value ") + (value))); } + public valueOf(): int { return PropType.#ValuesArray[this.#ordinal]; } + public toString(): String { return PropType.#StringValuesArray[this.#ordinal]; } + public static values(): PropType[] { return PropType.#ItemsArray; } + public getOrdinal(): int { return this.#ordinal; } + public static $_get(e: PropType): String { return e.getName(); } + } -@Component({freezeWhenInactive:false}) final class Parent extends CustomComponent { - public __initializeStruct(initializers: __Options_Parent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_provideVar1 = this.addProvidedVar("provideVar1", "provideVar1", ((({let gensym___181030638 = initializers; +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_provideVar1 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar1", "provideVar1", ((({let gensym___181030638 = initializers; (((gensym___181030638) == (null)) ? undefined : gensym___181030638.provideVar1)})) ?? (new Per(6))), false); - this.__backing_provideVar2 = this.addProvidedVar>("provideVar2", "provideVar2", ((({let gensym___143944235 = initializers; + this.__backing_provideVar2 = STATE_MGMT_FACTORY.makeProvide>(this, "provideVar2", "provideVar2", ((({let gensym___143944235 = initializers; (((gensym___143944235) == (null)) ? undefined : gensym___143944235.provideVar2)})) ?? (new Array(3, 6, 8))), false); - this.__backing_provideVar3 = this.addProvidedVar("provideVar3", "provideVar3", ((({let gensym___262195977 = initializers; + this.__backing_provideVar3 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar3", "provideVar3", ((({let gensym___262195977 = initializers; (((gensym___262195977) == (null)) ? undefined : gensym___262195977.provideVar3)})) ?? (PropType.TYPE3)), false); - this.__backing_provideVar4 = this.addProvidedVar>("provideVar4", "provideVar4", ((({let gensym___85711435 = initializers; + this.__backing_provideVar4 = STATE_MGMT_FACTORY.makeProvide>(this, "provideVar4", "provideVar4", ((({let gensym___85711435 = initializers; (((gensym___85711435) == (null)) ? undefined : gensym___85711435.provideVar4)})) ?? (new Set(new Array("aa", "bb")))), false); - this.__backing_provideVar5 = this.addProvidedVar>("provideVar5", "provideVar5", ((({let gensym___139253630 = initializers; + this.__backing_provideVar5 = STATE_MGMT_FACTORY.makeProvide>(this, "provideVar5", "provideVar5", ((({let gensym___139253630 = initializers; (((gensym___139253630) == (null)) ? undefined : gensym___139253630.provideVar5)})) ?? ([true, false])), false); - this.__backing_provideVar6 = this.addProvidedVar>("provideVar6", "provideVar6", ((({let gensym___146872112 = initializers; + this.__backing_provideVar6 = STATE_MGMT_FACTORY.makeProvide>(this, "provideVar6", "provideVar6", ((({let gensym___146872112 = initializers; (((gensym___146872112) == (null)) ? undefined : gensym___146872112.provideVar6)})) ?? (new Array(new Per(7), new Per(11)))), false); - this.__backing_provideVar7 = this.addProvidedVar>("provideVar7", "provideVar7", ((({let gensym___174412117 = initializers; + this.__backing_provideVar7 = STATE_MGMT_FACTORY.makeProvide>(this, "provideVar7", "provideVar7", ((({let gensym___174412117 = initializers; (((gensym___174412117) == (null)) ? undefined : gensym___174412117.provideVar7)})) ?? ([new Per(7), new Per(11)])), false); - this.__backing_provideVar8 = this.addProvidedVar<((sr: string)=> void)>("provideVar8", "provideVar8", ((({let gensym___253467853 = initializers; + this.__backing_provideVar8 = STATE_MGMT_FACTORY.makeProvide<((sr: string)=> void)>(this, "provideVar8", "provideVar8", ((({let gensym___253467853 = initializers; (((gensym___253467853) == (null)) ? undefined : gensym___253467853.provideVar8)})) ?? (((sr: string) => {}))), false); - this.__backing_provideVar9 = this.addProvidedVar("provideVar9", "provideVar9", ((({let gensym___179115605 = initializers; + this.__backing_provideVar9 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar9", "provideVar9", ((({let gensym___179115605 = initializers; (((gensym___179115605) == (null)) ? undefined : gensym___179115605.provideVar9)})) ?? (new Date("2025-4-23"))), false); - this.__backing_provideVar10 = this.addProvidedVar>("provideVar10", "provideVar10", ((({let gensym___209671248 = initializers; + this.__backing_provideVar10 = STATE_MGMT_FACTORY.makeProvide>(this, "provideVar10", "provideVar10", ((({let gensym___209671248 = initializers; (((gensym___209671248) == (null)) ? undefined : gensym___209671248.provideVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]]))), false); - this.__backing_provideVar11 = this.addProvidedVar("provideVar11", "provideVar11", ((({let gensym___150211849 = initializers; + this.__backing_provideVar11 = STATE_MGMT_FACTORY.makeProvide<(string | number)>(this, "provideVar11", "provideVar11", ((({let gensym___150211849 = initializers; (((gensym___150211849) == (null)) ? undefined : gensym___150211849.provideVar11)})) ?? (0.0)), false); - this.__backing_provideVar12 = this.addProvidedVar | Per>("provideVar12", "provideVar12", ((({let gensym___256025818 = initializers; + this.__backing_provideVar12 = STATE_MGMT_FACTORY.makeProvide<(Set | Per)>(this, "provideVar12", "provideVar12", ((({let gensym___256025818 = initializers; (((gensym___256025818) == (null)) ? undefined : gensym___256025818.provideVar12)})) ?? (new Per(6))), false); } - public __updateStruct(initializers: __Options_Parent | undefined): void {} - private __backing_provideVar1?: ProvideDecoratedVariable; + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_provideVar1?: IProvideDecoratedVariable; + public get provideVar1(): Per { return this.__backing_provideVar1!.get(); } + public set provideVar1(value: Per) { this.__backing_provideVar1!.set(value); } - private __backing_provideVar2?: ProvideDecoratedVariable>; + + private __backing_provideVar2?: IProvideDecoratedVariable>; + public get provideVar2(): Array { return this.__backing_provideVar2!.get(); } + public set provideVar2(value: Array) { this.__backing_provideVar2!.set(value); } - private __backing_provideVar3?: ProvideDecoratedVariable; + + private __backing_provideVar3?: IProvideDecoratedVariable; + public get provideVar3(): PropType { return this.__backing_provideVar3!.get(); } + public set provideVar3(value: PropType) { this.__backing_provideVar3!.set(value); } - private __backing_provideVar4?: ProvideDecoratedVariable>; + + private __backing_provideVar4?: IProvideDecoratedVariable>; + public get provideVar4(): Set { return this.__backing_provideVar4!.get(); } + public set provideVar4(value: Set) { this.__backing_provideVar4!.set(value); } - private __backing_provideVar5?: ProvideDecoratedVariable>; + + private __backing_provideVar5?: IProvideDecoratedVariable>; + public get provideVar5(): Array { return this.__backing_provideVar5!.get(); } + public set provideVar5(value: Array) { this.__backing_provideVar5!.set(value); } - private __backing_provideVar6?: ProvideDecoratedVariable>; + + private __backing_provideVar6?: IProvideDecoratedVariable>; + public get provideVar6(): Array { return this.__backing_provideVar6!.get(); } + public set provideVar6(value: Array) { this.__backing_provideVar6!.set(value); } - private __backing_provideVar7?: ProvideDecoratedVariable>; + + private __backing_provideVar7?: IProvideDecoratedVariable>; + public get provideVar7(): Array { return this.__backing_provideVar7!.get(); } + public set provideVar7(value: Array) { this.__backing_provideVar7!.set(value); } - private __backing_provideVar8?: ProvideDecoratedVariable<((sr: string)=> void)>; + + private __backing_provideVar8?: IProvideDecoratedVariable<((sr: string)=> void)>; + public get provideVar8(): ((sr: string)=> void) { return this.__backing_provideVar8!.get(); } + public set provideVar8(value: ((sr: string)=> void)) { this.__backing_provideVar8!.set(value); } - private __backing_provideVar9?: ProvideDecoratedVariable; + + private __backing_provideVar9?: IProvideDecoratedVariable; + public get provideVar9(): Date { return this.__backing_provideVar9!.get(); } + public set provideVar9(value: Date) { this.__backing_provideVar9!.set(value); } - private __backing_provideVar10?: ProvideDecoratedVariable>; + + private __backing_provideVar10?: IProvideDecoratedVariable>; + public get provideVar10(): Map { return this.__backing_provideVar10!.get(); } + public set provideVar10(value: Map) { this.__backing_provideVar10!.set(value); } - private __backing_provideVar11?: ProvideDecoratedVariable; - public get provideVar11(): string | number { + + private __backing_provideVar11?: IProvideDecoratedVariable<(string | number)>; + + public get provideVar11(): (string | number) { return this.__backing_provideVar11!.get(); } - public set provideVar11(value: string | number) { + + public set provideVar11(value: (string | number)) { this.__backing_provideVar11!.set(value); } - private __backing_provideVar12?: ProvideDecoratedVariable | Per>; - public get provideVar12(): Set | Per { + + private __backing_provideVar12?: IProvideDecoratedVariable<(Set | Per)>; + + public get provideVar12(): (Set | Per) { return this.__backing_provideVar12!.get(); } - public set provideVar12(value: Set | Per) { + + public set provideVar12(value: (Set | Per)) { this.__backing_provideVar12!.set(value); } - @memo() public _build(@memo() style: ((instance: Parent)=> Parent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_Parent { - set provideVar1(provideVar1: Per | undefined) - get provideVar1(): Per | undefined - set __backing_provideVar1(__backing_provideVar1: ProvideDecoratedVariable | undefined) - get __backing_provideVar1(): ProvideDecoratedVariable | undefined - set provideVar2(provideVar2: Array | undefined) - get provideVar2(): Array | undefined - set __backing_provideVar2(__backing_provideVar2: ProvideDecoratedVariable> | undefined) - get __backing_provideVar2(): ProvideDecoratedVariable> | undefined - set provideVar3(provideVar3: PropType | undefined) - get provideVar3(): PropType | undefined - set __backing_provideVar3(__backing_provideVar3: ProvideDecoratedVariable | undefined) - get __backing_provideVar3(): ProvideDecoratedVariable | undefined - set provideVar4(provideVar4: Set | undefined) - get provideVar4(): Set | undefined - set __backing_provideVar4(__backing_provideVar4: ProvideDecoratedVariable> | undefined) - get __backing_provideVar4(): ProvideDecoratedVariable> | undefined - set provideVar5(provideVar5: Array | undefined) - get provideVar5(): Array | undefined - set __backing_provideVar5(__backing_provideVar5: ProvideDecoratedVariable> | undefined) - get __backing_provideVar5(): ProvideDecoratedVariable> | undefined - set provideVar6(provideVar6: Array | undefined) - get provideVar6(): Array | undefined - set __backing_provideVar6(__backing_provideVar6: ProvideDecoratedVariable> | undefined) - get __backing_provideVar6(): ProvideDecoratedVariable> | undefined - set provideVar7(provideVar7: Array | undefined) - get provideVar7(): Array | undefined - set __backing_provideVar7(__backing_provideVar7: ProvideDecoratedVariable> | undefined) - get __backing_provideVar7(): ProvideDecoratedVariable> | undefined - set provideVar8(provideVar8: ((sr: string)=> void) | undefined) - get provideVar8(): ((sr: string)=> void) | undefined - set __backing_provideVar8(__backing_provideVar8: ProvideDecoratedVariable<((sr: string)=> void)> | undefined) - get __backing_provideVar8(): ProvideDecoratedVariable<((sr: string)=> void)> | undefined - set provideVar9(provideVar9: Date | undefined) - get provideVar9(): Date | undefined - set __backing_provideVar9(__backing_provideVar9: ProvideDecoratedVariable | undefined) - get __backing_provideVar9(): ProvideDecoratedVariable | undefined - set provideVar10(provideVar10: Map | undefined) - get provideVar10(): Map | undefined - set __backing_provideVar10(__backing_provideVar10: ProvideDecoratedVariable> | undefined) - get __backing_provideVar10(): ProvideDecoratedVariable> | undefined - set provideVar11(provideVar11: string | number | undefined) - get provideVar11(): string | number | undefined - set __backing_provideVar11(__backing_provideVar11: ProvideDecoratedVariable | undefined) - get __backing_provideVar11(): ProvideDecoratedVariable | undefined - set provideVar12(provideVar12: Set | Per | undefined) - get provideVar12(): Set | Per | undefined - set __backing_provideVar12(__backing_provideVar12: ProvideDecoratedVariable | Per> | undefined) - get __backing_provideVar12(): ProvideDecoratedVariable | Per> | undefined +@Component() export interface __Options_Parent { + set provideVar1(provideVar1: (Per | undefined)) + + get provideVar1(): (Per | undefined) + set __backing_provideVar1(__backing_provideVar1: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar1(): (IProvideDecoratedVariable | undefined) + set provideVar2(provideVar2: (Array | undefined)) + + get provideVar2(): (Array | undefined) + set __backing_provideVar2(__backing_provideVar2: (IProvideDecoratedVariable> | undefined)) + + get __backing_provideVar2(): (IProvideDecoratedVariable> | undefined) + set provideVar3(provideVar3: (PropType | undefined)) + + get provideVar3(): (PropType | undefined) + set __backing_provideVar3(__backing_provideVar3: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar3(): (IProvideDecoratedVariable | undefined) + set provideVar4(provideVar4: (Set | undefined)) + + get provideVar4(): (Set | undefined) + set __backing_provideVar4(__backing_provideVar4: (IProvideDecoratedVariable> | undefined)) + + get __backing_provideVar4(): (IProvideDecoratedVariable> | undefined) + set provideVar5(provideVar5: (Array | undefined)) + + get provideVar5(): (Array | undefined) + set __backing_provideVar5(__backing_provideVar5: (IProvideDecoratedVariable> | undefined)) + + get __backing_provideVar5(): (IProvideDecoratedVariable> | undefined) + set provideVar6(provideVar6: (Array | undefined)) + + get provideVar6(): (Array | undefined) + set __backing_provideVar6(__backing_provideVar6: (IProvideDecoratedVariable> | undefined)) + + get __backing_provideVar6(): (IProvideDecoratedVariable> | undefined) + set provideVar7(provideVar7: (Array | undefined)) + + get provideVar7(): (Array | undefined) + set __backing_provideVar7(__backing_provideVar7: (IProvideDecoratedVariable> | undefined)) + + get __backing_provideVar7(): (IProvideDecoratedVariable> | undefined) + set provideVar8(provideVar8: (((sr: string)=> void) | undefined)) + + get provideVar8(): (((sr: string)=> void) | undefined) + set __backing_provideVar8(__backing_provideVar8: (IProvideDecoratedVariable<((sr: string)=> void)> | undefined)) + + get __backing_provideVar8(): (IProvideDecoratedVariable<((sr: string)=> void)> | undefined) + set provideVar9(provideVar9: (Date | undefined)) + + get provideVar9(): (Date | undefined) + set __backing_provideVar9(__backing_provideVar9: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar9(): (IProvideDecoratedVariable | undefined) + set provideVar10(provideVar10: (Map | undefined)) + + get provideVar10(): (Map | undefined) + set __backing_provideVar10(__backing_provideVar10: (IProvideDecoratedVariable> | undefined)) + + get __backing_provideVar10(): (IProvideDecoratedVariable> | undefined) + set provideVar11(provideVar11: ((string | number) | undefined)) + + get provideVar11(): ((string | number) | undefined) + set __backing_provideVar11(__backing_provideVar11: (IProvideDecoratedVariable<(string | number)> | undefined)) + + get __backing_provideVar11(): (IProvideDecoratedVariable<(string | number)> | undefined) + set provideVar12(provideVar12: ((Set | Per) | undefined)) + + get provideVar12(): ((Set | Per) | undefined) + set __backing_provideVar12(__backing_provideVar12: (IProvideDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_provideVar12(): (IProvideDecoratedVariable<(Set | Per)> | undefined) + } `; @@ -278,9 +369,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test complex type @Provide decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-to-consume.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-to-consume.test.ts new file mode 100644 index 000000000..3f01659b4 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-to-consume.test.ts @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provide-and-consume'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'provide-to-consume.ets'), +]; + +const pluginTester = new PluginTester('test usage of @Provide and @Consume decorator', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedParsedScript: string = ` +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Column as Column, Text as Text } from "@ohos.arkui.component"; + +import { Consume as Consume, Provide as Provide } from "@ohos.arkui.stateManagement"; + +@Component() final struct Child extends CustomComponent { + @Consume() public num!: number; + + @Consume({value:"ss"}) public str!: string; + + public build() { + Column(){ + Text(\`Child num: \${this.num}\`); + Text(\`Child str: \${this.str}\`); + }; + } + + public constructor() {} + +} + +@Component() final struct Parent extends CustomComponent { + @Provide({alias:"num"}) public num: number = 10; + + @Provide({alias:"ss"}) public str: string = "hello"; + + public build() { + Column(){ + Text(\`Parent num: \${this.num}\`); + Text(\`Parent str: \${this.str}\`); + Child(); + }; + } + + public constructor() {} + +} + +@Component() export interface __Options_Child { + num?: number; + @Consume() __backing_num?: number; + str?: string; + @Consume({value:"ss"}) __backing_str?: string; + +} + +@Component() export interface __Options_Parent { + num?: number; + @Provide({alias:"num"}) __backing_num?: number; + str?: string; + @Provide({alias:"ss"}) __backing_str?: string; + +} +`; + +const expectedCheckedScript: string = ` +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IConsumeDecoratedVariable as IConsumeDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Column as Column, Text as Text } from "@ohos.arkui.component"; + +import { Consume as Consume, Provide as Provide } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = STATE_MGMT_FACTORY.makeConsume(this, "num", "num"); + this.__backing_str = STATE_MGMT_FACTORY.makeConsume(this, "str", "ss"); + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_num?: IConsumeDecoratedVariable; + + public get num(): number { + return this.__backing_num!.get(); + } + + public set num(value: number) { + this.__backing_num!.set(value); + } + + private __backing_str?: IConsumeDecoratedVariable; + + public get str(): string { + return this.__backing_str!.get(); + } + + public set str(value: string) { + this.__backing_str!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, \`Child num: \${this.num}\`, undefined, undefined); + Text(undefined, \`Child str: \${this.str}\`, undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = STATE_MGMT_FACTORY.makeProvide(this, "num", "num", ((({let gensym___83257243 = initializers; + (((gensym___83257243) == (null)) ? undefined : gensym___83257243.num)})) ?? (10)), false); + this.__backing_str = STATE_MGMT_FACTORY.makeProvide(this, "str", "ss", ((({let gensym___249074315 = initializers; + (((gensym___249074315) == (null)) ? undefined : gensym___249074315.str)})) ?? ("hello")), false); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_num?: IProvideDecoratedVariable; + + public get num(): number { + return this.__backing_num!.get(); + } + + public set num(value: number) { + this.__backing_num!.set(value); + } + + private __backing_str?: IProvideDecoratedVariable; + + public get str(): string { + return this.__backing_str!.get(); + } + + public set str(value: string) { + this.__backing_str!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, \`Parent num: \${this.num}\`, undefined, undefined); + Text(undefined, \`Parent str: \${this.str}\`, undefined, undefined); + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), undefined, undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_Child { + set num(num: (number | undefined)) + + get num(): (number | undefined) + set __backing_num(__backing_num: (IConsumeDecoratedVariable | undefined)) + + get __backing_num(): (IConsumeDecoratedVariable | undefined) + set str(str: (string | undefined)) + + get str(): (string | undefined) + set __backing_str(__backing_str: (IConsumeDecoratedVariable | undefined)) + + get __backing_str(): (IConsumeDecoratedVariable | undefined) + +} + +@Component() export interface __Options_Parent { + set num(num: (number | undefined)) + + get num(): (number | undefined) + set __backing_num(__backing_num: (IProvideDecoratedVariable | undefined)) + + get __backing_num(): (IProvideDecoratedVariable | undefined) + set str(str: (string | undefined)) + + get str(): (string | undefined) + set __backing_str(__backing_str: (IProvideDecoratedVariable | undefined)) + + get __backing_str(): (IProvideDecoratedVariable | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test usage of @Provide and @Consume decorator', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/consumer-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/consumer-basic-type.test.ts new file mode 100644 index 000000000..7c4e62f2d --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/consumer-basic-type.test.ts @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provider-and-consumer'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'consumer-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @Consumer decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IConsumerDecoratedVariable as IConsumerDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Consumer as Consumer } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_consumerVar1 = STATE_MGMT_FACTORY.makeConsumer(this, "consumerVar1", "consumerVar1", "propVar1"); + this.__backing_consumerVar2 = STATE_MGMT_FACTORY.makeConsumer(this, "consumerVar2", "consumerVar2", 50); + this.__backing_consumerVar3 = STATE_MGMT_FACTORY.makeConsumer(this, "consumerVar3", "consumerVar3", true); + this.__backing_consumerVar4 = STATE_MGMT_FACTORY.makeConsumer(this, "consumerVar4", "consumerVar4", undefined); + this.__backing_consumerVar5 = STATE_MGMT_FACTORY.makeConsumer(this, "consumerVar5", "consumerVar5", null); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_consumerVar1?: IConsumerDecoratedVariable; + + public get consumerVar1(): string { + return this.__backing_consumerVar1!.get(); + } + + public set consumerVar1(value: string) { + this.__backing_consumerVar1!.set(value); + } + + private __backing_consumerVar2?: IConsumerDecoratedVariable; + + public get consumerVar2(): number { + return this.__backing_consumerVar2!.get(); + } + + public set consumerVar2(value: number) { + this.__backing_consumerVar2!.set(value); + } + + private __backing_consumerVar3?: IConsumerDecoratedVariable; + + public get consumerVar3(): boolean { + return this.__backing_consumerVar3!.get(); + } + + public set consumerVar3(value: boolean) { + this.__backing_consumerVar3!.set(value); + } + + private __backing_consumerVar4?: IConsumerDecoratedVariable; + + public get consumerVar4(): undefined { + return this.__backing_consumerVar4!.get(); + } + + public set consumerVar4(value: undefined) { + this.__backing_consumerVar4!.set(value); + } + + private __backing_consumerVar5?: IConsumerDecoratedVariable; + + public get consumerVar5(): null { + return this.__backing_consumerVar5!.get(); + } + + public set consumerVar5(value: null) { + this.__backing_consumerVar5!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set consumerVar1(consumerVar1: (string | undefined)) + + get consumerVar1(): (string | undefined) + set __backing_consumerVar1(__backing_consumerVar1: (IConsumerDecoratedVariable | undefined)) + + get __backing_consumerVar1(): (IConsumerDecoratedVariable | undefined) + set consumerVar2(consumerVar2: (number | undefined)) + + get consumerVar2(): (number | undefined) + set __backing_consumerVar2(__backing_consumerVar2: (IConsumerDecoratedVariable | undefined)) + + get __backing_consumerVar2(): (IConsumerDecoratedVariable | undefined) + set consumerVar3(consumerVar3: (boolean | undefined)) + + get consumerVar3(): (boolean | undefined) + set __backing_consumerVar3(__backing_consumerVar3: (IConsumerDecoratedVariable | undefined)) + + get __backing_consumerVar3(): (IConsumerDecoratedVariable | undefined) + set consumerVar4(consumerVar4: (undefined | undefined)) + + get consumerVar4(): (undefined | undefined) + set __backing_consumerVar4(__backing_consumerVar4: (IConsumerDecoratedVariable | undefined)) + + get __backing_consumerVar4(): (IConsumerDecoratedVariable | undefined) + set consumerVar5(consumerVar5: (null | undefined)) + + get consumerVar5(): (null | undefined) + set __backing_consumerVar5(__backing_consumerVar5: (IConsumerDecoratedVariable | undefined)) + + get __backing_consumerVar5(): (IConsumerDecoratedVariable | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test basic type @Consumer decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/consumer-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/consumer-complex-type.test.ts new file mode 100644 index 000000000..809d7e3ce --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/consumer-complex-type.test.ts @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provider-and-consumer'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'consumer-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @Consumer decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IConsumerDecoratedVariable as IConsumerDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Consumer as Consumer } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class StateType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: StateType = new StateType(0, 0); + + public static readonly TYPE2: StateType = new StateType(1, 1); + + public static readonly TYPE3: StateType = new StateType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: StateType[] = [StateType.TYPE1, StateType.TYPE2, StateType.TYPE3]; + + public getName(): String { + return StateType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): StateType { + for (let i = 0;((i) < (StateType.#NamesArray.length));(++i)) { + if (((name) == (StateType.#NamesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant StateType.") + (name))); + } + + public static fromValue(value: int): StateType { + for (let i = 0;((i) < (StateType.#ValuesArray.length));(++i)) { + if (((value) == (StateType.#ValuesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum StateType with value ") + (value))); + } + + public valueOf(): int { + return StateType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return StateType.#StringValuesArray[this.#ordinal]; + } + + public static values(): StateType[] { + return StateType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: StateType): String { + return e.getName(); + } + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_paramVar1 = STATE_MGMT_FACTORY.makeConsumer(this, "paramVar1", "paramVar1", new Per(6)); + this.__backing_paramVar2 = STATE_MGMT_FACTORY.makeConsumer>(this, "paramVar2", "paramVar2", new Array(3, 6, 8)); + this.__backing_paramVar3 = STATE_MGMT_FACTORY.makeConsumer(this, "paramVar3", "paramVar3", StateType.TYPE3); + this.__backing_paramVar4 = STATE_MGMT_FACTORY.makeConsumer>(this, "paramVar4", "paramVar4", new Set(new Array("aa", "bb"))); + this.__backing_paramVar5 = STATE_MGMT_FACTORY.makeConsumer>(this, "paramVar5", "paramVar5", [true, false]); + this.__backing_paramVar6 = STATE_MGMT_FACTORY.makeConsumer>(this, "paramVar6", "paramVar6", new Array(new Per(7), new Per(11))); + this.__backing_paramVar7 = STATE_MGMT_FACTORY.makeConsumer>(this, "paramVar7", "paramVar7", [new Per(7), new Per(11)]); + this.__backing_paramVar9 = STATE_MGMT_FACTORY.makeConsumer(this, "paramVar9", "paramVar9", new Date("2025-4-23")); + this.__backing_paramVar10 = STATE_MGMT_FACTORY.makeConsumer>(this, "paramVar10", "paramVar10", new Map([[0, new Per(7)], [1, new Per(10)]])); + this.__backing_paramVar11 = STATE_MGMT_FACTORY.makeConsumer<(string | number)>(this, "paramVar11", "paramVar11", 0.0); + this.__backing_paramVar12 = STATE_MGMT_FACTORY.makeConsumer<(Set | Per)>(this, "paramVar12", "paramVar12", new Per(6)); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_paramVar1?: IConsumerDecoratedVariable; + + public get paramVar1(): Per { + return this.__backing_paramVar1!.get(); + } + + public set paramVar1(value: Per) { + this.__backing_paramVar1!.set(value); + } + + private __backing_paramVar2?: IConsumerDecoratedVariable>; + + public get paramVar2(): Array { + return this.__backing_paramVar2!.get(); + } + + public set paramVar2(value: Array) { + this.__backing_paramVar2!.set(value); + } + + private __backing_paramVar3?: IConsumerDecoratedVariable; + + public get paramVar3(): StateType { + return this.__backing_paramVar3!.get(); + } + + public set paramVar3(value: StateType) { + this.__backing_paramVar3!.set(value); + } + + private __backing_paramVar4?: IConsumerDecoratedVariable>; + + public get paramVar4(): Set { + return this.__backing_paramVar4!.get(); + } + + public set paramVar4(value: Set) { + this.__backing_paramVar4!.set(value); + } + + private __backing_paramVar5?: IConsumerDecoratedVariable>; + + public get paramVar5(): Array { + return this.__backing_paramVar5!.get(); + } + + public set paramVar5(value: Array) { + this.__backing_paramVar5!.set(value); + } + + private __backing_paramVar6?: IConsumerDecoratedVariable>; + + public get paramVar6(): Array { + return this.__backing_paramVar6!.get(); + } + + public set paramVar6(value: Array) { + this.__backing_paramVar6!.set(value); + } + + private __backing_paramVar7?: IConsumerDecoratedVariable>; + + public get paramVar7(): Array { + return this.__backing_paramVar7!.get(); + } + + public set paramVar7(value: Array) { + this.__backing_paramVar7!.set(value); + } + + private __backing_paramVar9?: IConsumerDecoratedVariable; + + public get paramVar9(): Date { + return this.__backing_paramVar9!.get(); + } + + public set paramVar9(value: Date) { + this.__backing_paramVar9!.set(value); + } + + private __backing_paramVar10?: IConsumerDecoratedVariable>; + + public get paramVar10(): Map { + return this.__backing_paramVar10!.get(); + } + + public set paramVar10(value: Map) { + this.__backing_paramVar10!.set(value); + } + + private __backing_paramVar11?: IConsumerDecoratedVariable<(string | number)>; + + public get paramVar11(): (string | number) { + return this.__backing_paramVar11!.get(); + } + + public set paramVar11(value: (string | number)) { + this.__backing_paramVar11!.set(value); + } + + private __backing_paramVar12?: IConsumerDecoratedVariable<(Set | Per)>; + + public get paramVar12(): (Set | Per) { + return this.__backing_paramVar12!.get(); + } + + public set paramVar12(value: (Set | Per)) { + this.__backing_paramVar12!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set paramVar1(paramVar1: (Per | undefined)) + + get paramVar1(): (Per | undefined) + set __backing_paramVar1(__backing_paramVar1: (IConsumerDecoratedVariable | undefined)) + + get __backing_paramVar1(): (IConsumerDecoratedVariable | undefined) + set paramVar2(paramVar2: (Array | undefined)) + + get paramVar2(): (Array | undefined) + set __backing_paramVar2(__backing_paramVar2: (IConsumerDecoratedVariable> | undefined)) + + get __backing_paramVar2(): (IConsumerDecoratedVariable> | undefined) + set paramVar3(paramVar3: (StateType | undefined)) + + get paramVar3(): (StateType | undefined) + set __backing_paramVar3(__backing_paramVar3: (IConsumerDecoratedVariable | undefined)) + + get __backing_paramVar3(): (IConsumerDecoratedVariable | undefined) + set paramVar4(paramVar4: (Set | undefined)) + + get paramVar4(): (Set | undefined) + set __backing_paramVar4(__backing_paramVar4: (IConsumerDecoratedVariable> | undefined)) + + get __backing_paramVar4(): (IConsumerDecoratedVariable> | undefined) + set paramVar5(paramVar5: (Array | undefined)) + + get paramVar5(): (Array | undefined) + set __backing_paramVar5(__backing_paramVar5: (IConsumerDecoratedVariable> | undefined)) + + get __backing_paramVar5(): (IConsumerDecoratedVariable> | undefined) + set paramVar6(paramVar6: (Array | undefined)) + + get paramVar6(): (Array | undefined) + set __backing_paramVar6(__backing_paramVar6: (IConsumerDecoratedVariable> | undefined)) + + get __backing_paramVar6(): (IConsumerDecoratedVariable> | undefined) + set paramVar7(paramVar7: (Array | undefined)) + + get paramVar7(): (Array | undefined) + set __backing_paramVar7(__backing_paramVar7: (IConsumerDecoratedVariable> | undefined)) + + get __backing_paramVar7(): (IConsumerDecoratedVariable> | undefined) + set paramVar9(paramVar9: (Date | undefined)) + + get paramVar9(): (Date | undefined) + set __backing_paramVar9(__backing_paramVar9: (IConsumerDecoratedVariable | undefined)) + + get __backing_paramVar9(): (IConsumerDecoratedVariable | undefined) + set paramVar10(paramVar10: (Map | undefined)) + + get paramVar10(): (Map | undefined) + set __backing_paramVar10(__backing_paramVar10: (IConsumerDecoratedVariable> | undefined)) + + get __backing_paramVar10(): (IConsumerDecoratedVariable> | undefined) + set paramVar11(paramVar11: ((string | number) | undefined)) + + get paramVar11(): ((string | number) | undefined) + set __backing_paramVar11(__backing_paramVar11: (IConsumerDecoratedVariable<(string | number)> | undefined)) + + get __backing_paramVar11(): (IConsumerDecoratedVariable<(string | number)> | undefined) + set paramVar12(paramVar12: ((Set | Per) | undefined)) + + get paramVar12(): ((Set | Per) | undefined) + set __backing_paramVar12(__backing_paramVar12: (IConsumerDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_paramVar12(): (IConsumerDecoratedVariable<(Set | Per)> | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test complex type @Consumer decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-basic-type.test.ts new file mode 100644 index 000000000..9943acaee --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-basic-type.test.ts @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provider-and-consumer'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'provider-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @Provider decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IProviderDecoratedVariable as IProviderDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Provider as Provider } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_providerVar1 = STATE_MGMT_FACTORY.makeProvider(this, "providerVar1", "providerVar1", "propVar1"); + this.__backing_providerVar2 = STATE_MGMT_FACTORY.makeProvider(this, "providerVar2", "providerVar2", 50); + this.__backing_providerVar3 = STATE_MGMT_FACTORY.makeProvider(this, "providerVar3", "providerVar3", true); + this.__backing_providerVar4 = STATE_MGMT_FACTORY.makeProvider(this, "providerVar4", "providerVar4", undefined); + this.__backing_providerVar5 = STATE_MGMT_FACTORY.makeProvider(this, "providerVar5", "providerVar5", null); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_providerVar1?: IProviderDecoratedVariable; + + public get providerVar1(): string { + return this.__backing_providerVar1!.get(); + } + + public set providerVar1(value: string) { + this.__backing_providerVar1!.set(value); + } + + private __backing_providerVar2?: IProviderDecoratedVariable; + + public get providerVar2(): number { + return this.__backing_providerVar2!.get(); + } + + public set providerVar2(value: number) { + this.__backing_providerVar2!.set(value); + } + + private __backing_providerVar3?: IProviderDecoratedVariable; + + public get providerVar3(): boolean { + return this.__backing_providerVar3!.get(); + } + + public set providerVar3(value: boolean) { + this.__backing_providerVar3!.set(value); + } + + private __backing_providerVar4?: IProviderDecoratedVariable; + + public get providerVar4(): undefined { + return this.__backing_providerVar4!.get(); + } + + public set providerVar4(value: undefined) { + this.__backing_providerVar4!.set(value); + } + + private __backing_providerVar5?: IProviderDecoratedVariable; + + public get providerVar5(): null { + return this.__backing_providerVar5!.get(); + } + + public set providerVar5(value: null) { + this.__backing_providerVar5!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set providerVar1(providerVar1: (string | undefined)) + + get providerVar1(): (string | undefined) + set __backing_providerVar1(__backing_providerVar1: (IProviderDecoratedVariable | undefined)) + + get __backing_providerVar1(): (IProviderDecoratedVariable | undefined) + set providerVar2(providerVar2: (number | undefined)) + + get providerVar2(): (number | undefined) + set __backing_providerVar2(__backing_providerVar2: (IProviderDecoratedVariable | undefined)) + + get __backing_providerVar2(): (IProviderDecoratedVariable | undefined) + set providerVar3(providerVar3: (boolean | undefined)) + + get providerVar3(): (boolean | undefined) + set __backing_providerVar3(__backing_providerVar3: (IProviderDecoratedVariable | undefined)) + + get __backing_providerVar3(): (IProviderDecoratedVariable | undefined) + set providerVar4(providerVar4: (undefined | undefined)) + + get providerVar4(): (undefined | undefined) + set __backing_providerVar4(__backing_providerVar4: (IProviderDecoratedVariable | undefined)) + + get __backing_providerVar4(): (IProviderDecoratedVariable | undefined) + set providerVar5(providerVar5: (null | undefined)) + + get providerVar5(): (null | undefined) + set __backing_providerVar5(__backing_providerVar5: (IProviderDecoratedVariable | undefined)) + + get __backing_providerVar5(): (IProviderDecoratedVariable | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test basic type @Provider decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-complex-type.test.ts new file mode 100644 index 000000000..2a953691a --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-complex-type.test.ts @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provider-and-consumer'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'provider-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @Provider decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IProviderDecoratedVariable as IProviderDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Provider as Provider } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class StateType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: StateType = new StateType(0, 0); + + public static readonly TYPE2: StateType = new StateType(1, 1); + + public static readonly TYPE3: StateType = new StateType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: StateType[] = [StateType.TYPE1, StateType.TYPE2, StateType.TYPE3]; + + public getName(): String { + return StateType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): StateType { + for (let i = 0;((i) < (StateType.#NamesArray.length));(++i)) { + if (((name) == (StateType.#NamesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant StateType.") + (name))); + } + + public static fromValue(value: int): StateType { + for (let i = 0;((i) < (StateType.#ValuesArray.length));(++i)) { + if (((value) == (StateType.#ValuesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum StateType with value ") + (value))); + } + + public valueOf(): int { + return StateType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return StateType.#StringValuesArray[this.#ordinal]; + } + + public static values(): StateType[] { + return StateType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: StateType): String { + return e.getName(); + } + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_paramVar1 = STATE_MGMT_FACTORY.makeProvider(this, "paramVar1", "paramVar1", new Per(6)); + this.__backing_paramVar2 = STATE_MGMT_FACTORY.makeProvider>(this, "paramVar2", "paramVar2", new Array(3, 6, 8)); + this.__backing_paramVar3 = STATE_MGMT_FACTORY.makeProvider(this, "paramVar3", "paramVar3", StateType.TYPE3); + this.__backing_paramVar4 = STATE_MGMT_FACTORY.makeProvider>(this, "paramVar4", "paramVar4", new Set(new Array("aa", "bb"))); + this.__backing_paramVar5 = STATE_MGMT_FACTORY.makeProvider>(this, "paramVar5", "paramVar5", [true, false]); + this.__backing_paramVar6 = STATE_MGMT_FACTORY.makeProvider>(this, "paramVar6", "paramVar6", new Array(new Per(7), new Per(11))); + this.__backing_paramVar7 = STATE_MGMT_FACTORY.makeProvider>(this, "paramVar7", "paramVar7", [new Per(7), new Per(11)]); + this.__backing_paramVar9 = STATE_MGMT_FACTORY.makeProvider(this, "paramVar9", "paramVar9", new Date("2025-4-23")); + this.__backing_paramVar10 = STATE_MGMT_FACTORY.makeProvider>(this, "paramVar10", "paramVar10", new Map([[0, new Per(7)], [1, new Per(10)]])); + this.__backing_paramVar11 = STATE_MGMT_FACTORY.makeProvider<(string | number)>(this, "paramVar11", "paramVar11", 0.0); + this.__backing_paramVar12 = STATE_MGMT_FACTORY.makeProvider<(Set | Per)>(this, "paramVar12", "paramVar12", new Per(6)); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_paramVar1?: IProviderDecoratedVariable; + + public get paramVar1(): Per { + return this.__backing_paramVar1!.get(); + } + + public set paramVar1(value: Per) { + this.__backing_paramVar1!.set(value); + } + + private __backing_paramVar2?: IProviderDecoratedVariable>; + + public get paramVar2(): Array { + return this.__backing_paramVar2!.get(); + } + + public set paramVar2(value: Array) { + this.__backing_paramVar2!.set(value); + } + + private __backing_paramVar3?: IProviderDecoratedVariable; + + public get paramVar3(): StateType { + return this.__backing_paramVar3!.get(); + } + + public set paramVar3(value: StateType) { + this.__backing_paramVar3!.set(value); + } + + private __backing_paramVar4?: IProviderDecoratedVariable>; + + public get paramVar4(): Set { + return this.__backing_paramVar4!.get(); + } + + public set paramVar4(value: Set) { + this.__backing_paramVar4!.set(value); + } + + private __backing_paramVar5?: IProviderDecoratedVariable>; + + public get paramVar5(): Array { + return this.__backing_paramVar5!.get(); + } + + public set paramVar5(value: Array) { + this.__backing_paramVar5!.set(value); + } + + private __backing_paramVar6?: IProviderDecoratedVariable>; + + public get paramVar6(): Array { + return this.__backing_paramVar6!.get(); + } + + public set paramVar6(value: Array) { + this.__backing_paramVar6!.set(value); + } + + private __backing_paramVar7?: IProviderDecoratedVariable>; + + public get paramVar7(): Array { + return this.__backing_paramVar7!.get(); + } + + public set paramVar7(value: Array) { + this.__backing_paramVar7!.set(value); + } + + private __backing_paramVar9?: IProviderDecoratedVariable; + + public get paramVar9(): Date { + return this.__backing_paramVar9!.get(); + } + + public set paramVar9(value: Date) { + this.__backing_paramVar9!.set(value); + } + + private __backing_paramVar10?: IProviderDecoratedVariable>; + + public get paramVar10(): Map { + return this.__backing_paramVar10!.get(); + } + + public set paramVar10(value: Map) { + this.__backing_paramVar10!.set(value); + } + + private __backing_paramVar11?: IProviderDecoratedVariable<(string | number)>; + + public get paramVar11(): (string | number) { + return this.__backing_paramVar11!.get(); + } + + public set paramVar11(value: (string | number)) { + this.__backing_paramVar11!.set(value); + } + + private __backing_paramVar12?: IProviderDecoratedVariable<(Set | Per)>; + + public get paramVar12(): (Set | Per) { + return this.__backing_paramVar12!.get(); + } + + public set paramVar12(value: (Set | Per)) { + this.__backing_paramVar12!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set paramVar1(paramVar1: (Per | undefined)) + + get paramVar1(): (Per | undefined) + set __backing_paramVar1(__backing_paramVar1: (IProviderDecoratedVariable | undefined)) + + get __backing_paramVar1(): (IProviderDecoratedVariable | undefined) + set paramVar2(paramVar2: (Array | undefined)) + + get paramVar2(): (Array | undefined) + set __backing_paramVar2(__backing_paramVar2: (IProviderDecoratedVariable> | undefined)) + + get __backing_paramVar2(): (IProviderDecoratedVariable> | undefined) + set paramVar3(paramVar3: (StateType | undefined)) + + get paramVar3(): (StateType | undefined) + set __backing_paramVar3(__backing_paramVar3: (IProviderDecoratedVariable | undefined)) + + get __backing_paramVar3(): (IProviderDecoratedVariable | undefined) + set paramVar4(paramVar4: (Set | undefined)) + + get paramVar4(): (Set | undefined) + set __backing_paramVar4(__backing_paramVar4: (IProviderDecoratedVariable> | undefined)) + + get __backing_paramVar4(): (IProviderDecoratedVariable> | undefined) + set paramVar5(paramVar5: (Array | undefined)) + + get paramVar5(): (Array | undefined) + set __backing_paramVar5(__backing_paramVar5: (IProviderDecoratedVariable> | undefined)) + + get __backing_paramVar5(): (IProviderDecoratedVariable> | undefined) + set paramVar6(paramVar6: (Array | undefined)) + + get paramVar6(): (Array | undefined) + set __backing_paramVar6(__backing_paramVar6: (IProviderDecoratedVariable> | undefined)) + + get __backing_paramVar6(): (IProviderDecoratedVariable> | undefined) + set paramVar7(paramVar7: (Array | undefined)) + + get paramVar7(): (Array | undefined) + set __backing_paramVar7(__backing_paramVar7: (IProviderDecoratedVariable> | undefined)) + + get __backing_paramVar7(): (IProviderDecoratedVariable> | undefined) + set paramVar9(paramVar9: (Date | undefined)) + + get paramVar9(): (Date | undefined) + set __backing_paramVar9(__backing_paramVar9: (IProviderDecoratedVariable | undefined)) + + get __backing_paramVar9(): (IProviderDecoratedVariable | undefined) + set paramVar10(paramVar10: (Map | undefined)) + + get paramVar10(): (Map | undefined) + set __backing_paramVar10(__backing_paramVar10: (IProviderDecoratedVariable> | undefined)) + + get __backing_paramVar10(): (IProviderDecoratedVariable> | undefined) + set paramVar11(paramVar11: ((string | number) | undefined)) + + get paramVar11(): ((string | number) | undefined) + set __backing_paramVar11(__backing_paramVar11: (IProviderDecoratedVariable<(string | number)> | undefined)) + + get __backing_paramVar11(): (IProviderDecoratedVariable<(string | number)> | undefined) + set paramVar12(paramVar12: ((Set | Per) | undefined)) + + get paramVar12(): ((Set | Per) | undefined) + set __backing_paramVar12(__backing_paramVar12: (IProviderDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_paramVar12(): (IProviderDecoratedVariable<(Set | Per)> | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test complex type @Provider decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-to-consumer.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-to-consumer.test.ts new file mode 100644 index 000000000..89a5a701f --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-to-consumer.test.ts @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provider-and-consumer'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'provider-to-consumer.ets'), +]; + +const pluginTester = new PluginTester('test usage of @Provider and @Consumer decorator', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedCheckedScript: string = ` +import { IConsumerDecoratedVariable as IConsumerDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { IProviderDecoratedVariable as IProviderDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, DragEvent as DragEvent, Button as Button, Column as Column, Text as Text, ForEach as ForEach, Divider as Divider } from "@ohos.arkui.component"; + +import { Provider as Provider, Consumer as Consumer, Local as Local, ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +const data: Array = [new User("Json", 10), new User("Eric", 15)]; + +function main() {} + +data = [new User("Json", 10), new User("Eric", 15)]; + +@ObservedV2() class User implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"name"}) private __backing_name?: string; + + @JSONStringifyIgnore() private __meta_name: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"age"}) private __backing_age?: number; + + @JSONStringifyIgnore() private __meta_age: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get name(): string { + this.conditionalAddRef(this.__meta_name); + return UIUtils.makeObserved((this.__backing_name as string)); + } + + public set name(newValue: string) { + if (((this.__backing_name) !== (newValue))) { + this.__backing_name = newValue; + this.__meta_name.fireChange(); + this.executeOnSubscribingWatches("name"); + } + } + + public get age(): number { + this.conditionalAddRef(this.__meta_age); + return UIUtils.makeObserved((this.__backing_age as number)); + } + + public set age(newValue: number) { + if (((this.__backing_age) !== (newValue))) { + this.__backing_age = newValue; + this.__meta_age.fireChange(); + this.executeOnSubscribingWatches("age"); + } + } + + public constructor(name: string, age: number) { + this.name = name; + this.age = age; + } + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_users = STATE_MGMT_FACTORY.makeProvider>(this, "users", "data", data); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_users?: IProviderDecoratedVariable>; + + public get users(): Array { + return this.__backing_users!.get(); + } + + public set users(value: Array) { + this.__backing_users!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), undefined, undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.users.push(new User("Molly", 18)); + })); + return; + }), "add new user", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + (this.users[0].age++); + })); + return; + }), "age++", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.users[0].name = "Shelly"; + })); + return; + }), "change name", undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() final struct Child extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_users = STATE_MGMT_FACTORY.makeConsumer>(this, "users", "data", []); + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_users?: IConsumerDecoratedVariable>; + + public get users(): Array { + return this.__backing_users!.get(); + } + + public set users(value: Array) { + this.__backing_users!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ForEach(((): Array => { + return this.users; + }), ((item: User) => { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(30); + return; + }), \`name: \${item.name}\`, undefined, undefined); + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(30); + return; + }), \`age: \${item.age}\`, undefined, undefined); + Divider(undefined); + })); + })); + })); + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set users(users: (Array | undefined)) + + get users(): (Array | undefined) + set __backing_users(__backing_users: (IProviderDecoratedVariable> | undefined)) + + get __backing_users(): (IProviderDecoratedVariable> | undefined) + +} + +@ComponentV2() export interface __Options_Child { + set users(users: (Array | undefined)) + + get users(): (Array | undefined) + set __backing_users(__backing_users: (IConsumerDecoratedVariable> | undefined)) + + get __backing_users(): (IConsumerDecoratedVariable> | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test usage of @Provider and @Consumer decorator', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/require/basic-require.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/require/basic-require.test.ts new file mode 100644 index 000000000..a7892cf9f --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/require/basic-require.test.ts @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/require'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'basic-require.ets'), +]; + +const pluginTester = new PluginTester('test @Require decorator capability', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedParsedScript: string = ` +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, ComponentV2 as ComponentV2, BuilderParam as BuilderParam } from "@ohos.arkui.component"; +import { State as State, Require as Require, Prop as Prop, Provide as Provide, Param as Param } from "@ohos.arkui.stateManagement"; + +@Component() final struct MyStateSample extends CustomComponent { + public hello: string = "hello"; + @State() public state1: boolean = false; + @Require() public select100!: string; + @Require() @State() public select0!: number; + @Require() @State() public select3?: (number | null); + @Require() @State() public select4?: undefined; + @Require() @Prop() public select1!: string; + @Require() @Provide({alias:"15"}) public select2!: string[]; + @Require() @Provide({alias:"t"}) public select6?: (string[] | undefined | string); + @Require() @BuilderParam() public builder!: (()=> void); + + public build() {} + + public constructor() {} + +} + +@ComponentV2() final struct V2222 extends CustomComponentV2 { + @Require() @Param() public select1!: string; + + public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + hello?: string; + state1?: boolean; + @State() __backing_state1?: boolean; + select100?: string; + select0?: number; + @Require() @State() __backing_select0?: number; + select3?: (number | null); + @Require() @State() __backing_select3?: (number | null); + select4?: undefined; + @Require() @State() __backing_select4?: undefined; + select1?: string; + @Require() @Prop() __backing_select1?: string; + select2?: string[]; + @Require() @Provide({alias:"15"}) __backing_select2?: string[]; + select6?: (string[] | undefined | string); + @Require() @Provide({alias:"t"}) __backing_select6?: (string[] | undefined | string); + @BuilderParam() builder?: (()=> void); + +} + +@ComponentV2() export interface __Options_V2222 { + select1?: string; + @Require() @Param() __backing_select1?: string; + +} +`; + +const expectedCheckedScript: string = ` +import { IParamDecoratedVariable as IParamDecoratedVariable } from "arkui.stateManagement.decorator"; +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, ComponentV2 as ComponentV2, BuilderParam as BuilderParam } from "@ohos.arkui.component"; +import { State as State, Require as Require, Prop as Prop, Provide as Provide, Param as Param } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_hello = ((({let gensym___159351621 = initializers; + (((gensym___159351621) == (null)) ? undefined : gensym___159351621.hello)})) ?? ("hello")); + this.__backing_state1 = STATE_MGMT_FACTORY.makeState(this, "state1", ((({let gensym___152317197 = initializers; + (((gensym___152317197) == (null)) ? undefined : gensym___152317197.state1)})) ?? (false))); + this.__backing_select100 = ((({let gensym___257385749 = initializers; + (((gensym___257385749) == (null)) ? undefined : gensym___257385749.select100)})) ?? (undefined)); + this.__backing_select0 = STATE_MGMT_FACTORY.makeState(this, "select0", (initializers!.select0 as number)); + this.__backing_select3 = STATE_MGMT_FACTORY.makeState<(number | null)>(this, "select3", (initializers!.select3 as (number | null))); + this.__backing_select4 = STATE_MGMT_FACTORY.makeState(this, "select4", (initializers!.select4 as undefined)); + this.__backing_select1 = STATE_MGMT_FACTORY.makeProp(this, "select1", (initializers!.select1 as string)); + this.__backing_select2 = STATE_MGMT_FACTORY.makeProvide>(this, "select2", "15", (initializers!.select2 as Array), false); + this.__backing_select6 = STATE_MGMT_FACTORY.makeProvide<(Array | undefined | string)>(this, "select6", "t", (initializers!.select6 as (Array | undefined | string)), false); + this.__backing_builder = ((((({let gensym___57081607 = initializers; + (((gensym___57081607) == (null)) ? undefined : gensym___57081607.builder)})) ?? (content))) ?? (undefined)) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void { + if (((({let gensym___171969630 = initializers; + (((gensym___171969630) == (null)) ? undefined : gensym___171969630.select1)})) !== (undefined))) { + this.__backing_select1!.update((initializers!.select1 as string)); + } + } + + private __backing_hello?: string; + + public get hello(): string { + return (this.__backing_hello as string); + } + + public set hello(value: string) { + this.__backing_hello = value; + } + + private __backing_state1?: IStateDecoratedVariable; + + public get state1(): boolean { + return this.__backing_state1!.get(); + } + + public set state1(value: boolean) { + this.__backing_state1!.set(value); + } + + private __backing_select100?: string; + + public get select100(): string { + return (this.__backing_select100 as string); + } + + public set select100(value: string) { + this.__backing_select100 = value; + } + + private __backing_select0?: IStateDecoratedVariable; + + public get select0(): number { + return this.__backing_select0!.get(); + } + + public set select0(value: number) { + this.__backing_select0!.set(value); + } + + private __backing_select3?: IStateDecoratedVariable<(number | null)>; + + public get select3(): (number | null) { + return this.__backing_select3!.get(); + } + + public set select3(value: (number | null)) { + this.__backing_select3!.set(value); + } + + private __backing_select4?: IStateDecoratedVariable; + + public get select4(): undefined { + return this.__backing_select4!.get(); + } + + public set select4(value: undefined) { + this.__backing_select4!.set(value); + } + + private __backing_select1?: IPropDecoratedVariable; + + public get select1(): string { + return this.__backing_select1!.get(); + } + + public set select1(value: string) { + this.__backing_select1!.set(value); + } + + private __backing_select2?: IProvideDecoratedVariable>; + + public get select2(): Array { + return this.__backing_select2!.get(); + } + + public set select2(value: Array) { + this.__backing_select2!.set(value); + } + + private __backing_select6?: IProvideDecoratedVariable<(Array | undefined | string)>; + + public get select6(): (Array | undefined | string) { + return this.__backing_select6!.get(); + } + + public set select6(value: (Array | undefined | string)) { + this.__backing_select6!.set(value); + } + + private __backing_builder?: @memo() (()=> void); + + public get builder(): @memo() (()=> void) { + return this.__backing_builder!; + } + + public set builder(value: @memo() (()=> void)) { + this.__backing_builder = value; + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() final struct V2222 extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_V2222 | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_select1 = STATE_MGMT_FACTORY.makeParam(this, "select1", (initializers!.select1 as string)); + } + + public __updateStruct(initializers: (__Options_V2222 | undefined)): void { + if (((({let gensym___155019449 = initializers; + (((gensym___155019449) == (null)) ? undefined : gensym___155019449.select1)})) !== (undefined))) { + this.__backing_select1!.update((initializers!.select1 as string)); + } + } + + private __backing_select1?: IParamDecoratedVariable; + + public get select1(): string { + return this.__backing_select1!.get(); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set hello(hello: (string | undefined)) + + get hello(): (string | undefined) + set state1(state1: (boolean | undefined)) + + get state1(): (boolean | undefined) + set __backing_state1(__backing_state1: (IStateDecoratedVariable | undefined)) + + get __backing_state1(): (IStateDecoratedVariable | undefined) + set select100(select100: (string | undefined)) + + get select100(): (string | undefined) + set select0(select0: (number | undefined)) + + get select0(): (number | undefined) + @Require() set __backing_select0(__backing_select0: (IStateDecoratedVariable | undefined)) + + @Require() get __backing_select0(): (IStateDecoratedVariable | undefined) + set select3(select3: ((number | null) | undefined)) + + get select3(): ((number | null) | undefined) + @Require() set __backing_select3(__backing_select3: (IStateDecoratedVariable<(number | null)> | undefined)) + + @Require() get __backing_select3(): (IStateDecoratedVariable<(number | null)> | undefined) + set select4(select4: (undefined | undefined)) + + get select4(): (undefined | undefined) + @Require() set __backing_select4(__backing_select4: (IStateDecoratedVariable | undefined)) + + @Require() get __backing_select4(): (IStateDecoratedVariable | undefined) + set select1(select1: (string | undefined)) + + get select1(): (string | undefined) + @Require() set __backing_select1(__backing_select1: (IPropDecoratedVariable | undefined)) + + @Require() get __backing_select1(): (IPropDecoratedVariable | undefined) + set select2(select2: (Array | undefined)) + + get select2(): (Array | undefined) + @Require() set __backing_select2(__backing_select2: (IProvideDecoratedVariable> | undefined)) + + @Require() get __backing_select2(): (IProvideDecoratedVariable> | undefined) + set select6(select6: ((Array | undefined | string) | undefined)) + + get select6(): ((Array | undefined | string) | undefined) + @Require() set __backing_select6(__backing_select6: (IProvideDecoratedVariable<(Array | undefined | string)> | undefined)) + + @Require() get __backing_select6(): (IProvideDecoratedVariable<(Array | undefined | string)> | undefined) + set builder(builder: (@memo() (()=> void) | undefined)) + + get builder(): (@memo() (()=> void) | undefined) + +} + +@ComponentV2() export interface __Options_V2222 { + set select1(select1: (string | undefined)) + + get select1(): (string | undefined) + @Require() set __backing_select1(__backing_select1: (IParamDecoratedVariable | undefined)) + + @Require() get __backing_select1(): (IParamDecoratedVariable | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test @Require decorator capability', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-build.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-build.test.ts index ad9e6798b..a0e462617 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-build.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-build.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -33,96 +34,128 @@ const pluginTester = new PluginTester('test resource transform in build method', const parsedTransform: Plugins = { name: 'resource-in-build', - parsed: uiTransform().parsed + parsed: uiTransform().parsed, }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { _rawfile as _rawfile } from "arkui.component.resources"; + +import { ImageAnimatorAttribute as ImageAnimatorAttribute } from "arkui.component.imageAnimator"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { UIImageAnimatorAttribute as UIImageAnimatorAttribute } from "@ohos.arkui.component"; -import { UISelectAttribute as UISelectAttribute } from "@ohos.arkui.component"; -import { UITextInputAttribute as UITextInputAttribute } from "@ohos.arkui.component"; -import { UIImageAttribute as UIImageAttribute } from "@ohos.arkui.component"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; -import { _rawfile as _rawfile } from "@ohos.arkui.component"; -import { _r as _r } from "@ohos.arkui.component"; + +import { ImageAttribute as ImageAttribute } from "arkui.component.image"; + +import { _r as _r } from "arkui.component.resources"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; -import { Component as Component, $r as $r, $rawfile as $rawfile, Column as Column, Text as Text, Image as Image, TextInput as TextInput, Select as Select, SelectOption as SelectOption, Margin as Margin, ImageAnimator as ImageAnimator } from "@ohos.arkui.component"; + +import { Component as Component, $r as $r, $rawfile as $rawfile, Column as Column, Text as Text, Image as Image, TextInput as TextInput, Select as Select, SelectOption as SelectOption, Margin as Margin, ImageAnimator as ImageAnimator, Resource as Resource } from "@ohos.arkui.component"; function main() {} -@Component({freezeWhenInactive:false}) final class ResourceComponent extends CustomComponent { - public __initializeStruct(initializers: __Options_ResourceComponent | undefined, @memo() content: (()=> void) | undefined): void { +@Component() final struct ResourceComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_ResourceComponent | undefined), @memo() content: ((()=> void) | undefined)): void { this.__backing_str1 = ((({let gensym___147578113 = initializers; (((gensym___147578113) == (null)) ? undefined : gensym___147578113.str1)})) ?? ("app.media.ri")); this.__backing_str2 = ((({let gensym___220149772 = initializers; (((gensym___220149772) == (null)) ? undefined : gensym___220149772.str2)})) ?? ("app.photo2.png")); + this.__backing_numbers = ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.numbers)})) ?? (["0", "1", "3", "5", "8"])); } - public __updateStruct(initializers: __Options_ResourceComponent | undefined): void {} + + public __updateStruct(initializers: (__Options_ResourceComponent | undefined)): void {} + private __backing_str1?: string; + public get str1(): string { return (this.__backing_str1 as string); } + public set str1(value: string) { this.__backing_str1 = value; } + private __backing_str2?: string; + public get str2(): string { return (this.__backing_str2 as string); } + public set str2(value: string) { this.__backing_str2 = value; } + private __backing_numbers?: Array; + + public get numbers(): Array { + return (this.__backing_numbers as Array); + } + + public set numbers(value: Array) { + this.__backing_numbers = value; + } + public aboutToAppear() { + let arr: Array = new Array(); + for (let i = 0;((i) < (5));(i++)) { + arr.push(_r(16777216, 10003, "com.example.mock", "entry")); + } + for (let item of this.numbers) { + arr.push(_r(16777216, 10003, "com.example.mock", "entry")); + } + } - @memo() public _build(@memo() style: ((instance: ResourceComponent)=> ResourceComponent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_ResourceComponent | undefined): void { - Column(undefined, undefined, (() => { - Text(undefined, _r("", "", "app.string.app_name"), undefined, undefined); - Image(undefined, _rawfile("", "", "app.photo.png"), undefined, undefined); + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, _r(16777216, 10003, "com.example.mock", "entry"), undefined, undefined); + Image(undefined, _rawfile(0, 30000, "com.example.mock", "entry", "app.mock.txt"), undefined, undefined); TextInput(undefined, { - text: _r("", "", "app.string.input_content"), + text: _r(16777220, 10003, "com.example.mock", "entry"), }, undefined); - Text(undefined, _r("", "", this.str1), undefined, undefined); - Text(undefined, _r("", "", this.str2), undefined, undefined); + Text(undefined, _r(-1, -1, "com.example.mock", "entry", this.str1), undefined, undefined); + Text(undefined, _r(-1, -1, "com.example.mock", "entry", this.str2), undefined, undefined); Select(undefined, new Array({ value: "aaa", - icon: _r("", "", "app.media.selection"), + icon: _r(16777223, 20000, "com.example.mock", "entry"), }, { value: "bbb", - icon: _r("", "", "app.media.selection"), + icon: _r(16777223, 20000, "com.example.mock", "entry"), }, { value: "ccc", - icon: _r("", "", "app.media.selection"), + icon: _r(16777223, 20000, "com.example.mock", "entry"), }, { value: "ddd", - icon: _r("", "", "app.media.selection"), + icon: _r(16777223, 20000, "com.example.mock", "entry"), }), undefined); - Image(@memo() ((instance: UIImageAttribute): void => { + Image(@memo() ((instance: ImageAttribute): void => { instance.margin(({ - top: _r("", "", "app.float.elements_margin_horizontal_m"), - bottom: _r("", "", "app.float.elements_margin_horizontal_l"), + top: _r(16777222, 10002, "com.example.mock", "entry"), + bottom: _r(16777222, 10002, "com.example.mock", "entry"), } as Margin)); return; - }), _r("", "", "app.media.app_icon"), undefined, undefined); - ImageAnimator(@memo() ((instance: UIImageAnimatorAttribute): void => { + }), _r(16777217, 20000, "com.example.mock", "entry"), undefined, undefined); + ImageAnimator(@memo() ((instance: ImageAnimatorAttribute): void => { instance.images([{ - src: _r("", "", "app.media.aaa"), + src: _r(16777217, 20000, "com.example.mock", "entry"), }, { - src: _r("", "", "app.media.bbb"), + src: _r(16777225, 20000, "com.example.mock", "entry"), }]); return; - }), undefined); + })); })); } + public constructor() {} + } -interface __Options_ResourceComponent { - set str1(str1: string | undefined) - get str1(): string | undefined - set str2(str2: string | undefined) - get str2(): string | undefined +@Component() export interface __Options_ResourceComponent { + set str1(str1: (string | undefined)) + get str1(): (string | undefined) + set str2(str2: (string | undefined)) + get str2(): (string | undefined) + set numbers(numbers: (Array | undefined)) + get numbers(): (Array | undefined) } `; @@ -132,9 +165,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test resource transform in build method', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-property.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-property.test.ts index 741751b9f..8856ce402 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-property.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-property.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,29 +38,25 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { UIImageAttribute as UIImageAttribute } from "@ohos.arkui.component"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; -import { _rawfile as _rawfile } from "@ohos.arkui.component"; -import { _r as _r } from "@ohos.arkui.component"; +import { _rawfile as _rawfile } from "arkui.component.resources"; +import { _r as _r } from "arkui.component.resources"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, $r as $r, $rawfile as $rawfile, Column as Column, Text as Text, Image as Image, Resource as Resource } from "@ohos.arkui.component"; let i: Resource; + function main() {} -i = _r("", "", "app.string.app_name"); -@Component({freezeWhenInactive:false}) final class ResourceComponent extends CustomComponent { - public __initializeStruct(initializers: __Options_ResourceComponent | undefined, @memo() content: (()=> void) | undefined): void { +i = _r(16777216, 10003, "com.example.mock", "entry"); +@Component() final struct ResourceComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_ResourceComponent | undefined), @memo() content: ((()=> void) | undefined)): void { this.__backing_str = ((({let gensym___42103502 = initializers; - (((gensym___42103502) == (null)) ? undefined : gensym___42103502.str)})) ?? (_r("", "", "app.string.app_name"))); + (((gensym___42103502) == (null)) ? undefined : gensym___42103502.str)})) ?? (_r(16777216, 10003, "com.example.mock", "entry"))); this.__backing_icon = ((({let gensym___38135554 = initializers; - (((gensym___38135554) == (null)) ? undefined : gensym___38135554.icon)})) ?? (_rawfile("", "", "app.photo.png"))); + (((gensym___38135554) == (null)) ? undefined : gensym___38135554.icon)})) ?? (_rawfile(0, 30000, "com.example.mock", "entry", "app.mock.txt"))); } - public __updateStruct(initializers: __Options_ResourceComponent | undefined): void {} + public __updateStruct(initializers: (__Options_ResourceComponent | undefined)): void {} private __backing_str?: Resource; public get str(): Resource { return (this.__backing_str as Resource); @@ -74,21 +71,23 @@ i = _r("", "", "app.string.app_name"); public set icon(value: Resource) { this.__backing_icon = value; } - @memo() public _build(@memo() style: ((instance: ResourceComponent)=> ResourceComponent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_ResourceComponent | undefined): void { - Column(undefined, undefined, (() => { + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { Text(undefined, this.str, undefined, undefined); Text(undefined, i, undefined, undefined); Image(undefined, this.icon, undefined, undefined); })); } public constructor() {} + } -interface __Options_ResourceComponent { - set str(str: Resource | undefined) - get str(): Resource | undefined - set icon(icon: Resource | undefined) - get icon(): Resource | undefined +@Component() export interface __Options_ResourceComponent { + set str(str: (Resource | undefined)) + get str(): (Resource | undefined) + set icon(icon: (Resource | undefined)) + get icon(): (Resource | undefined) } `; @@ -98,9 +97,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test resource transform in property', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-basic.test.ts index 0589b25ab..1414b3d22 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-basic.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-basic.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,58 +38,58 @@ const reusableTransform: Plugins = { const pluginTester = new PluginTester('test basic reusable', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; -import { memo as memo } from "arkui.stateManagement.runtime"; - -import { DecoratedV1VariableBase as DecoratedV1VariableBase } from "@ohos.arkui.stateManagement"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; -import { LinkDecoratedVariable as LinkDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { memo as memo } from "arkui.stateManagement.runtime"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, Reusable as Reusable } from "@ohos.arkui.component"; -import { State as State, Link as Link } from "@ohos.arkui.stateManagement"; +import { State as State, Prop as Prop } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void { + @memo() public build() { Child._instantiateImpl(undefined, (() => { return new Child(); - }), ({ + }), { num: 5, - } as __Options_Child), undefined, "Child"); + }, "Child", undefined); } public constructor() {} } -@Component({freezeWhenInactive:false}) @Reusable() final class Child extends CustomComponent { - public __initializeStruct(initializers: __Options_Child | undefined, @memo() content: (()=> void) | undefined): void { - if (({let gensym___98468840 = initializers; - (((gensym___98468840) == (null)) ? undefined : gensym___98468840.__backing_num)})) { - this.__backing_num = new LinkDecoratedVariable("num", initializers!.__backing_num!); - }; - this.__backing_num1 = new StateDecoratedVariable("num1", ((({let gensym___33833641 = initializers; - (((gensym___33833641) == (null)) ? undefined : gensym___33833641.num1)})) ?? (2))); +@Component() @Reusable() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = STATE_MGMT_FACTORY.makeProp(this, "num", ((({let gensym___83257243 = initializers; + (((gensym___83257243) == (null)) ? undefined : gensym___83257243.num)})) ?? (1))); + this.__backing_num1 = STATE_MGMT_FACTORY.makeState(this, "num1", ((({let gensym___24398512 = initializers; + (((gensym___24398512) == (null)) ? undefined : gensym___24398512.num1)})) ?? (2))); } - public __updateStruct(initializers: __Options_Child | undefined): void {} + public __updateStruct(initializers: (__Options_Child | undefined)): void { + if (((({let gensym___108716469 = initializers; + (((gensym___108716469) == (null)) ? undefined : gensym___108716469.num)})) !== (undefined))) { + this.__backing_num!.update((initializers!.num as number)); + } + } - public override __toRecord(params: Object): Record { + public override constructor __toRecord(params: Object): Record { const paramsCasted = (params as __Options_Child); return { "num": ((paramsCasted.num) ?? (new Object())), @@ -96,7 +97,7 @@ function main() {} }; } - private __backing_num?: LinkDecoratedVariable; + private __backing_num?: IPropDecoratedVariable; public get num(): number { return this.__backing_num!.get(); @@ -106,7 +107,7 @@ function main() {} this.__backing_num!.set(value); } - private __backing_num1?: StateDecoratedVariable; + private __backing_num1?: IStateDecoratedVariable; public get num1(): number { return this.__backing_num1!.get(); @@ -116,29 +117,29 @@ function main() {} this.__backing_num1!.set(value); } - @memo() public _build(@memo() style: ((instance: Child)=> Child) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Child | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Component() export interface __Options_MyStateSample { } -interface __Options_Child { - set num(num: number | undefined) +@Component() @Reusable() export interface __Options_Child { + set num(num: (number | undefined)) - get num(): number | undefined - set __backing_num(__backing_num: DecoratedV1VariableBase | undefined) + get num(): (number | undefined) + set __backing_num(__backing_num: (IPropDecoratedVariable | undefined)) - get __backing_num(): DecoratedV1VariableBase | undefined - set num1(num1: number | undefined) + get __backing_num(): (IPropDecoratedVariable | undefined) + set num1(num1: (number | undefined)) - get num1(): number | undefined - set __backing_num1(__backing_num1: StateDecoratedVariable | undefined) + get num1(): (number | undefined) + set __backing_num1(__backing_num1: (IStateDecoratedVariable | undefined)) - get __backing_num1(): StateDecoratedVariable | undefined + get __backing_num1(): (IStateDecoratedVariable | undefined) } `; @@ -149,9 +150,9 @@ function testReusableTransformer(this: PluginTestContext): void { pluginTester.run( 'test basic reusable', - [reusableTransform, uiNoRecheck], + [reusableTransform, uiNoRecheck, recheck], { - checked: [testReusableTransformer], + 'checked:ui-no-recheck': [testReusableTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-complex.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-complex.test.ts index 873738ced..8c3c455dc 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-complex.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-complex.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,22 +38,30 @@ const reusableTransform: Plugins = { const pluginTester = new PluginTester('test complex reusable', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { TextAttribute as TextAttribute } from "arkui.component.text"; -import { memo as memo } from "arkui.stateManagement.runtime"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ConditionScope as ConditionScope } from "arkui.component.builder"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; -import { UIButtonAttribute as UIButtonAttribute } from "@ohos.arkui.component"; +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { memo as memo } from "arkui.stateManagement.runtime"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; +import { ColumnAttribute as ColumnAttribute } from "arkui.component.column"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, Entry as Entry, Reusable as Reusable, Column as Column, Text as Text, Button as Button, ClickEvent as ClickEvent, FontWeight as FontWeight } from "@ohos.arkui.component"; @@ -61,10 +70,16 @@ import { State as State } from "@ohos.arkui.stateManagement"; function main() {} - +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/reusable/reusable-complex", + pageFullPath: "test/demo/mock/decorators/reusable/reusable-complex", + integratedHsp: "false", + } as NavInterface)); class Message { - public value: string | undefined; + public value: (string | undefined); public constructor(value: string) { this.value = value; @@ -72,15 +87,15 @@ class Message { } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class Index extends CustomComponent { - public __initializeStruct(initializers: __Options_Index | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_display = new StateDecoratedVariable("display", ((({let gensym___83835842 = initializers; +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_display = STATE_MGMT_FACTORY.makeState(this, "display", ((({let gensym___83835842 = initializers; (((gensym___83835842) == (null)) ? undefined : gensym___83835842.display)})) ?? (true))); } - public __updateStruct(initializers: __Options_Index | undefined): void {} + public __updateStruct(initializers: (__Options_Index | undefined)): void {} - private __backing_display?: StateDecoratedVariable; + private __backing_display?: IStateDecoratedVariable; public get display(): boolean { return this.__backing_display!.get(); @@ -90,24 +105,28 @@ class Message { this.__backing_display!.set(value); } - @memo() public _build(@memo() style: ((instance: Index)=> Index) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Index | undefined): void { - Column(@memo() ((instance: UIColumnAttribute): void => { + @memo() public build() { + Column(@memo() ((instance: ColumnAttribute): void => { instance.height("100%").width("100%"); return; - }), undefined, (() => { - Button(@memo() ((instance: UIButtonAttribute): void => { + }), undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.fontSize(30).fontWeight(FontWeight.Bold).onClick(((e: ClickEvent) => { this.display = !(this.display); })); return; }), "Hello", undefined, undefined); - if (this.display) { - Child._instantiateImpl(undefined, (() => { - return new Child(); - }), ({ - message: new Message("Child"), - } as __Options_Child), undefined, "Child"); - } + ConditionScope(@memo() (() => { + if (this.display) { + ConditionBranch(@memo() (() => { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + message: new Message("Child"), + }, "Child", undefined); + })); + } + })); })); } @@ -115,22 +134,22 @@ class Message { } -@Reusable() @Component({freezeWhenInactive:false}) final class Child extends CustomComponent { - public __initializeStruct(initializers: __Options_Child | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_message = new StateDecoratedVariable("message", ((({let gensym___91869411 = initializers; +@Reusable() @Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_message = STATE_MGMT_FACTORY.makeState(this, "message", ((({let gensym___91869411 = initializers; (((gensym___91869411) == (null)) ? undefined : gensym___91869411.message)})) ?? (new Message("AboutToReuse")))); } - public __updateStruct(initializers: __Options_Child | undefined): void {} + public __updateStruct(initializers: (__Options_Child | undefined)): void {} - public override __toRecord(params: Object): Record { + public override constructor __toRecord(params: Object): Record { const paramsCasted = (params as __Options_Child); return { "message": ((paramsCasted.message) ?? (new Object())), }; } - private __backing_message?: StateDecoratedVariable; + private __backing_message?: IStateDecoratedVariable; public get message(): Message { return this.__backing_message!.get(); @@ -144,12 +163,12 @@ class Message { console.info("Recycle ====Child=="); } - @memo() public _build(@memo() style: ((instance: Child)=> Child) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Child | undefined): void { - Column(@memo() ((instance: UIColumnAttribute): void => { + @memo() public build() { + Column(@memo() ((instance: ColumnAttribute): void => { instance.borderWidth(1).height(100); return; - }), undefined, (() => { - Text(@memo() ((instance: UITextAttribute): void => { + }), undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { instance.fontSize(30); return; }), this.message.value, undefined, undefined); @@ -160,23 +179,23 @@ class Message { } -interface __Options_Index { - set display(display: boolean | undefined) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_Index { + set display(display: (boolean | undefined)) - get display(): boolean | undefined - set __backing_display(__backing_display: StateDecoratedVariable | undefined) + get display(): (boolean | undefined) + set __backing_display(__backing_display: (IStateDecoratedVariable | undefined)) - get __backing_display(): StateDecoratedVariable | undefined + get __backing_display(): (IStateDecoratedVariable | undefined) } -interface __Options_Child { - set message(message: Message | undefined) +@Reusable() @Component() export interface __Options_Child { + set message(message: (Message | undefined)) - get message(): Message | undefined - set __backing_message(__backing_message: StateDecoratedVariable | undefined) + get message(): (Message | undefined) + set __backing_message(__backing_message: (IStateDecoratedVariable | undefined)) - get __backing_message(): StateDecoratedVariable | undefined + get __backing_message(): (IStateDecoratedVariable | undefined) } @@ -190,7 +209,6 @@ class __EntryWrapper extends EntryPoint { public constructor() {} } - `; function testReusableTransformer(this: PluginTestContext): void { @@ -199,9 +217,9 @@ function testReusableTransformer(this: PluginTestContext): void { pluginTester.run( 'test complex reusable', - [reusableTransform, uiNoRecheck], + [reusableTransform, uiNoRecheck, recheck], { - checked: [testReusableTransformer], + 'checked:ui-no-recheck': [testReusableTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-basic-type.test.ts index 96d03ea4b..b354198ae 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-basic-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-basic-type.test.ts @@ -14,19 +14,20 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; -const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/state'; +const STATE_DIR_PATH: string = 'decorators/state'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'state-basic-type.ets'), + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'state-basic-type.ets'), ]; const pluginTester = new PluginTester('test basic type @State decorated variables transformation', buildConfig); @@ -37,91 +38,129 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { State as State } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class Parent extends CustomComponent { - public __initializeStruct(initializers: __Options_Parent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_stateVar1 = new StateDecoratedVariable("stateVar1", ((({let gensym___213853607 = initializers; + + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_stateVar1 = STATE_MGMT_FACTORY.makeState(this, "stateVar1", ((({let gensym___213853607 = initializers; (((gensym___213853607) == (null)) ? undefined : gensym___213853607.stateVar1)})) ?? ("stateVar1"))); - this.__backing_stateVar2 = new StateDecoratedVariable("stateVar2", ((({let gensym___113574154 = initializers; + this.__backing_stateVar2 = STATE_MGMT_FACTORY.makeState(this, "stateVar2", ((({let gensym___113574154 = initializers; (((gensym___113574154) == (null)) ? undefined : gensym___113574154.stateVar2)})) ?? (50))); - this.__backing_stateVar3 = new StateDecoratedVariable("stateVar3", ((({let gensym___166994972 = initializers; + this.__backing_stateVar3 = STATE_MGMT_FACTORY.makeState(this, "stateVar3", ((({let gensym___166994972 = initializers; (((gensym___166994972) == (null)) ? undefined : gensym___166994972.stateVar3)})) ?? (true))); - this.__backing_stateVar4 = new StateDecoratedVariable("stateVar4", ((({let gensym___148024261 = initializers; + this.__backing_stateVar4 = STATE_MGMT_FACTORY.makeState(this, "stateVar4", ((({let gensym___148024261 = initializers; (((gensym___148024261) == (null)) ? undefined : gensym___148024261.stateVar4)})) ?? (undefined))); - this.__backing_stateVar5 = new StateDecoratedVariable("stateVar5", ((({let gensym___99384342 = initializers; + this.__backing_stateVar5 = STATE_MGMT_FACTORY.makeState(this, "stateVar5", ((({let gensym___99384342 = initializers; (((gensym___99384342) == (null)) ? undefined : gensym___99384342.stateVar5)})) ?? (null))); } - public __updateStruct(initializers: __Options_Parent | undefined): void {} - private __backing_stateVar1?: StateDecoratedVariable; + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_stateVar1?: IStateDecoratedVariable; + public get stateVar1(): string { return this.__backing_stateVar1!.get(); } + public set stateVar1(value: string) { this.__backing_stateVar1!.set(value); } - private __backing_stateVar2?: StateDecoratedVariable; + + private __backing_stateVar2?: IStateDecoratedVariable; + public get stateVar2(): number { return this.__backing_stateVar2!.get(); } + public set stateVar2(value: number) { this.__backing_stateVar2!.set(value); } - private __backing_stateVar3?: StateDecoratedVariable; + + private __backing_stateVar3?: IStateDecoratedVariable; + public get stateVar3(): boolean { return this.__backing_stateVar3!.get(); } + public set stateVar3(value: boolean) { this.__backing_stateVar3!.set(value); } - private __backing_stateVar4?: StateDecoratedVariable; + + private __backing_stateVar4?: IStateDecoratedVariable; + public get stateVar4(): undefined { return this.__backing_stateVar4!.get(); } + public set stateVar4(value: undefined) { this.__backing_stateVar4!.set(value); } - private __backing_stateVar5?: StateDecoratedVariable; + + private __backing_stateVar5?: IStateDecoratedVariable; + public get stateVar5(): null { return this.__backing_stateVar5!.get(); } + public set stateVar5(value: null) { this.__backing_stateVar5!.set(value); } - @memo() public _build(@memo() style: ((instance: Parent)=> Parent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_Parent { - set stateVar1(stateVar1: string | undefined) - get stateVar1(): string | undefined - set __backing_stateVar1(__backing_stateVar1: StateDecoratedVariable | undefined) - get __backing_stateVar1(): StateDecoratedVariable | undefined - set stateVar2(stateVar2: number | undefined) - get stateVar2(): number | undefined - set __backing_stateVar2(__backing_stateVar2: StateDecoratedVariable | undefined) - get __backing_stateVar2(): StateDecoratedVariable | undefined - set stateVar3(stateVar3: boolean | undefined) - get stateVar3(): boolean | undefined - set __backing_stateVar3(__backing_stateVar3: StateDecoratedVariable | undefined) - get __backing_stateVar3(): StateDecoratedVariable | undefined - set stateVar4(stateVar4: undefined | undefined) - get stateVar4(): undefined | undefined - set __backing_stateVar4(__backing_stateVar4: StateDecoratedVariable | undefined) - get __backing_stateVar4(): StateDecoratedVariable | undefined - set stateVar5(stateVar5: null | undefined) - get stateVar5(): null | undefined - set __backing_stateVar5(__backing_stateVar5: StateDecoratedVariable | undefined) - get __backing_stateVar5(): StateDecoratedVariable | undefined -} +@Component() export interface __Options_Parent { + set stateVar1(stateVar1: (string | undefined)) + + get stateVar1(): (string | undefined) + set __backing_stateVar1(__backing_stateVar1: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar1(): (IStateDecoratedVariable | undefined) + set stateVar2(stateVar2: (number | undefined)) + + get stateVar2(): (number | undefined) + set __backing_stateVar2(__backing_stateVar2: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar2(): (IStateDecoratedVariable | undefined) + set stateVar3(stateVar3: (boolean | undefined)) + + get stateVar3(): (boolean | undefined) + set __backing_stateVar3(__backing_stateVar3: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar3(): (IStateDecoratedVariable | undefined) + set stateVar4(stateVar4: (undefined | undefined)) + + get stateVar4(): (undefined | undefined) + set __backing_stateVar4(__backing_stateVar4: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar4(): (IStateDecoratedVariable | undefined) + set stateVar5(stateVar5: (null | undefined)) + + get stateVar5(): (null | undefined) + set __backing_stateVar5(__backing_stateVar5: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar5(): (IStateDecoratedVariable | undefined) + +} `; function testParsedAndCheckedTransformer(this: PluginTestContext): void { @@ -130,9 +169,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test basic type @State decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-complex-type.test.ts index e42e34006..40f1f71d9 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-complex-type.test.ts @@ -14,19 +14,20 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; -const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/state'; +const STATE_DIR_PATH: string = 'decorators/state'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'state-complex-type.ets'), + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'state-complex-type.ets'), ]; const pluginTester = new PluginTester('test complex type @State decorated variables transformation', buildConfig); @@ -37,40 +38,58 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { State as State } from "@ohos.arkui.stateManagement"; function main() {} class Per { public num: number; + public constructor(num: number) { this.num = num; } + } final class StateType extends BaseEnum { private readonly #ordinal: int; + private static () {} + public constructor(ordinal: int, value: int) { super(value); this.#ordinal = ordinal; } + public static readonly TYPE1: StateType = new StateType(0, 0); + public static readonly TYPE2: StateType = new StateType(1, 1); + public static readonly TYPE3: StateType = new StateType(2, 3); + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + private static readonly #ValuesArray: int[] = [0, 1, 3]; + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + private static readonly #ItemsArray: StateType[] = [StateType.TYPE1, StateType.TYPE2, StateType.TYPE3]; + public getName(): String { return StateType.#NamesArray[this.#ordinal]; } + public static getValueOf(name: String): StateType { for (let i = 0;((i) < (StateType.#NamesArray.length));(++i)) { if (((name) == (StateType.#NamesArray[i]))) { @@ -79,6 +98,7 @@ final class StateType extends BaseEnum { } throw new Error((("No enum constant StateType.") + (name))); } + public static fromValue(value: int): StateType { for (let i = 0;((i) < (StateType.#ValuesArray.length));(++i)) { if (((value) == (StateType.#ValuesArray[i]))) { @@ -87,189 +107,261 @@ final class StateType extends BaseEnum { } throw new Error((("No enum StateType with value ") + (value))); } + public valueOf(): int { return StateType.#ValuesArray[this.#ordinal]; } + public toString(): String { return StateType.#StringValuesArray[this.#ordinal]; } + public static values(): StateType[] { return StateType.#ItemsArray; } + public getOrdinal(): int { return this.#ordinal; } + public static $_get(e: StateType): String { return e.getName(); } + } -@Component({freezeWhenInactive:false}) final class Parent extends CustomComponent { - public __initializeStruct(initializers: __Options_Parent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_stateVar1 = new StateDecoratedVariable("stateVar1", ((({let gensym___213853607 = initializers; +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_stateVar1 = STATE_MGMT_FACTORY.makeState(this, "stateVar1", ((({let gensym___213853607 = initializers; (((gensym___213853607) == (null)) ? undefined : gensym___213853607.stateVar1)})) ?? (new Per(6)))); - this.__backing_stateVar2 = new StateDecoratedVariable>("stateVar2", ((({let gensym___113574154 = initializers; + this.__backing_stateVar2 = STATE_MGMT_FACTORY.makeState>(this, "stateVar2", ((({let gensym___113574154 = initializers; (((gensym___113574154) == (null)) ? undefined : gensym___113574154.stateVar2)})) ?? (new Array(3, 6, 8)))); - this.__backing_stateVar3 = new StateDecoratedVariable("stateVar3", ((({let gensym___166994972 = initializers; + this.__backing_stateVar3 = STATE_MGMT_FACTORY.makeState(this, "stateVar3", ((({let gensym___166994972 = initializers; (((gensym___166994972) == (null)) ? undefined : gensym___166994972.stateVar3)})) ?? (StateType.TYPE3))); - this.__backing_stateVar4 = new StateDecoratedVariable>("stateVar4", ((({let gensym___148024261 = initializers; + this.__backing_stateVar4 = STATE_MGMT_FACTORY.makeState>(this, "stateVar4", ((({let gensym___148024261 = initializers; (((gensym___148024261) == (null)) ? undefined : gensym___148024261.stateVar4)})) ?? (new Set(new Array("aa", "bb"))))); - this.__backing_stateVar5 = new StateDecoratedVariable>("stateVar5", ((({let gensym___99384342 = initializers; + this.__backing_stateVar5 = STATE_MGMT_FACTORY.makeState>(this, "stateVar5", ((({let gensym___99384342 = initializers; (((gensym___99384342) == (null)) ? undefined : gensym___99384342.stateVar5)})) ?? ([true, false]))); - this.__backing_stateVar6 = new StateDecoratedVariable>("stateVar6", ((({let gensym___133364871 = initializers; + this.__backing_stateVar6 = STATE_MGMT_FACTORY.makeState>(this, "stateVar6", ((({let gensym___133364871 = initializers; (((gensym___133364871) == (null)) ? undefined : gensym___133364871.stateVar6)})) ?? (new Array(new Per(7), new Per(11))))); - this.__backing_stateVar7 = new StateDecoratedVariable>("stateVar7", ((({let gensym___69403028 = initializers; + this.__backing_stateVar7 = STATE_MGMT_FACTORY.makeState>(this, "stateVar7", ((({let gensym___69403028 = initializers; (((gensym___69403028) == (null)) ? undefined : gensym___69403028.stateVar7)})) ?? ([new Per(7), new Per(11)]))); - this.__backing_stateVar8 = new StateDecoratedVariable<((sr: string)=> void)>("stateVar8", ((({let gensym___219403122 = initializers; + this.__backing_stateVar8 = STATE_MGMT_FACTORY.makeState<((sr: string)=> void)>(this, "stateVar8", ((({let gensym___219403122 = initializers; (((gensym___219403122) == (null)) ? undefined : gensym___219403122.stateVar8)})) ?? (((sr: string) => {})))); - this.__backing_stateVar9 = new StateDecoratedVariable("stateVar9", ((({let gensym___171171899 = initializers; + this.__backing_stateVar9 = STATE_MGMT_FACTORY.makeState(this, "stateVar9", ((({let gensym___171171899 = initializers; (((gensym___171171899) == (null)) ? undefined : gensym___171171899.stateVar9)})) ?? (new Date("2025-4-23")))); - this.__backing_stateVar10 = new StateDecoratedVariable>("stateVar10", ((({let gensym___91651348 = initializers; + this.__backing_stateVar10 = STATE_MGMT_FACTORY.makeState>(this, "stateVar10", ((({let gensym___91651348 = initializers; (((gensym___91651348) == (null)) ? undefined : gensym___91651348.stateVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]])))); - this.__backing_stateVar11 = new StateDecoratedVariable("stateVar11", ((({let gensym___56045278 = initializers; + this.__backing_stateVar11 = STATE_MGMT_FACTORY.makeState<(string | number)>(this, "stateVar11", ((({let gensym___56045278 = initializers; (((gensym___56045278) == (null)) ? undefined : gensym___56045278.stateVar11)})) ?? (0.0))); - this.__backing_stateVar12 = new StateDecoratedVariable | Per>("stateVar12", ((({let gensym___164759887 = initializers; + this.__backing_stateVar12 = STATE_MGMT_FACTORY.makeState<(Set | Per)>(this, "stateVar12", ((({let gensym___164759887 = initializers; (((gensym___164759887) == (null)) ? undefined : gensym___164759887.stateVar12)})) ?? (new Per(6)))); } - public __updateStruct(initializers: __Options_Parent | undefined): void {} - private __backing_stateVar1?: StateDecoratedVariable; + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_stateVar1?: IStateDecoratedVariable; + public get stateVar1(): Per { return this.__backing_stateVar1!.get(); } + public set stateVar1(value: Per) { this.__backing_stateVar1!.set(value); } - private __backing_stateVar2?: StateDecoratedVariable>; + + private __backing_stateVar2?: IStateDecoratedVariable>; + public get stateVar2(): Array { return this.__backing_stateVar2!.get(); } + public set stateVar2(value: Array) { this.__backing_stateVar2!.set(value); } - private __backing_stateVar3?: StateDecoratedVariable; + + private __backing_stateVar3?: IStateDecoratedVariable; + public get stateVar3(): StateType { return this.__backing_stateVar3!.get(); } + public set stateVar3(value: StateType) { this.__backing_stateVar3!.set(value); } - private __backing_stateVar4?: StateDecoratedVariable>; + + private __backing_stateVar4?: IStateDecoratedVariable>; + public get stateVar4(): Set { return this.__backing_stateVar4!.get(); } + public set stateVar4(value: Set) { this.__backing_stateVar4!.set(value); } - private __backing_stateVar5?: StateDecoratedVariable>; + + private __backing_stateVar5?: IStateDecoratedVariable>; + public get stateVar5(): Array { return this.__backing_stateVar5!.get(); } + public set stateVar5(value: Array) { this.__backing_stateVar5!.set(value); } - private __backing_stateVar6?: StateDecoratedVariable>; + + private __backing_stateVar6?: IStateDecoratedVariable>; + public get stateVar6(): Array { return this.__backing_stateVar6!.get(); } + public set stateVar6(value: Array) { this.__backing_stateVar6!.set(value); } - private __backing_stateVar7?: StateDecoratedVariable>; + + private __backing_stateVar7?: IStateDecoratedVariable>; + public get stateVar7(): Array { return this.__backing_stateVar7!.get(); } + public set stateVar7(value: Array) { this.__backing_stateVar7!.set(value); } - private __backing_stateVar8?: StateDecoratedVariable<((sr: string)=> void)>; + + private __backing_stateVar8?: IStateDecoratedVariable<((sr: string)=> void)>; + public get stateVar8(): ((sr: string)=> void) { return this.__backing_stateVar8!.get(); } + public set stateVar8(value: ((sr: string)=> void)) { this.__backing_stateVar8!.set(value); } - private __backing_stateVar9?: StateDecoratedVariable; + + private __backing_stateVar9?: IStateDecoratedVariable; + public get stateVar9(): Date { return this.__backing_stateVar9!.get(); } + public set stateVar9(value: Date) { this.__backing_stateVar9!.set(value); } - private __backing_stateVar10?: StateDecoratedVariable>; + + private __backing_stateVar10?: IStateDecoratedVariable>; + public get stateVar10(): Map { return this.__backing_stateVar10!.get(); } + public set stateVar10(value: Map) { this.__backing_stateVar10!.set(value); } - private __backing_stateVar11?: StateDecoratedVariable; - public get stateVar11(): string | number { + + private __backing_stateVar11?: IStateDecoratedVariable<(string | number)>; + + public get stateVar11(): (string | number) { return this.__backing_stateVar11!.get(); } - public set stateVar11(value: string | number) { + + public set stateVar11(value: (string | number)) { this.__backing_stateVar11!.set(value); } - private __backing_stateVar12?: StateDecoratedVariable | Per>; - public get stateVar12(): Set | Per { + + private __backing_stateVar12?: IStateDecoratedVariable<(Set | Per)>; + + public get stateVar12(): (Set | Per) { return this.__backing_stateVar12!.get(); } - public set stateVar12(value: Set | Per) { + + public set stateVar12(value: (Set | Per)) { this.__backing_stateVar12!.set(value); } - @memo() public _build(@memo() style: ((instance: Parent)=> Parent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_Parent { - set stateVar1(stateVar1: Per | undefined) - get stateVar1(): Per | undefined - set __backing_stateVar1(__backing_stateVar1: StateDecoratedVariable | undefined) - get __backing_stateVar1(): StateDecoratedVariable | undefined - set stateVar2(stateVar2: Array | undefined) - get stateVar2(): Array | undefined - set __backing_stateVar2(__backing_stateVar2: StateDecoratedVariable> | undefined) - get __backing_stateVar2(): StateDecoratedVariable> | undefined - set stateVar3(stateVar3: StateType | undefined) - get stateVar3(): StateType | undefined - set __backing_stateVar3(__backing_stateVar3: StateDecoratedVariable | undefined) - get __backing_stateVar3(): StateDecoratedVariable | undefined - set stateVar4(stateVar4: Set | undefined) - get stateVar4(): Set | undefined - set __backing_stateVar4(__backing_stateVar4: StateDecoratedVariable> | undefined) - get __backing_stateVar4(): StateDecoratedVariable> | undefined - set stateVar5(stateVar5: Array | undefined) - get stateVar5(): Array | undefined - set __backing_stateVar5(__backing_stateVar5: StateDecoratedVariable> | undefined) - get __backing_stateVar5(): StateDecoratedVariable> | undefined - set stateVar6(stateVar6: Array | undefined) - get stateVar6(): Array | undefined - set __backing_stateVar6(__backing_stateVar6: StateDecoratedVariable> | undefined) - get __backing_stateVar6(): StateDecoratedVariable> | undefined - set stateVar7(stateVar7: Array | undefined) - get stateVar7(): Array | undefined - set __backing_stateVar7(__backing_stateVar7: StateDecoratedVariable> | undefined) - get __backing_stateVar7(): StateDecoratedVariable> | undefined - set stateVar8(stateVar8: ((sr: string)=> void) | undefined) - get stateVar8(): ((sr: string)=> void) | undefined - set __backing_stateVar8(__backing_stateVar8: StateDecoratedVariable<((sr: string)=> void)> | undefined) - get __backing_stateVar8(): StateDecoratedVariable<((sr: string)=> void)> | undefined - set stateVar9(stateVar9: Date | undefined) - get stateVar9(): Date | undefined - set __backing_stateVar9(__backing_stateVar9: StateDecoratedVariable | undefined) - get __backing_stateVar9(): StateDecoratedVariable | undefined - set stateVar10(stateVar10: Map | undefined) - get stateVar10(): Map | undefined - set __backing_stateVar10(__backing_stateVar10: StateDecoratedVariable> | undefined) - get __backing_stateVar10(): StateDecoratedVariable> | undefined - set stateVar11(stateVar11: string | number | undefined) - get stateVar11(): string | number | undefined - set __backing_stateVar11(__backing_stateVar11: StateDecoratedVariable | undefined) - get __backing_stateVar11(): StateDecoratedVariable | undefined - set stateVar12(stateVar12: Set | Per | undefined) - get stateVar12(): Set | Per | undefined - set __backing_stateVar12(__backing_stateVar12: StateDecoratedVariable | Per> | undefined) - get __backing_stateVar12(): StateDecoratedVariable | Per> | undefined +@Component() export interface __Options_Parent { + set stateVar1(stateVar1: (Per | undefined)) + + get stateVar1(): (Per | undefined) + set __backing_stateVar1(__backing_stateVar1: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar1(): (IStateDecoratedVariable | undefined) + set stateVar2(stateVar2: (Array | undefined)) + + get stateVar2(): (Array | undefined) + set __backing_stateVar2(__backing_stateVar2: (IStateDecoratedVariable> | undefined)) + + get __backing_stateVar2(): (IStateDecoratedVariable> | undefined) + set stateVar3(stateVar3: (StateType | undefined)) + + get stateVar3(): (StateType | undefined) + set __backing_stateVar3(__backing_stateVar3: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar3(): (IStateDecoratedVariable | undefined) + set stateVar4(stateVar4: (Set | undefined)) + + get stateVar4(): (Set | undefined) + set __backing_stateVar4(__backing_stateVar4: (IStateDecoratedVariable> | undefined)) + + get __backing_stateVar4(): (IStateDecoratedVariable> | undefined) + set stateVar5(stateVar5: (Array | undefined)) + + get stateVar5(): (Array | undefined) + set __backing_stateVar5(__backing_stateVar5: (IStateDecoratedVariable> | undefined)) + + get __backing_stateVar5(): (IStateDecoratedVariable> | undefined) + set stateVar6(stateVar6: (Array | undefined)) + + get stateVar6(): (Array | undefined) + set __backing_stateVar6(__backing_stateVar6: (IStateDecoratedVariable> | undefined)) + + get __backing_stateVar6(): (IStateDecoratedVariable> | undefined) + set stateVar7(stateVar7: (Array | undefined)) + + get stateVar7(): (Array | undefined) + set __backing_stateVar7(__backing_stateVar7: (IStateDecoratedVariable> | undefined)) + + get __backing_stateVar7(): (IStateDecoratedVariable> | undefined) + set stateVar8(stateVar8: (((sr: string)=> void) | undefined)) + + get stateVar8(): (((sr: string)=> void) | undefined) + set __backing_stateVar8(__backing_stateVar8: (IStateDecoratedVariable<((sr: string)=> void)> | undefined)) + + get __backing_stateVar8(): (IStateDecoratedVariable<((sr: string)=> void)> | undefined) + set stateVar9(stateVar9: (Date | undefined)) + + get stateVar9(): (Date | undefined) + set __backing_stateVar9(__backing_stateVar9: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar9(): (IStateDecoratedVariable | undefined) + set stateVar10(stateVar10: (Map | undefined)) + + get stateVar10(): (Map | undefined) + set __backing_stateVar10(__backing_stateVar10: (IStateDecoratedVariable> | undefined)) + + get __backing_stateVar10(): (IStateDecoratedVariable> | undefined) + set stateVar11(stateVar11: ((string | number) | undefined)) + + get stateVar11(): ((string | number) | undefined) + set __backing_stateVar11(__backing_stateVar11: (IStateDecoratedVariable<(string | number)> | undefined)) + + get __backing_stateVar11(): (IStateDecoratedVariable<(string | number)> | undefined) + set stateVar12(stateVar12: ((Set | Per) | undefined)) + + get stateVar12(): ((Set | Per) | undefined) + set __backing_stateVar12(__backing_stateVar12: (IStateDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_stateVar12(): (IStateDecoratedVariable<(Set | Per)> | undefined) + } + `; function testParsedAndCheckedTransformer(this: PluginTestContext): void { @@ -278,9 +370,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test complex type @State decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-to-state.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-to-state.test.ts index 52a438991..45f033642 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-to-state.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-to-state.test.ts @@ -14,19 +14,20 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; -const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/state'; +const STATE_DIR_PATH: string = 'decorators/state'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'state-to-state.ets'), + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'state-to-state.ets'), ]; const pluginTester = new PluginTester('test @State decorated variables passing', buildConfig); @@ -37,83 +38,110 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, Column as Column, Text as Text } from "@ohos.arkui.component"; + import { State as State } from "@ohos.arkui.stateManagement"; function main() {} + + class Per { public str: string; + public constructor(str: string) { this.str = str; } + } -@Component({freezeWhenInactive:false}) final class Parent extends CustomComponent { - public __initializeStruct(initializers: __Options_Parent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_parentVar1 = new StateDecoratedVariable("parentVar1", ((({let gensym___247315634 = initializers; +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_parentVar1 = STATE_MGMT_FACTORY.makeState(this, "parentVar1", ((({let gensym___247315634 = initializers; (((gensym___247315634) == (null)) ? undefined : gensym___247315634.parentVar1)})) ?? (new Per("hello")))); } - public __updateStruct(initializers: __Options_Parent | undefined): void {} - private __backing_parentVar1?: StateDecoratedVariable; + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_parentVar1?: IStateDecoratedVariable; + public get parentVar1(): Per { return this.__backing_parentVar1!.get(); } + public set parentVar1(value: Per) { this.__backing_parentVar1!.set(value); } - @memo() public _build(@memo() style: ((instance: Parent)=> Parent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parent | undefined): void { - Column(undefined, undefined, (() => { + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { Child._instantiateImpl(undefined, (() => { return new Child(); - }), ({ + }), { childVar1: this.parentVar1, - } as __Options_Child), undefined, undefined); + }, undefined, undefined); })); } + public constructor() {} + } -@Component({freezeWhenInactive:false}) final class Child extends CustomComponent { - public __initializeStruct(initializers: __Options_Child | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_childVar1 = new StateDecoratedVariable("childVar1", ((({let gensym___218939886 = initializers; +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_childVar1 = STATE_MGMT_FACTORY.makeState(this, "childVar1", ((({let gensym___218939886 = initializers; (((gensym___218939886) == (null)) ? undefined : gensym___218939886.childVar1)})) ?? (new Per("ccc")))); } - public __updateStruct(initializers: __Options_Child | undefined): void {} - private __backing_childVar1?: StateDecoratedVariable; + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_childVar1?: IStateDecoratedVariable; + public get childVar1(): Per { return this.__backing_childVar1!.get(); } + public set childVar1(value: Per) { this.__backing_childVar1!.set(value); } - @memo() public _build(@memo() style: ((instance: Child)=> Child) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Child | undefined): void { + + @memo() public build() { Text(undefined, this.childVar1.str, undefined, undefined); } + public constructor() {} } -interface __Options_Parent { - set parentVar1(parentVar1: Per | undefined) - get parentVar1(): Per | undefined - set __backing_parentVar1(__backing_parentVar1: StateDecoratedVariable | undefined) - get __backing_parentVar1(): StateDecoratedVariable | undefined +@Component() export interface __Options_Parent { + set parentVar1(parentVar1: (Per | undefined)) + + get parentVar1(): (Per | undefined) + set __backing_parentVar1(__backing_parentVar1: (IStateDecoratedVariable | undefined)) + + get __backing_parentVar1(): (IStateDecoratedVariable | undefined) + } -interface __Options_Child { - set childVar1(childVar1: Per | undefined) - get childVar1(): Per | undefined - set __backing_childVar1(__backing_childVar1: StateDecoratedVariable | undefined) - get __backing_childVar1(): StateDecoratedVariable | undefined -} +@Component() export interface __Options_Child { + set childVar1(childVar1: (Per | undefined)) + + get childVar1(): (Per | undefined) + set __backing_childVar1(__backing_childVar1: (IStateDecoratedVariable | undefined)) + + get __backing_childVar1(): (IStateDecoratedVariable | undefined) + +} `; function testParsedAndCheckedTransformer(this: PluginTestContext): void { @@ -122,9 +150,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test @State decorated variables passing', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-appstorage.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-appstorage.test.ts index 7f4460286..42f22ac6e 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-appstorage.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-appstorage.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,24 +38,15 @@ const storageLinkTransform: Plugins = { const pluginTester = new PluginTester('test storagelink with appstorage', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; - +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; +import { IStorageLinkDecoratedVariable as IStorageLinkDecoratedVariable } from "arkui.stateManagement.decorator"; import { memo as memo } from "arkui.stateManagement.runtime"; - -import { StorageLinkDecoratedVariable as StorageLinkDecoratedVariable } from "@ohos.arkui.stateManagement"; - -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; - -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; - +import { TextAttribute as TextAttribute } from "arkui.component.text"; +import { NavInterface as NavInterface } from "arkui.UserView"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; - import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; - import { Component as Component, Entry as Entry, Column as Column, Text as Text, ClickEvent as ClickEvent } from "@ohos.arkui.component"; - import { StorageLink as StorageLink, AppStorage as AppStorage } from "@ohos.arkui.stateManagement"; function main() {} @@ -62,24 +54,30 @@ function main() {} AppStorage.setOrCreate("PropA", 47); AppStorage.setOrCreate("PropB", new Data(50)); +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storagelink/storagelink-appstorage", + pageFullPath: "test/demo/mock/decorators/storagelink/storagelink-appstorage", + integratedHsp: "false", + } as NavInterface)); + class Data { public code: number; - public constructor(code: number) { this.code = code; } - } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class Index extends CustomComponent { - public __initializeStruct(initializers: __Options_Index | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_storageLink = new StorageLinkDecoratedVariable("PropA", "storageLink", 1) - this.__backing_storageLinkObject = new StorageLinkDecoratedVariable("PropB", "storageLinkObject", new Data(1)) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_storageLink = STATE_MGMT_FACTORY.makeStorageLink(this, "PropA", "storageLink", 1) + this.__backing_storageLinkObject = STATE_MGMT_FACTORY.makeStorageLink(this, "PropB", "storageLinkObject", new Data(1)) } - public __updateStruct(initializers: __Options_Index | undefined): void {} + public __updateStruct(initializers: (__Options_Index | undefined)): void {} - private __backing_storageLink?: StorageLinkDecoratedVariable; + private __backing_storageLink?: IStorageLinkDecoratedVariable; public get storageLink(): number { return this.__backing_storageLink!.get(); @@ -89,7 +87,7 @@ class Data { this.__backing_storageLink!.set(value); } - private __backing_storageLinkObject?: StorageLinkDecoratedVariable; + private __backing_storageLinkObject?: IStorageLinkDecoratedVariable; public get storageLinkObject(): Data { return this.__backing_storageLinkObject!.get(); @@ -99,15 +97,15 @@ class Data { this.__backing_storageLinkObject!.set(value); } - @memo() public _build(@memo() style: ((instance: Index)=> Index) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Index | undefined): void { - Column(undefined, undefined, (() => { - Text(@memo() ((instance: UITextAttribute): void => { + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { instance.onClick(((e: ClickEvent) => { this.storageLink += 1; })); return; }), \`From AppStorage \${this.storageLink}\`, undefined, undefined); - Text(@memo() ((instance: UITextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { instance.onClick(((e: ClickEvent) => { this.storageLinkObject.code += 1; })); @@ -120,19 +118,19 @@ class Data { } -interface __Options_Index { - set storageLink(storageLink: number | undefined) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_Index { + set storageLink(storageLink: (number | undefined)) - get storageLink(): number | undefined - set __backing_storageLink(__backing_storageLink: StorageLinkDecoratedVariable | undefined) + get storageLink(): (number | undefined) + set __backing_storageLink(__backing_storageLink: (IStorageLinkDecoratedVariable | undefined)) - get __backing_storageLink(): StorageLinkDecoratedVariable | undefined - set storageLinkObject(storageLinkObject: Data | undefined) + get __backing_storageLink(): (IStorageLinkDecoratedVariable | undefined) + set storageLinkObject(storageLinkObject: (Data | undefined)) - get storageLinkObject(): Data | undefined - set __backing_storageLinkObject(__backing_storageLinkObject: StorageLinkDecoratedVariable | undefined) + get storageLinkObject(): (Data | undefined) + set __backing_storageLinkObject(__backing_storageLinkObject: (IStorageLinkDecoratedVariable | undefined)) - get __backing_storageLinkObject(): StorageLinkDecoratedVariable | undefined + get __backing_storageLinkObject(): (IStorageLinkDecoratedVariable | undefined) } @@ -154,9 +152,9 @@ function testStorageLinkTransformer(this: PluginTestContext): void { pluginTester.run( 'test storagelink with appstorage', - [storageLinkTransform, uiNoRecheck], + [storageLinkTransform, uiNoRecheck, recheck], { - checked: [testStorageLinkTransformer], + 'checked:ui-no-recheck': [testStorageLinkTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-complex-type.test.ts index dba913594..f28433b8f 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-complex-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,25 @@ const storageLinkTransform: Plugins = { const pluginTester = new PluginTester('test storagelink complex type transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; - import { memo as memo } from "arkui.stateManagement.runtime"; - -import { StorageLinkDecoratedVariable as StorageLinkDecoratedVariable } from "@ohos.arkui.stateManagement"; - +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; +import { IStorageLinkDecoratedVariable as IStorageLinkDecoratedVariable } from "arkui.stateManagement.decorator"; +import { NavInterface as NavInterface } from "arkui.UserView"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; - import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; - import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; - import { StorageLink as StorageLink } from "@ohos.arkui.stateManagement"; function main() {} - +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storagelink/storagelink-complex-type", + pageFullPath: "test/demo/mock/decorators/storagelink/storagelink-complex-type", + integratedHsp: "false", + } as NavInterface)); class Person { public name: string = ""; @@ -132,21 +133,20 @@ final class Status extends BaseEnum { } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_arrayA = new StorageLinkDecoratedVariable>("Prop1", "arrayA", [1, 2, 3]) - this.__backing_objectA = new StorageLinkDecoratedVariable("Prop2", "objectA", {}) - this.__backing_dateA = new StorageLinkDecoratedVariable("Prop3", "dateA", new Date("2021-08-08")) - this.__backing_setA = new StorageLinkDecoratedVariable>("Prop4", "setA", new Set()) - this.__backing_mapA = new StorageLinkDecoratedVariable>("Prop5", "mapA", new Map()) - this.__backing_unionA = new StorageLinkDecoratedVariable("Prop6", "unionA", "") - this.__backing_classA = new StorageLinkDecoratedVariable("Prop7", "classA", new Person("John")) - this.__backing_enumA = new StorageLinkDecoratedVariable("Prop8", "enumA", Status.NotFound) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_arrayA = STATE_MGMT_FACTORY.makeStorageLink>(this, "Prop1", "arrayA", [1, 2, 3]) + this.__backing_objectA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop2", "objectA", {}) + this.__backing_dateA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop3", "dateA", new Date("2021-08-08")) + this.__backing_setA = STATE_MGMT_FACTORY.makeStorageLink>(this, "Prop4", "setA", new Set()) + this.__backing_mapA = STATE_MGMT_FACTORY.makeStorageLink>(this, "Prop5", "mapA", new Map()) + this.__backing_classA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop7", "classA", new Person("John")) + this.__backing_enumA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop8", "enumA", Status.NotFound) } - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - private __backing_arrayA?: StorageLinkDecoratedVariable>; + private __backing_arrayA?: IStorageLinkDecoratedVariable>; public get arrayA(): Array { return this.__backing_arrayA!.get(); @@ -156,7 +156,7 @@ final class Status extends BaseEnum { this.__backing_arrayA!.set(value); } - private __backing_objectA?: StorageLinkDecoratedVariable; + private __backing_objectA?: IStorageLinkDecoratedVariable; public get objectA(): Object { return this.__backing_objectA!.get(); @@ -166,7 +166,7 @@ final class Status extends BaseEnum { this.__backing_objectA!.set(value); } - private __backing_dateA?: StorageLinkDecoratedVariable; + private __backing_dateA?: IStorageLinkDecoratedVariable; public get dateA(): Date { return this.__backing_dateA!.get(); @@ -176,7 +176,7 @@ final class Status extends BaseEnum { this.__backing_dateA!.set(value); } - private __backing_setA?: StorageLinkDecoratedVariable>; + private __backing_setA?: IStorageLinkDecoratedVariable>; public get setA(): Set { return this.__backing_setA!.get(); @@ -186,7 +186,7 @@ final class Status extends BaseEnum { this.__backing_setA!.set(value); } - private __backing_mapA?: StorageLinkDecoratedVariable>; + private __backing_mapA?: IStorageLinkDecoratedVariable>; public get mapA(): Map { return this.__backing_mapA!.get(); @@ -196,17 +196,7 @@ final class Status extends BaseEnum { this.__backing_mapA!.set(value); } - private __backing_unionA?: StorageLinkDecoratedVariable; - - public get unionA(): string | undefined { - return this.__backing_unionA!.get(); - } - - public set unionA(value: string | undefined) { - this.__backing_unionA!.set(value); - } - - private __backing_classA?: StorageLinkDecoratedVariable; + private __backing_classA?: IStorageLinkDecoratedVariable; public get classA(): Person { return this.__backing_classA!.get(); @@ -216,7 +206,7 @@ final class Status extends BaseEnum { this.__backing_classA!.set(value); } - private __backing_enumA?: StorageLinkDecoratedVariable; + private __backing_enumA?: IStorageLinkDecoratedVariable; public get enumA(): Status { return this.__backing_enumA!.get(); @@ -226,61 +216,55 @@ final class Status extends BaseEnum { this.__backing_enumA!.set(value); } - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { - set arrayA(arrayA: Array | undefined) - - get arrayA(): Array | undefined - set __backing_arrayA(__backing_arrayA: StorageLinkDecoratedVariable> | undefined) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set arrayA(arrayA: (Array | undefined)) - get __backing_arrayA(): StorageLinkDecoratedVariable> | undefined - set objectA(objectA: Object | undefined) + get arrayA(): (Array | undefined) + set __backing_arrayA(__backing_arrayA: (IStorageLinkDecoratedVariable> | undefined)) - get objectA(): Object | undefined - set __backing_objectA(__backing_objectA: StorageLinkDecoratedVariable | undefined) + get __backing_arrayA(): (IStorageLinkDecoratedVariable> | undefined) + set objectA(objectA: (Object | undefined)) - get __backing_objectA(): StorageLinkDecoratedVariable | undefined - set dateA(dateA: Date | undefined) + get objectA(): (Object | undefined) + set __backing_objectA(__backing_objectA: (IStorageLinkDecoratedVariable | undefined)) - get dateA(): Date | undefined - set __backing_dateA(__backing_dateA: StorageLinkDecoratedVariable | undefined) + get __backing_objectA(): (IStorageLinkDecoratedVariable | undefined) + set dateA(dateA: (Date | undefined)) - get __backing_dateA(): StorageLinkDecoratedVariable | undefined - set setA(setA: Set | undefined) + get dateA(): (Date | undefined) + set __backing_dateA(__backing_dateA: (IStorageLinkDecoratedVariable | undefined)) - get setA(): Set | undefined - set __backing_setA(__backing_setA: StorageLinkDecoratedVariable> | undefined) + get __backing_dateA(): (IStorageLinkDecoratedVariable | undefined) + set setA(setA: (Set | undefined)) - get __backing_setA(): StorageLinkDecoratedVariable> | undefined - set mapA(mapA: Map | undefined) + get setA(): (Set | undefined) + set __backing_setA(__backing_setA: (IStorageLinkDecoratedVariable> | undefined)) - get mapA(): Map | undefined - set __backing_mapA(__backing_mapA: StorageLinkDecoratedVariable> | undefined) + get __backing_setA(): (IStorageLinkDecoratedVariable> | undefined) + set mapA(mapA: (Map | undefined)) - get __backing_mapA(): StorageLinkDecoratedVariable> | undefined - set unionA(unionA: string | undefined | undefined) + get mapA(): (Map | undefined) + set __backing_mapA(__backing_mapA: (IStorageLinkDecoratedVariable> | undefined)) - get unionA(): string | undefined | undefined - set __backing_unionA(__backing_unionA: StorageLinkDecoratedVariable | undefined) + get __backing_mapA(): (IStorageLinkDecoratedVariable> | undefined) + set classA(classA: (Person | undefined)) - get __backing_unionA(): StorageLinkDecoratedVariable | undefined - set classA(classA: Person | undefined) + get classA(): (Person | undefined) + set __backing_classA(__backing_classA: (IStorageLinkDecoratedVariable | undefined)) - get classA(): Person | undefined - set __backing_classA(__backing_classA: StorageLinkDecoratedVariable | undefined) + get __backing_classA(): (IStorageLinkDecoratedVariable | undefined) + set enumA(enumA: (Status | undefined)) - get __backing_classA(): StorageLinkDecoratedVariable | undefined - set enumA(enumA: Status | undefined) + get enumA(): (Status | undefined) + set __backing_enumA(__backing_enumA: (IStorageLinkDecoratedVariable | undefined)) - get enumA(): Status | undefined - set __backing_enumA(__backing_enumA: StorageLinkDecoratedVariable | undefined) - - get __backing_enumA(): StorageLinkDecoratedVariable | undefined + get __backing_enumA(): (IStorageLinkDecoratedVariable | undefined) } @@ -294,7 +278,6 @@ class __EntryWrapper extends EntryPoint { public constructor() {} } - `; function testStorageLinkTransformer(this: PluginTestContext): void { @@ -303,9 +286,9 @@ function testStorageLinkTransformer(this: PluginTestContext): void { pluginTester.run( 'test storagelink complex type transform', - [storageLinkTransform, uiNoRecheck], + [storageLinkTransform, uiNoRecheck, recheck], { - checked: [testStorageLinkTransformer], + 'checked:ui-no-recheck': [testStorageLinkTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-primitive-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-primitive-type.test.ts index 27a78307e..923664b6c 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-primitive-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-primitive-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,36 +38,36 @@ const storageLinkTransform: Plugins = { const pluginTester = new PluginTester('test storagelink primitive type transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; - import { memo as memo } from "arkui.stateManagement.runtime"; - -import { StorageLinkDecoratedVariable as StorageLinkDecoratedVariable } from "@ohos.arkui.stateManagement"; - +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; +import { IStorageLinkDecoratedVariable as IStorageLinkDecoratedVariable } from "arkui.stateManagement.decorator"; +import { NavInterface as NavInterface } from "arkui.UserView"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; - import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; - import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; - import { StorageLink as StorageLink } from "@ohos.arkui.stateManagement"; function main() {} - - -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_numA = new StorageLinkDecoratedVariable("Prop1", "numA", 33) - this.__backing_stringA = new StorageLinkDecoratedVariable("Prop2", "stringA", "AA") - this.__backing_booleanA = new StorageLinkDecoratedVariable("Prop3", "booleanA", true) +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storagelink/storagelink-primitive-type", + pageFullPath: "test/demo/mock/decorators/storagelink/storagelink-primitive-type", + integratedHsp: "false", + } as NavInterface)); + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_numA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop1", "numA", 33) + this.__backing_stringA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop2", "stringA", "AA") + this.__backing_booleanA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop3", "booleanA", true) } - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - private __backing_numA?: StorageLinkDecoratedVariable; + private __backing_numA?: IStorageLinkDecoratedVariable; public get numA(): number { return this.__backing_numA!.get(); @@ -76,7 +77,7 @@ function main() {} this.__backing_numA!.set(value); } - private __backing_stringA?: StorageLinkDecoratedVariable; + private __backing_stringA?: IStorageLinkDecoratedVariable; public get stringA(): string { return this.__backing_stringA!.get(); @@ -86,7 +87,7 @@ function main() {} this.__backing_stringA!.set(value); } - private __backing_booleanA?: StorageLinkDecoratedVariable; + private __backing_booleanA?: IStorageLinkDecoratedVariable; public get booleanA(): boolean { return this.__backing_booleanA!.get(); @@ -96,31 +97,31 @@ function main() {} this.__backing_booleanA!.set(value); } - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { - set numA(numA: number | undefined) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set numA(numA: (number | undefined)) - get numA(): number | undefined - set __backing_numA(__backing_numA: StorageLinkDecoratedVariable | undefined) + get numA(): (number | undefined) + set __backing_numA(__backing_numA: (IStorageLinkDecoratedVariable | undefined)) - get __backing_numA(): StorageLinkDecoratedVariable | undefined - set stringA(stringA: string | undefined) + get __backing_numA(): (IStorageLinkDecoratedVariable | undefined) + set stringA(stringA: (string | undefined)) - get stringA(): string | undefined - set __backing_stringA(__backing_stringA: StorageLinkDecoratedVariable | undefined) + get stringA(): (string | undefined) + set __backing_stringA(__backing_stringA: (IStorageLinkDecoratedVariable | undefined)) - get __backing_stringA(): StorageLinkDecoratedVariable | undefined - set booleanA(booleanA: boolean | undefined) + get __backing_stringA(): (IStorageLinkDecoratedVariable | undefined) + set booleanA(booleanA: (boolean | undefined)) - get booleanA(): boolean | undefined - set __backing_booleanA(__backing_booleanA: StorageLinkDecoratedVariable | undefined) + get booleanA(): (boolean | undefined) + set __backing_booleanA(__backing_booleanA: (IStorageLinkDecoratedVariable | undefined)) - get __backing_booleanA(): StorageLinkDecoratedVariable | undefined + get __backing_booleanA(): (IStorageLinkDecoratedVariable | undefined) } @@ -134,7 +135,6 @@ class __EntryWrapper extends EntryPoint { public constructor() {} } - `; function testStorageLinkTransformer(this: PluginTestContext): void { @@ -143,9 +143,9 @@ function testStorageLinkTransformer(this: PluginTestContext): void { pluginTester.run( 'test storagelink primitive type transform', - [storageLinkTransform, uiNoRecheck], + [storageLinkTransform, uiNoRecheck, recheck], { - checked: [testStorageLinkTransformer], + 'checked:ui-no-recheck': [testStorageLinkTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop-ref/storageprop-ref-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop-ref/storageprop-ref-complex-type.test.ts new file mode 100644 index 000000000..c38a8280e --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop-ref/storageprop-ref-complex-type.test.ts @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STORAGEPROP_DIR_PATH: string = 'decorators/storageprop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGEPROP_DIR_PATH, 'storageprop-ref-complex-type.ets'), +]; + +const storagePropTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @StoragePropRef complex type transform', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { StoragePropRef as StoragePropRef } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class Person { + public name: string = ""; + + public constructor(name: string) {} + +} + +final class Status extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly Success: Status = new Status(0, 200); + + public static readonly NotFound: Status = new Status(1, 404); + + public static readonly ServerError: Status = new Status(2, 500); + + private static readonly #NamesArray: String[] = ["Success", "NotFound", "ServerError"]; + + private static readonly #ValuesArray: int[] = [200, 404, 500]; + + private static readonly #StringValuesArray: String[] = ["200", "404", "500"]; + + private static readonly #ItemsArray: Status[] = [Status.Success, Status.NotFound, Status.ServerError]; + + public getName(): String { + return Status.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): Status { + for (let i = 0;((i) < (Status.#NamesArray.length));(++i)) { + if (((name) == (Status.#NamesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum constant Status.") + (name))); + } + + public static fromValue(value: int): Status { + for (let i = 0;((i) < (Status.#ValuesArray.length));(++i)) { + if (((value) == (Status.#ValuesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum Status with value ") + (value))); + } + + public valueOf(): int { + return Status.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return Status.#StringValuesArray[this.#ordinal]; + } + + public static values(): Status[] { + return Status.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: Status): String { + return e.getName(); + } + +} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_arrayB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop1", "arrayB", [1, 2, 3]) + this.__backing_objectB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop2", "objectB", {}) + this.__backing_dateB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop3", "dateB", new Date("2021-09-09")) + this.__backing_setB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop4", "setB", new Set()) + this.__backing_mapB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop5", "mapB", new Map()) + this.__backing_classB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop7", "classB", new Person("Kevin")) + this.__backing_enumB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop8", "enumB", Status.NotFound) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_arrayB?: IStoragePropRefDecoratedVariable>; + + public get arrayB(): Array { + return this.__backing_arrayB!.get(); + } + + public set arrayB(value: Array) { + this.__backing_arrayB!.set(value); + } + + private __backing_objectB?: IStoragePropRefDecoratedVariable; + + public get objectB(): Object { + return this.__backing_objectB!.get(); + } + + public set objectB(value: Object) { + this.__backing_objectB!.set(value); + } + + private __backing_dateB?: IStoragePropRefDecoratedVariable; + + public get dateB(): Date { + return this.__backing_dateB!.get(); + } + + public set dateB(value: Date) { + this.__backing_dateB!.set(value); + } + + private __backing_setB?: IStoragePropRefDecoratedVariable>; + + public get setB(): Set { + return this.__backing_setB!.get(); + } + + public set setB(value: Set) { + this.__backing_setB!.set(value); + } + + private __backing_mapB?: IStoragePropRefDecoratedVariable>; + + public get mapB(): Map { + return this.__backing_mapB!.get(); + } + + public set mapB(value: Map) { + this.__backing_mapB!.set(value); + } + + private __backing_classB?: IStoragePropRefDecoratedVariable; + + public get classB(): Person { + return this.__backing_classB!.get(); + } + + public set classB(value: Person) { + this.__backing_classB!.set(value); + } + + private __backing_enumB?: IStoragePropRefDecoratedVariable; + + public get enumB(): Status { + return this.__backing_enumB!.get(); + } + + public set enumB(value: Status) { + this.__backing_enumB!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set arrayB(arrayB: (Array | undefined)) + + get arrayB(): (Array | undefined) + set __backing_arrayB(__backing_arrayB: (IStoragePropRefDecoratedVariable> | undefined)) + + get __backing_arrayB(): (IStoragePropRefDecoratedVariable> | undefined) + set objectB(objectB: (Object | undefined)) + + get objectB(): (Object | undefined) + set __backing_objectB(__backing_objectB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_objectB(): (IStoragePropRefDecoratedVariable | undefined) + set dateB(dateB: (Date | undefined)) + + get dateB(): (Date | undefined) + set __backing_dateB(__backing_dateB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_dateB(): (IStoragePropRefDecoratedVariable | undefined) + set setB(setB: (Set | undefined)) + + get setB(): (Set | undefined) + set __backing_setB(__backing_setB: (IStoragePropRefDecoratedVariable> | undefined)) + + get __backing_setB(): (IStoragePropRefDecoratedVariable> | undefined) + set mapB(mapB: (Map | undefined)) + + get mapB(): (Map | undefined) + set __backing_mapB(__backing_mapB: (IStoragePropRefDecoratedVariable> | undefined)) + + get __backing_mapB(): (IStoragePropRefDecoratedVariable> | undefined) + set classB(classB: (Person | undefined)) + + get classB(): (Person | undefined) + set __backing_classB(__backing_classB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_classB(): (IStoragePropRefDecoratedVariable | undefined) + set enumB(enumB: (Status | undefined)) + + get enumB(): (Status | undefined) + set __backing_enumB(__backing_enumB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_enumB(): (IStoragePropRefDecoratedVariable | undefined) + +} +`; + +function testStoragePropTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @StoragePropRef complex type transform', + [storagePropTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testStoragePropTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop-ref/storageprop-ref-primitive-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop-ref/storageprop-ref-primitive-type.test.ts new file mode 100644 index 000000000..31d27dd46 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop-ref/storageprop-ref-primitive-type.test.ts @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STORAGEPROP_DIR_PATH: string = 'decorators/storageprop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGEPROP_DIR_PATH, 'storageprop-ref-primitive-type.ets'), +]; + +const storagePropTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @StoragePropRef primitive type transform', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { StoragePropRef as StoragePropRef } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_numB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop1", "numB", 43) + this.__backing_stringB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop2", "stringB", "BB") + this.__backing_booleanB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop3", "booleanB", false) + this.__backing_undefinedB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop4", "undefinedB", undefined) + this.__backing_nullB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop5", "nullB", null) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_numB?: IStoragePropRefDecoratedVariable; + + public get numB(): number { + return this.__backing_numB!.get(); + } + + public set numB(value: number) { + this.__backing_numB!.set(value); + } + + private __backing_stringB?: IStoragePropRefDecoratedVariable; + + public get stringB(): string { + return this.__backing_stringB!.get(); + } + + public set stringB(value: string) { + this.__backing_stringB!.set(value); + } + + private __backing_booleanB?: IStoragePropRefDecoratedVariable; + + public get booleanB(): boolean { + return this.__backing_booleanB!.get(); + } + + public set booleanB(value: boolean) { + this.__backing_booleanB!.set(value); + } + + private __backing_undefinedB?: IStoragePropRefDecoratedVariable; + + public get undefinedB(): undefined { + return this.__backing_undefinedB!.get(); + } + + public set undefinedB(value: undefined) { + this.__backing_undefinedB!.set(value); + } + + private __backing_nullB?: IStoragePropRefDecoratedVariable; + + public get nullB(): null { + return this.__backing_nullB!.get(); + } + + public set nullB(value: null) { + this.__backing_nullB!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set numB(numB: (number | undefined)) + + get numB(): (number | undefined) + set __backing_numB(__backing_numB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_numB(): (IStoragePropRefDecoratedVariable | undefined) + set stringB(stringB: (string | undefined)) + + get stringB(): (string | undefined) + set __backing_stringB(__backing_stringB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_stringB(): (IStoragePropRefDecoratedVariable | undefined) + set booleanB(booleanB: (boolean | undefined)) + + get booleanB(): (boolean | undefined) + set __backing_booleanB(__backing_booleanB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_booleanB(): (IStoragePropRefDecoratedVariable | undefined) + set undefinedB(undefinedB: (undefined | undefined)) + + get undefinedB(): (undefined | undefined) + set __backing_undefinedB(__backing_undefinedB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_undefinedB(): (IStoragePropRefDecoratedVariable | undefined) + set nullB(nullB: (null | undefined)) + + get nullB(): (null | undefined) + set __backing_nullB(__backing_nullB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_nullB(): (IStoragePropRefDecoratedVariable | undefined) + +} +`; + +function testStoragePropTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @StoragePropRef primitive type transform', + [storagePropTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testStoragePropTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts index e00d66d86..374e2187b 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,20 +38,21 @@ const storagePropTransform: Plugins = { const pluginTester = new PluginTester('test storageprop with appstorage', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { StoragePropDecoratedVariable as StoragePropDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { TextAttribute as TextAttribute } from "arkui.component.text"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; +import { NavInterface as NavInterface } from "arkui.UserView"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, Entry as Entry, Column as Column, Text as Text, ClickEvent as ClickEvent } from "@ohos.arkui.component"; @@ -62,6 +64,14 @@ function main() {} AppStorage.setOrCreate("PropA", 47); AppStorage.setOrCreate("PropB", new Data(50)); +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storageprop/storageprop-appstorage", + pageFullPath: "test/demo/mock/decorators/storageprop/storageprop-appstorage", + integratedHsp: "false", + } as NavInterface)); + class Data { public code: number; @@ -71,15 +81,15 @@ class Data { } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class Index extends CustomComponent { - public __initializeStruct(initializers: __Options_Index | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_storageProp = new StoragePropDecoratedVariable("PropA", "storageProp", 1) - this.__backing_storagePropObject = new StoragePropDecoratedVariable("PropB", "storagePropObject", new Data(1)) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_storageProp = STATE_MGMT_FACTORY.makeStoragePropRef(this, "PropA", "storageProp", 1) + this.__backing_storagePropObject = STATE_MGMT_FACTORY.makeStoragePropRef(this, "PropB", "storagePropObject", new Data(1)) } - public __updateStruct(initializers: __Options_Index | undefined): void {} + public __updateStruct(initializers: (__Options_Index | undefined)): void {} - private __backing_storageProp?: StoragePropDecoratedVariable; + private __backing_storageProp?: IStoragePropRefDecoratedVariable; public get storageProp(): number { return this.__backing_storageProp!.get(); @@ -89,7 +99,7 @@ class Data { this.__backing_storageProp!.set(value); } - private __backing_storagePropObject?: StoragePropDecoratedVariable; + private __backing_storagePropObject?: IStoragePropRefDecoratedVariable; public get storagePropObject(): Data { return this.__backing_storagePropObject!.get(); @@ -99,15 +109,15 @@ class Data { this.__backing_storagePropObject!.set(value); } - @memo() public _build(@memo() style: ((instance: Index)=> Index) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Index | undefined): void { - Column(undefined, undefined, (() => { - Text(@memo() ((instance: UITextAttribute): void => { + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { instance.onClick(((e: ClickEvent) => { this.storageProp += 1; })); return; }), \`From AppStorage \${this.storageProp}\`, undefined, undefined); - Text(@memo() ((instance: UITextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { instance.onClick(((e: ClickEvent) => { this.storagePropObject.code += 1; })); @@ -120,19 +130,19 @@ class Data { } -interface __Options_Index { - set storageProp(storageProp: number | undefined) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_Index { + set storageProp(storageProp: (number | undefined)) - get storageProp(): number | undefined - set __backing_storageProp(__backing_storageProp: StoragePropDecoratedVariable | undefined) + get storageProp(): (number | undefined) + set __backing_storageProp(__backing_storageProp: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_storageProp(): StoragePropDecoratedVariable | undefined - set storagePropObject(storagePropObject: Data | undefined) + get __backing_storageProp(): (IStoragePropRefDecoratedVariable | undefined) + set storagePropObject(storagePropObject: (Data | undefined)) - get storagePropObject(): Data | undefined - set __backing_storagePropObject(__backing_storagePropObject: StoragePropDecoratedVariable | undefined) + get storagePropObject(): (Data | undefined) + set __backing_storagePropObject(__backing_storagePropObject: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_storagePropObject(): StoragePropDecoratedVariable | undefined + get __backing_storagePropObject(): (IStoragePropRefDecoratedVariable | undefined) } @@ -146,7 +156,6 @@ class __EntryWrapper extends EntryPoint { public constructor() {} } - `; function testStoragePropTransformer(this: PluginTestContext): void { @@ -155,9 +164,9 @@ function testStoragePropTransformer(this: PluginTestContext): void { pluginTester.run( 'test storageprop with appstorage', - [storagePropTransform, uiNoRecheck], + [storagePropTransform, uiNoRecheck, recheck], { - checked: [testStoragePropTransformer], + 'checked:ui-no-recheck': [testStoragePropTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts index ca212c7ca..65aea99c9 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,25 @@ const storagePropTransform: Plugins = { const pluginTester = new PluginTester('test storageprop complex type transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; - import { memo as memo } from "arkui.stateManagement.runtime"; - -import { StoragePropDecoratedVariable as StoragePropDecoratedVariable } from "@ohos.arkui.stateManagement"; - +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; +import { NavInterface as NavInterface } from "arkui.UserView"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; - import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; - import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; - import { StorageProp as StorageProp } from "@ohos.arkui.stateManagement"; function main() {} - +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storageprop/storageprop-complex-type", + pageFullPath: "test/demo/mock/decorators/storageprop/storageprop-complex-type", + integratedHsp: "false", + } as NavInterface)); class Person { public name: string = ""; @@ -132,21 +133,20 @@ final class Status extends BaseEnum { } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_arrayB = new StoragePropDecoratedVariable>("Prop1", "arrayB", [1, 2, 3]) - this.__backing_objectB = new StoragePropDecoratedVariable("Prop2", "objectB", {}) - this.__backing_dateB = new StoragePropDecoratedVariable("Prop3", "dateB", new Date("2021-09-09")) - this.__backing_setB = new StoragePropDecoratedVariable>("Prop4", "setB", new Set()) - this.__backing_mapB = new StoragePropDecoratedVariable>("Prop5", "mapB", new Map()) - this.__backing_unionB = new StoragePropDecoratedVariable("Prop6", "unionB", "") - this.__backing_classB = new StoragePropDecoratedVariable("Prop7", "classB", new Person("Kevin")) - this.__backing_enumB = new StoragePropDecoratedVariable("Prop8", "enumB", Status.NotFound) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_arrayB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop1", "arrayB", [1, 2, 3]) + this.__backing_objectB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop2", "objectB", {}) + this.__backing_dateB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop3", "dateB", new Date("2021-09-09")) + this.__backing_setB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop4", "setB", new Set()) + this.__backing_mapB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop5", "mapB", new Map()) + this.__backing_classB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop7", "classB", new Person("Kevin")) + this.__backing_enumB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop8", "enumB", Status.NotFound) } - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - private __backing_arrayB?: StoragePropDecoratedVariable>; + private __backing_arrayB?: IStoragePropRefDecoratedVariable>; public get arrayB(): Array { return this.__backing_arrayB!.get(); @@ -156,7 +156,7 @@ final class Status extends BaseEnum { this.__backing_arrayB!.set(value); } - private __backing_objectB?: StoragePropDecoratedVariable; + private __backing_objectB?: IStoragePropRefDecoratedVariable; public get objectB(): Object { return this.__backing_objectB!.get(); @@ -166,7 +166,7 @@ final class Status extends BaseEnum { this.__backing_objectB!.set(value); } - private __backing_dateB?: StoragePropDecoratedVariable; + private __backing_dateB?: IStoragePropRefDecoratedVariable; public get dateB(): Date { return this.__backing_dateB!.get(); @@ -176,7 +176,7 @@ final class Status extends BaseEnum { this.__backing_dateB!.set(value); } - private __backing_setB?: StoragePropDecoratedVariable>; + private __backing_setB?: IStoragePropRefDecoratedVariable>; public get setB(): Set { return this.__backing_setB!.get(); @@ -186,7 +186,7 @@ final class Status extends BaseEnum { this.__backing_setB!.set(value); } - private __backing_mapB?: StoragePropDecoratedVariable>; + private __backing_mapB?: IStoragePropRefDecoratedVariable>; public get mapB(): Map { return this.__backing_mapB!.get(); @@ -196,17 +196,7 @@ final class Status extends BaseEnum { this.__backing_mapB!.set(value); } - private __backing_unionB?: StoragePropDecoratedVariable; - - public get unionB(): string | undefined { - return this.__backing_unionB!.get(); - } - - public set unionB(value: string | undefined) { - this.__backing_unionB!.set(value); - } - - private __backing_classB?: StoragePropDecoratedVariable; + private __backing_classB?: IStoragePropRefDecoratedVariable; public get classB(): Person { return this.__backing_classB!.get(); @@ -216,7 +206,7 @@ final class Status extends BaseEnum { this.__backing_classB!.set(value); } - private __backing_enumB?: StoragePropDecoratedVariable; + private __backing_enumB?: IStoragePropRefDecoratedVariable; public get enumB(): Status { return this.__backing_enumB!.get(); @@ -226,61 +216,55 @@ final class Status extends BaseEnum { this.__backing_enumB!.set(value); } - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { - set arrayB(arrayB: Array | undefined) - - get arrayB(): Array | undefined - set __backing_arrayB(__backing_arrayB: StoragePropDecoratedVariable> | undefined) - - get __backing_arrayB(): StoragePropDecoratedVariable> | undefined - set objectB(objectB: Object | undefined) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set arrayB(arrayB: (Array | undefined)) - get objectB(): Object | undefined - set __backing_objectB(__backing_objectB: StoragePropDecoratedVariable | undefined) + get arrayB(): (Array | undefined) + set __backing_arrayB(__backing_arrayB: (IStoragePropRefDecoratedVariable> | undefined)) - get __backing_objectB(): StoragePropDecoratedVariable | undefined - set dateB(dateB: Date | undefined) + get __backing_arrayB(): (IStoragePropRefDecoratedVariable> | undefined) + set objectB(objectB: (Object | undefined)) - get dateB(): Date | undefined - set __backing_dateB(__backing_dateB: StoragePropDecoratedVariable | undefined) + get objectB(): (Object | undefined) + set __backing_objectB(__backing_objectB: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_dateB(): StoragePropDecoratedVariable | undefined - set setB(setB: Set | undefined) + get __backing_objectB(): (IStoragePropRefDecoratedVariable | undefined) + set dateB(dateB: (Date | undefined)) - get setB(): Set | undefined - set __backing_setB(__backing_setB: StoragePropDecoratedVariable> | undefined) + get dateB(): (Date | undefined) + set __backing_dateB(__backing_dateB: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_setB(): StoragePropDecoratedVariable> | undefined - set mapB(mapB: Map | undefined) + get __backing_dateB(): (IStoragePropRefDecoratedVariable | undefined) + set setB(setB: (Set | undefined)) - get mapB(): Map | undefined - set __backing_mapB(__backing_mapB: StoragePropDecoratedVariable> | undefined) + get setB(): (Set | undefined) + set __backing_setB(__backing_setB: (IStoragePropRefDecoratedVariable> | undefined)) - get __backing_mapB(): StoragePropDecoratedVariable> | undefined - set unionB(unionB: string | undefined | undefined) + get __backing_setB(): (IStoragePropRefDecoratedVariable> | undefined) + set mapB(mapB: (Map | undefined)) - get unionB(): string | undefined | undefined - set __backing_unionB(__backing_unionB: StoragePropDecoratedVariable | undefined) + get mapB(): (Map | undefined) + set __backing_mapB(__backing_mapB: (IStoragePropRefDecoratedVariable> | undefined)) - get __backing_unionB(): StoragePropDecoratedVariable | undefined - set classB(classB: Person | undefined) + get __backing_mapB(): (IStoragePropRefDecoratedVariable> | undefined) + set classB(classB: (Person | undefined)) - get classB(): Person | undefined - set __backing_classB(__backing_classB: StoragePropDecoratedVariable | undefined) + get classB(): (Person | undefined) + set __backing_classB(__backing_classB: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_classB(): StoragePropDecoratedVariable | undefined - set enumB(enumB: Status | undefined) + get __backing_classB(): (IStoragePropRefDecoratedVariable | undefined) + set enumB(enumB: (Status | undefined)) - get enumB(): Status | undefined - set __backing_enumB(__backing_enumB: StoragePropDecoratedVariable | undefined) + get enumB(): (Status | undefined) + set __backing_enumB(__backing_enumB: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_enumB(): StoragePropDecoratedVariable | undefined + get __backing_enumB(): (IStoragePropRefDecoratedVariable | undefined) } @@ -302,9 +286,9 @@ function testStoragePropTransformer(this: PluginTestContext): void { pluginTester.run( 'test storageprop complex type transform', - [storagePropTransform, uiNoRecheck], + [storagePropTransform, uiNoRecheck, recheck], { - checked: [testStoragePropTransformer], + 'checked:ui-no-recheck': [testStoragePropTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts index d6247f205..660a51361 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,16 +38,19 @@ const storagePropTransform: Plugins = { const pluginTester = new PluginTester('test storageprop primitive type transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { memo as memo } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; -import { memo as memo } from "arkui.stateManagement.runtime"; +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { NavInterface as NavInterface } from "arkui.UserView"; -import { StoragePropDecoratedVariable as StoragePropDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; @@ -55,18 +59,24 @@ import { StorageProp as StorageProp } from "@ohos.arkui.stateManagement"; function main() {} - - -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_numB = new StoragePropDecoratedVariable("Prop1", "numB", 43) - this.__backing_stringB = new StoragePropDecoratedVariable("Prop2", "stringB", "BB") - this.__backing_booleanB = new StoragePropDecoratedVariable("Prop3", "booleanB", false) +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storageprop/storageprop-primitive-type", + pageFullPath: "test/demo/mock/decorators/storageprop/storageprop-primitive-type", + integratedHsp: "false", + } as NavInterface)); + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_numB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop1", "numB", 43) + this.__backing_stringB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop2", "stringB", "BB") + this.__backing_booleanB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop3", "booleanB", false) } - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - private __backing_numB?: StoragePropDecoratedVariable; + private __backing_numB?: IStoragePropRefDecoratedVariable; public get numB(): number { return this.__backing_numB!.get(); @@ -76,7 +86,7 @@ function main() {} this.__backing_numB!.set(value); } - private __backing_stringB?: StoragePropDecoratedVariable; + private __backing_stringB?: IStoragePropRefDecoratedVariable; public get stringB(): string { return this.__backing_stringB!.get(); @@ -86,7 +96,7 @@ function main() {} this.__backing_stringB!.set(value); } - private __backing_booleanB?: StoragePropDecoratedVariable; + private __backing_booleanB?: IStoragePropRefDecoratedVariable; public get booleanB(): boolean { return this.__backing_booleanB!.get(); @@ -96,31 +106,31 @@ function main() {} this.__backing_booleanB!.set(value); } - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { - set numB(numB: number | undefined) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set numB(numB: (number | undefined)) - get numB(): number | undefined - set __backing_numB(__backing_numB: StoragePropDecoratedVariable | undefined) + get numB(): (number | undefined) + set __backing_numB(__backing_numB: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_numB(): StoragePropDecoratedVariable | undefined - set stringB(stringB: string | undefined) + get __backing_numB(): (IStoragePropRefDecoratedVariable | undefined) + set stringB(stringB: (string | undefined)) - get stringB(): string | undefined - set __backing_stringB(__backing_stringB: StoragePropDecoratedVariable | undefined) + get stringB(): (string | undefined) + set __backing_stringB(__backing_stringB: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_stringB(): StoragePropDecoratedVariable | undefined - set booleanB(booleanB: boolean | undefined) + get __backing_stringB(): (IStoragePropRefDecoratedVariable | undefined) + set booleanB(booleanB: (boolean | undefined)) - get booleanB(): boolean | undefined - set __backing_booleanB(__backing_booleanB: StoragePropDecoratedVariable | undefined) + get booleanB(): (boolean | undefined) + set __backing_booleanB(__backing_booleanB: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_booleanB(): StoragePropDecoratedVariable | undefined + get __backing_booleanB(): (IStoragePropRefDecoratedVariable | undefined) } @@ -142,9 +152,9 @@ function testStoragePropTransformer(this: PluginTestContext): void { pluginTester.run( 'test storageprop primitive type transform', - [storagePropTransform, uiNoRecheck], + [storagePropTransform, uiNoRecheck, recheck], { - checked: [testStoragePropTransformer], + 'checked:ui-no-recheck': [testStoragePropTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts index fbb612c70..f38b5f6d6 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,58 +38,65 @@ const watchTransform: Plugins = { const pluginTester = new PluginTester('test basic watch transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { IConsumeDecoratedVariable as IConsumeDecoratedVariable } from "arkui.stateManagement.decorator"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; -import { memo as memo } from "arkui.stateManagement.runtime"; +import { IObjectLinkDecoratedVariable as IObjectLinkDecoratedVariable } from "arkui.stateManagement.decorator"; -import { ConsumeDecoratedVariable as ConsumeDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; -import { ProvideDecoratedVariable as ProvideDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { IStorageLinkDecoratedVariable as IStorageLinkDecoratedVariable } from "arkui.stateManagement.decorator"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { memo as memo } from "arkui.stateManagement.runtime"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { ObjectLinkDecoratedVariable as ObjectLinkDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { DecoratedV1VariableBase as DecoratedV1VariableBase } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { LinkDecoratedVariable as LinkDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { StoragePropDecoratedVariable as StoragePropDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { StorageLinkDecoratedVariable as StorageLinkDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; -import { PropDecoratedVariable as PropDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { NavInterface as NavInterface } from "arkui.UserView"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; -import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; +import { Component as Component, Entry as Entry, Column as Column } from "@ohos.arkui.component"; import { State as State, Prop as Prop, StorageLink as StorageLink, StorageProp as StorageProp, Link as Link, Watch as Watch, ObjectLink as ObjectLink, Observed as Observed, Track as Track, Provide as Provide, Consume as Consume } from "@ohos.arkui.stateManagement"; function main() {} +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/watch/watch-basic", + pageFullPath: "test/demo/mock/decorators/watch/watch-basic", + integratedHsp: "false", + } as NavInterface)); - -@Observed() class A implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class A implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -102,63 +110,85 @@ function main() {} this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } public propA: string = "hello"; - private __backing_trackA: string = "world"; + @JSONRename({newName:"trackA"}) private __backing_trackA: string = "world"; - private __meta_trackA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONStringifyIgnore() private __meta_trackA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); public constructor() {} public get trackA(): string { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_trackA.addRef(); - } + this.conditionalAddRef(this.__meta_trackA); return this.__backing_trackA; } public set trackA(newValue: string) { if (((this.__backing_trackA) !== (newValue))) { this.__backing_trackA = newValue; - this.__meta_trackA.fireChange(); - this.executeOnSubscribingWatches("trackA"); + this.__meta_trackA.fireChange(); + this.executeOnSubscribingWatches("trackA"); } } } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_statevar = new StateDecoratedVariable("statevar", ((({let gensym___76198660 = initializers; +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_statevar = STATE_MGMT_FACTORY.makeState(this, "statevar", ((({let gensym___76198660 = initializers; (((gensym___76198660) == (null)) ? undefined : gensym___76198660.statevar)})) ?? ("Hello World")), ((_: string): void => { this.stateOnChange(_); })); - this.__backing_propvar = new PropDecoratedVariable("propvar", ((({let gensym___241486692 = initializers; + this.__backing_propvar = STATE_MGMT_FACTORY.makeProp(this, "propvar", ((({let gensym___241486692 = initializers; (((gensym___241486692) == (null)) ? undefined : gensym___241486692.propvar)})) ?? ("Hello World")), ((_: string): void => { this.propOnChange(_); })); - this.__backing_storagelinkvar = new StorageLinkDecoratedVariable("prop1", "storagelinkvar", "Hello World", ((_: string): void => { + if (({let gensym___165820150 = initializers; + (((gensym___165820150) == (null)) ? undefined : gensym___165820150.__backing_linkvar)})) { + this.__backing_linkvar = STATE_MGMT_FACTORY.makeLink(this, "linkvar", initializers!.__backing_linkvar!, ((_: string): void => { + this.linkOnChange(_); + })); + }; + this.__backing_storagelinkvar = STATE_MGMT_FACTORY.makeStorageLink(this, "prop1", "storagelinkvar", "Hello World", ((_: string): void => { this.storageLinkOnChange(_); })) - this.__backing_storagepropvar = new StoragePropDecoratedVariable("prop2", "storagepropvar", "Hello World", ((_: string): void => { + this.__backing_storagepropvar = STATE_MGMT_FACTORY.makeStoragePropRef(this, "prop2", "storagepropvar", "Hello World", ((_: string): void => { this.storagePropOnChange(_); })) - this.__backing_providevar = this.addProvidedVar("providevar", "providevar", ((({let gensym___194235814 = initializers; - (((gensym___194235814) == (null)) ? undefined : gensym___194235814.providevar)})) ?? ("Hello World")), false, ((_: string): void => { + this.__backing_objectlinkvar = STATE_MGMT_FACTORY.makeObjectLink(this, "objectlinkvar", (({let gensym___172556967 = initializers; + (((gensym___172556967) == (null)) ? undefined : gensym___172556967.objectlinkvar)}) as A), ((_: string): void => { + this.objectLinkOnChange(_); + })) + this.__backing_providevar = STATE_MGMT_FACTORY.makeProvide(this, "providevar", "providevar", ((({let gensym___244584558 = initializers; + (((gensym___244584558) == (null)) ? undefined : gensym___244584558.providevar)})) ?? ("Hello World")), false, ((_: string): void => { this.ProvideOnChange(_); })); } - public __updateStruct(initializers: __Options_MyStateSample | undefined): void { + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void { if (((({let gensym___220608839 = initializers; (((gensym___220608839) == (null)) ? undefined : gensym___220608839.propvar)})) !== (undefined))) { this.__backing_propvar!.update((initializers!.propvar as string)); } + if (((({let gensym___164966179 = initializers; + (((gensym___164966179) == (null)) ? undefined : gensym___164966179.objectlinkvar)})) !== (undefined))) { + this.__backing_objectlinkvar!.update(initializers!.objectlinkvar!); + } } - private __backing_statevar?: StateDecoratedVariable; + private __backing_statevar?: IStateDecoratedVariable; public get statevar(): string { return this.__backing_statevar!.get(); @@ -168,7 +198,7 @@ function main() {} this.__backing_statevar!.set(value); } - private __backing_propvar?: PropDecoratedVariable; + private __backing_propvar?: IPropDecoratedVariable; public get propvar(): string { return this.__backing_propvar!.get(); @@ -178,7 +208,17 @@ function main() {} this.__backing_propvar!.set(value); } - private __backing_storagelinkvar?: StorageLinkDecoratedVariable; + private __backing_linkvar?: ILinkDecoratedVariable; + + public get linkvar(): string { + return this.__backing_linkvar!.get(); + } + + public set linkvar(value: string) { + this.__backing_linkvar!.set(value); + } + + private __backing_storagelinkvar?: IStorageLinkDecoratedVariable; public get storagelinkvar(): string { return this.__backing_storagelinkvar!.get(); @@ -188,7 +228,7 @@ function main() {} this.__backing_storagelinkvar!.set(value); } - private __backing_storagepropvar?: StoragePropDecoratedVariable; + private __backing_storagepropvar?: IStoragePropRefDecoratedVariable; public get storagepropvar(): string { return this.__backing_storagepropvar!.get(); @@ -198,7 +238,13 @@ function main() {} this.__backing_storagepropvar!.set(value); } - private __backing_providevar?: ProvideDecoratedVariable; + private __backing_objectlinkvar?: IObjectLinkDecoratedVariable; + + public get objectlinkvar(): A { + return this.__backing_objectlinkvar!.get(); + } + + private __backing_providevar?: IProvideDecoratedVariable; public get providevar(): string { return this.__backing_providevar!.get(); @@ -212,68 +258,110 @@ function main() {} public propOnChange(propName: string) {} + public linkOnChange(propName: string) {} + public storageLinkOnChange(propName: string) {} public storagePropOnChange(propName: string) {} + public objectLinkOnChange(propName: string) {} + public ProvideOnChange(propName: string) {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void { - Child._instantiateImpl(undefined, (() => { - return new Child(); - }), undefined, undefined, undefined); + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), undefined, undefined, undefined); + })); } public constructor() {} } -@Component({freezeWhenInactive:false}) final class Child extends CustomComponent { - public __initializeStruct(initializers: __Options_Child | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_providevar = STATE_MGMT_FACTORY.makeConsume(this, "providevar", "providevar", ((_: string): void => { + this.ConsumeOnChange(_); + })); + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_providevar?: IConsumeDecoratedVariable; + + public get providevar(): string { + return this.__backing_providevar!.get(); + } + + public set providevar(value: string) { + this.__backing_providevar!.set(value); + } - public __updateStruct(initializers: __Options_Child | undefined): void {} + public ConsumeOnChange(propName: string) {} - @memo() public _build(@memo() style: ((instance: Child)=> Child) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Child | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { - set statevar(statevar: string | undefined) +@Retention({policy:"SOURCE"}) @interface __Link_intrinsic {} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set statevar(statevar: (string | undefined)) + + get statevar(): (string | undefined) + @Watch({value:"stateOnChange"}) set __backing_statevar(__backing_statevar: (IStateDecoratedVariable | undefined)) + + @Watch({value:"stateOnChange"}) get __backing_statevar(): (IStateDecoratedVariable | undefined) + set propvar(propvar: (string | undefined)) - get statevar(): string | undefined - set __backing_statevar(__backing_statevar: StateDecoratedVariable | undefined) + get propvar(): (string | undefined) + @Watch({value:"propOnChange"}) set __backing_propvar(__backing_propvar: (IPropDecoratedVariable | undefined)) - get __backing_statevar(): StateDecoratedVariable | undefined - set propvar(propvar: string | undefined) + @Watch({value:"propOnChange"}) get __backing_propvar(): (IPropDecoratedVariable | undefined) + @__Link_intrinsic() set linkvar(linkvar: (string | undefined)) - get propvar(): string | undefined - set __backing_propvar(__backing_propvar: PropDecoratedVariable | undefined) + @__Link_intrinsic() get linkvar(): (string | undefined) + @Watch({value:"linkOnChange"}) set __backing_linkvar(__backing_linkvar: (LinkSourceType | undefined)) - get __backing_propvar(): PropDecoratedVariable | undefined - set storagelinkvar(storagelinkvar: string | undefined) + @Watch({value:"linkOnChange"}) get __backing_linkvar(): (LinkSourceType | undefined) + set storagelinkvar(storagelinkvar: (string | undefined)) - get storagelinkvar(): string | undefined - set __backing_storagelinkvar(__backing_storagelinkvar: StorageLinkDecoratedVariable | undefined) + get storagelinkvar(): (string | undefined) + @Watch({value:"storageLinkOnChange"}) set __backing_storagelinkvar(__backing_storagelinkvar: (IStorageLinkDecoratedVariable | undefined)) - get __backing_storagelinkvar(): StorageLinkDecoratedVariable | undefined - set storagepropvar(storagepropvar: string | undefined) + @Watch({value:"storageLinkOnChange"}) get __backing_storagelinkvar(): (IStorageLinkDecoratedVariable | undefined) + set storagepropvar(storagepropvar: (string | undefined)) - get storagepropvar(): string | undefined - set __backing_storagepropvar(__backing_storagepropvar: StoragePropDecoratedVariable | undefined) + get storagepropvar(): (string | undefined) + @Watch({value:"storagePropOnChange"}) set __backing_storagepropvar(__backing_storagepropvar: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_storagepropvar(): StoragePropDecoratedVariable | undefined - set providevar(providevar: string | undefined) + @Watch({value:"storagePropOnChange"}) get __backing_storagepropvar(): (IStoragePropRefDecoratedVariable | undefined) + set objectlinkvar(objectlinkvar: (A | undefined)) - get providevar(): string | undefined - set __backing_providevar(__backing_providevar: ProvideDecoratedVariable | undefined) + get objectlinkvar(): (A | undefined) + @Watch({value:"objectLinkOnChange"}) set __backing_objectlinkvar(__backing_objectlinkvar: (IObjectLinkDecoratedVariable | undefined)) - get __backing_providevar(): ProvideDecoratedVariable | undefined + @Watch({value:"objectLinkOnChange"}) get __backing_objectlinkvar(): (IObjectLinkDecoratedVariable | undefined) + set providevar(providevar: (string | undefined)) + + get providevar(): (string | undefined) + @Watch({value:"ProvideOnChange"}) set __backing_providevar(__backing_providevar: (IProvideDecoratedVariable | undefined)) + + @Watch({value:"ProvideOnChange"}) get __backing_providevar(): (IProvideDecoratedVariable | undefined) } -interface __Options_Child { +@Component() export interface __Options_Child { + set providevar(providevar: (string | undefined)) + + get providevar(): (string | undefined) + @Watch({value:"ConsumeOnChange"}) set __backing_providevar(__backing_providevar: (IConsumeDecoratedVariable | undefined)) + + @Watch({value:"ConsumeOnChange"}) get __backing_providevar(): (IConsumeDecoratedVariable | undefined) } @@ -287,7 +375,6 @@ class __EntryWrapper extends EntryPoint { public constructor() {} } - `; function testWatchTransformer(this: PluginTestContext): void { @@ -296,9 +383,9 @@ function testWatchTransformer(this: PluginTestContext): void { pluginTester.run( 'test basic watch transform', - [watchTransform, uiNoRecheck], + [watchTransform, uiNoRecheck, recheck], { - checked: [testWatchTransformer], + 'checked:ui-no-recheck': [testWatchTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-griditem.test.ts b/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-griditem.test.ts new file mode 100644 index 000000000..570b3cf1a --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-griditem.test.ts @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const DOUBLE_DOLLAR_DIR_PATH: string = 'double-dollar'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, DOUBLE_DOLLAR_DIR_PATH, 'double-dollar-griditem.ets'), +]; + +const pluginTester = new PluginTester('test griditem bindable capability', buildConfig); + +const parsedTransform: Plugins = { + name: 'double-dollar-griditem', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { GridItemAttribute as GridItemAttribute } from "arkui.component.gridItem"; + +import { Bindable as Bindable } from "arkui.component.common"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Text as Text, Entry as Entry, Column as Column, Component as Component, $$ as $$, Grid as Grid, GridItem as GridItem } from "@ohos.arkui.component"; + +import { State as State } from "@ohos.arkui.stateManagement"; + +let c: boolean; + +function main() {} + +c = false; + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../double-dollar/double-dollar-griditem", + pageFullPath: "test/demo/mock/double-dollar/double-dollar-griditem", + integratedHsp: "false", + } as NavInterface)); + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_boo = STATE_MGMT_FACTORY.makeState(this, "boo", ((({let gensym___9142460 = initializers; + (((gensym___9142460) == (null)) ? undefined : gensym___9142460.boo)})) ?? (true))); + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_boo?: IStateDecoratedVariable; + + public get boo(): boolean { + return this.__backing_boo!.get(); + } + + public set boo(value: boolean) { + this.__backing_boo!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Grid(undefined, undefined, undefined, @memo() (() => { + GridItem(@memo() ((instance: GridItemAttribute): void => { + instance.selected(({ + value: this.boo, + onChange: ((value: boolean) => { + this.boo = value; + }), + } as Bindable)); + return; + }), undefined, @memo() (() => { + Text(undefined, "nihao", undefined, undefined); + })); + GridItem(@memo() ((instance: GridItemAttribute): void => { + instance.selected(({ + value: c, + onChange: ((value: boolean) => { + c = value; + }), + } as Bindable)); + return; + }), undefined, @memo() (() => { + Text(undefined, "nihao", undefined, undefined); + })); + })); + })); + } + + public constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set boo(boo: (boolean | undefined)) + + get boo(): (boolean | undefined) + set __backing_boo(__backing_boo: (IStateDecoratedVariable | undefined)) + + get __backing_boo(): (IStateDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test griditem bindable capability', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-toggle.test.ts b/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-toggle.test.ts new file mode 100644 index 000000000..2688f3be5 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-toggle.test.ts @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const DOUBLE_DOLLAR_DIR_PATH: string = 'double-dollar'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, DOUBLE_DOLLAR_DIR_PATH, 'double-dollar-toggle.ets'), +]; + +const pluginTester = new PluginTester('test toggle bindable capability', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { Bindable as Bindable } from "arkui.component.common"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Text as Text, Column as Column, Component as Component, $$ as $$, Toggle as Toggle, ToggleType as ToggleType } from "@ohos.arkui.component"; + +import { State as State } from "@ohos.arkui.stateManagement"; + +let c: Array; + +function main() {} + +c = [true, false, true]; + +class BooleanClass { + public isOn: boolean = true; + + public constructor() {} + +} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_boo = STATE_MGMT_FACTORY.makeState>(this, "boo", ((({let gensym___9142460 = initializers; + (((gensym___9142460) == (null)) ? undefined : gensym___9142460.boo)})) ?? ([true, false, true]))); + this.__backing_booClass = STATE_MGMT_FACTORY.makeState(this, "booClass", ((({let gensym___145381365 = initializers; + (((gensym___145381365) == (null)) ? undefined : gensym___145381365.booClass)})) ?? (new BooleanClass()))); + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_boo?: IStateDecoratedVariable>; + + public get boo(): Array { + return this.__backing_boo!.get(); + } + + public set boo(value: Array) { + this.__backing_boo!.set(value); + } + + private __backing_booClass?: IStateDecoratedVariable; + + public get booClass(): BooleanClass { + return this.__backing_booClass!.get(); + } + + public set booClass(value: BooleanClass) { + this.__backing_booClass!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Toggle(undefined, { + type: ToggleType.Checkbox, + isOn: ({ + value: this.boo[0], + onChange: ((value: boolean) => { + this.boo[0] = value; + }), + } as Bindable), + }, undefined); + Toggle(undefined, { + type: ToggleType.Checkbox, + isOn: ({ + value: this.booClass.isOn, + onChange: ((value: boolean) => { + this.booClass.isOn = value; + }), + } as Bindable), + }, undefined); + Toggle(undefined, { + type: ToggleType.Checkbox, + isOn: ({ + value: c[1], + onChange: ((value: boolean) => { + c[1] = value; + }), + } as Bindable), + }, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set boo(boo: (Array | undefined)) + + get boo(): (Array | undefined) + set __backing_boo(__backing_boo: (IStateDecoratedVariable> | undefined)) + + get __backing_boo(): (IStateDecoratedVariable> | undefined) + set booClass(booClass: (BooleanClass | undefined)) + + get booClass(): (BooleanClass | undefined) + set __backing_booClass(__backing_booClass: (IStateDecoratedVariable | undefined)) + + get __backing_booClass(): (IStateDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test toggle bindable capability', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/entry-only.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/entry-only.test.ts new file mode 100644 index 000000000..98c87cc96 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/entry-only.test.ts @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'entry-only.ets'), +]; + +const pluginTester = new PluginTester('test entry only', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry only', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +@Entry() @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() {} + +} + +@Entry() @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/entry-only", + pageFullPath: "test/demo/mock/entry/entry-only", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry only', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-false.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-false.test.ts new file mode 100644 index 000000000..23daf7f6f --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-false.test.ts @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry/localstorage'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'storage-use-shared-storage-false.ets'), +]; + +const pluginTester = new PluginTester('test entry with storage and useSharedStorage false', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry with storage', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorage as LocalStorage } from "@ohos.arkui.stateManagement"; + +const myStorage: (()=> LocalStorage) = (() => new LocalStorage()) +@Entry({storage:"myStorage",useSharedStorage:false}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() { + super(false, myStorage()); + } + +} + +@Entry({storage:"myStorage",useSharedStorage:false}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/localstorage/storage-use-shared-storage-false", + pageFullPath: "test/demo/mock/entry/localstorage/storage-use-shared-storage-false", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry with storage and useSharedStorage false', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-true.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-true.test.ts new file mode 100644 index 000000000..35a317289 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-true.test.ts @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry/localstorage'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'storage-use-shared-storage-true.ets'), +]; + +const pluginTester = new PluginTester('test entry with storage and useSharedStorage true', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry with storage', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorage as LocalStorage } from "@ohos.arkui.stateManagement"; + +const myStorage: (()=> LocalStorage) = (() => new LocalStorage()) +@Entry({storage:"myStorage",useSharedStorage:true}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() { + super(true, myStorage()); + } + +} + +@Entry({storage:"myStorage",useSharedStorage:true}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/localstorage/storage-use-shared-storage-true", + pageFullPath: "test/demo/mock/entry/localstorage/storage-use-shared-storage-true", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry with storage and useSharedStorage true', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage.test.ts new file mode 100644 index 000000000..54549712e --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage.test.ts @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry/localstorage'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'storage.ets'), +]; + +const pluginTester = new PluginTester('test entry with only storage', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry with storage', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorage as LocalStorage } from "@ohos.arkui.stateManagement"; + +const myStorage: (()=> LocalStorage) = (() => new LocalStorage()) +@Entry({storage:"myStorage"}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() { + super(false, myStorage()); + } + +} + +@Entry({storage:"myStorage"}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/localstorage/storage", + pageFullPath: "test/demo/mock/entry/localstorage/storage", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryStorageTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry with only storage', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryStorageTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-false.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-false.test.ts new file mode 100644 index 000000000..0a75f6608 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-false.test.ts @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry/localstorage'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'use-shared-storage-false.ets'), +]; + +const pluginTester = new PluginTester('test entry with only useSharedStorage false', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry with storage', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorage as LocalStorage } from "@ohos.arkui.stateManagement"; + +const myStorage: (()=> LocalStorage) = (() => new LocalStorage()) +@Entry({useSharedStorage:false}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() { + super(false, undefined); + } + +} + +@Entry({useSharedStorage:false}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/localstorage/use-shared-storage-false", + pageFullPath: "test/demo/mock/entry/localstorage/use-shared-storage-false", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry with only useSharedStorage false', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-true.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-true.test.ts new file mode 100644 index 000000000..523129df9 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-true.test.ts @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry/localstorage'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'use-shared-storage-true.ets'), +]; + +const pluginTester = new PluginTester('test entry with only useSharedStorage true', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry with storage', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorage as LocalStorage } from "@ohos.arkui.stateManagement"; + +const myStorage: (()=> LocalStorage) = (() => new LocalStorage()) +@Entry({useSharedStorage:true}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() { + super(true, undefined); + } + +} + +@Entry({useSharedStorage:true}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/localstorage/use-shared-storage-true", + pageFullPath: "test/demo/mock/entry/localstorage/use-shared-storage-true", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry with only useSharedStorage true', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name-storage-shared.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name-storage-shared.test.ts new file mode 100644 index 000000000..104404f1c --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name-storage-shared.test.ts @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry/route-name'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'route-name-storage-shared.ets'), +]; + +const pluginTester = new PluginTester('test entry with only routeName', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry with routeName, storage, and useSharedStorage', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorage as LocalStorage } from "@ohos.arkui.stateManagement"; + +const myStorage: (()=> LocalStorage) = (() => new LocalStorage()) + +@Entry({routeName:"MyPage",storage:"myStorage",useSharedStorage:true}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() { + super(true, myStorage()); + } + +} + +@Entry({routeName:"MyPage",storage:"myStorage",useSharedStorage:true}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("MyPage", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/route-name/route-name-storage-shared", + pageFullPath: "test/demo/mock/entry/route-name/route-name-storage-shared", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry with only routeName', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name.test.ts new file mode 100644 index 000000000..9d0ee8240 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name.test.ts @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry/route-name'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'route-name.ets'), +]; + +const pluginTester = new PluginTester('test entry with only routeName', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry with routeName', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + + +@Entry({routeName:"MyPage"}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() {} + +} + +@Entry({routeName:"MyPage"}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("MyPage", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/route-name/route-name", + pageFullPath: "test/demo/mock/entry/route-name/route-name", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry with only routeName', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/imports/import-struct.test.ts b/arkui-plugins/test/ut/ui-plugins/imports/import-struct.test.ts new file mode 100644 index 000000000..f72ccb65e --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/imports/import-struct.test.ts @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const IMPORT_DIR_PATH: string = 'imports'; +const IMPORT_UTILS_DIR_PATH: string = 'imports/utils'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, IMPORT_DIR_PATH, 'import-struct.ets'), + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, IMPORT_UTILS_DIR_PATH, 'simple-struct.ets') +]; + +const importParsed: Plugins = { + name: 'import-parsed', + parsed: uiTransform().parsed, +}; + +const pluginTester = new PluginTester('test import transform', buildConfig); + +const expectedParsedScript: string = ` +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text } from "@ohos.arkui.component"; + +import { SimpleStruct as SimpleStruct } from "./utils/simple-struct"; + +@Component() final struct ImportStruct extends CustomComponent { + public build() { + SimpleStruct(); + SimpleStruct({ + message: "str1", + }); + SimpleStruct(){ + Text("a"); + }; + } + + public constructor() {} + +} + +@Component() export interface __Options_ImportStruct { + +} +`; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text } from "@ohos.arkui.component"; + +import { SimpleStruct as SimpleStruct } from "./utils/simple-struct"; + +function main() {} + + + +@Component() final struct ImportStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_ImportStruct | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_ImportStruct | undefined)): void {} + + @memo() public build() { + SimpleStruct._instantiateImpl(undefined, (() => { + return new SimpleStruct(); + }), undefined, undefined, undefined); + SimpleStruct._instantiateImpl(undefined, (() => { + return new SimpleStruct(); + }), { + message: "str1", + }, undefined, undefined); + SimpleStruct._instantiateImpl(undefined, (() => { + return new SimpleStruct(); + }), undefined, undefined, @memo() (() => { + Text(undefined, "a", undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_ImportStruct { + +} +`; + +function testImportParsed(this: PluginTestContext): void { + expect(parseDumpSrc(this?.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testImportChecked(this: PluginTestContext): void { + expect(parseDumpSrc(this?.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test import struct from another file', + [importParsed, uiNoRecheck, recheck], + { + 'parsed:import-struct': [testImportParsed], + 'checked:ui-no-recheck:import-struct': [testImportChecked], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts b/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts new file mode 100644 index 000000000..62b772f7a --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const IMPORT_DIR_PATH: string = 'imports'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, IMPORT_DIR_PATH, 'kit-import.ets'), +]; + +const importParsed: Plugins = { + name: 'import-parsed', + parsed: uiTransform().parsed, +}; + +const pluginTester = new PluginTester('test import transform', buildConfig); + +const expectedParsedScript: string = ` +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Prop as Prop, Column as Column, Entry as Entry } from "@kit.ArkUI"; + +import { Text as Text, Component as Component, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { State as State } from "@ohos.arkui.stateManagement"; + +import { Button as Button } from "arkui.component.button"; + +import hilog from "@ohos.hilog"; + +@Entry() @Component() final struct A extends CustomComponent implements PageLifeCycle { + @State() public a: string = "str"; + + @Prop() public b!: string; + + public build() { + Column(){ + Button("button").onClick(((e: ClickEvent) => {})); + Text("text").fontSize(20); + }; + } + + public constructor() {} + +} + +@Entry() @Component() export interface __Options_A { + a?: string; + @State() __backing_a?: string; + b?: string; + @Prop() __backing_b?: string; + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + A(); + } + + public constructor() {} + +} +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../imports/kit-import", + pageFullPath: "test/demo/mock/imports/kit-import", + integratedHsp: "false", + } as NavInterface)) +`; + +const expectedCheckedScript: string = ` + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Prop as Prop, Column as Column, Entry as Entry } from "@kit.ArkUI"; + +import { Text as Text, Component as Component, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { State as State } from "@ohos.arkui.stateManagement"; + +import { Button as Button } from "arkui.component.button"; + +import hilog from "@ohos.hilog"; + +function main() {} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../imports/kit-import", + pageFullPath: "test/demo/mock/imports/kit-import", + integratedHsp: "false", + } as NavInterface)); + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct A extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_A | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_a = STATE_MGMT_FACTORY.makeState(this, "a", ((({let gensym___94024326 = initializers; + (((gensym___94024326) == (null)) ? undefined : gensym___94024326.a)})) ?? ("str"))); + this.__backing_b = STATE_MGMT_FACTORY.makeProp(this, "b", (initializers!.b as string)); + } + + public __updateStruct(initializers: (__Options_A | undefined)): void { + if (((({let gensym___81454501 = initializers; + (((gensym___81454501) == (null)) ? undefined : gensym___81454501.b)})) !== (undefined))) { + this.__backing_b!.update((initializers!.b as string)); + } + } + + private __backing_a?: IStateDecoratedVariable; + + public get a(): string { + return this.__backing_a!.get(); + } + + public set a(value: string) { + this.__backing_a!.set(value); + } + + private __backing_b?: IPropDecoratedVariable; + + public get b(): string { + return this.__backing_b!.get(); + } + + public set b(value: string) { + this.__backing_b!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => {})); + return; + }), "button", undefined, undefined); + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(20); + return; + }), "text", undefined, undefined); + })); + } + + public constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_A { + set a(a: (string | undefined)) + + get a(): (string | undefined) + set __backing_a(__backing_a: (IStateDecoratedVariable | undefined)) + + get __backing_a(): (IStateDecoratedVariable | undefined) + set b(b: (string | undefined)) + + get b(): (string | undefined) + set __backing_b(__backing_b: (IPropDecoratedVariable | undefined)) + + get __backing_b(): (IPropDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + A._instantiateImpl(undefined, (() => { + return new A(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testImportParsed(this: PluginTestContext): void { + expect(parseDumpSrc(this?.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testImportChecked(this: PluginTestContext): void { + expect(parseDumpSrc(this?.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test imports from different sources', + [importParsed, uiNoRecheck, recheck], + { + parsed: [testImportParsed], + 'checked:ui-no-recheck': [testImportChecked], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/wrap-builder/builder-in-generic.test.ts b/arkui-plugins/test/ut/ui-plugins/wrap-builder/builder-in-generic.test.ts new file mode 100644 index 000000000..c8334a9c9 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/builder-in-generic.test.ts @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { uiNoRecheck, recheck, memoNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const WRAP_BUILDER_DIR_PATH: string = 'wrap-builder'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, WRAP_BUILDER_DIR_PATH, 'builder-in-generic.ets'), +]; + +const pluginTester = new PluginTester('test builder with generic builder type', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsed transform', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from \"arkui.stateManagement.decorator\"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from \"arkui.stateManagement.decorator\"; +import { RowAttribute as RowAttribute } from \"arkui.component.row\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { TextAttribute as TextAttribute } from \"arkui.component.text\"; +import { NavInterface as NavInterface } from \"arkui.UserView\"; +import { PageLifeCycle as PageLifeCycle } from \"arkui.component.customComponent\"; +import { EntryPoint as EntryPoint } from \"arkui.UserView\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Builder as Builder, Text as Text, Color as Color, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Entry as Entry, Component as Component, Row as Row, ForEach as ForEach } from \"@ohos.arkui.component\"; +import { State as State } from \"@ohos.arkui.stateManagement\"; +@memo() let globalBuilder: @Builder() ((value: string, size: number)=> void); +let builderArr: Array<@Builder() ((value: string, size: number)=> void)>; +function main() {} +@memo() function MyBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size); + return; + }), value, undefined, undefined); +} +@memo() function YourBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size).fontColor(Color.Pink); + return; + }), value, undefined, undefined); +} +globalBuilder = MyBuilder; +builderArr = [MyBuilder, YourBuilder]; +__EntryWrapper.RegisterNamedRouter(\"\", new __EntryWrapper(), ({ + bundleName: \"com.example.mock\", + moduleName: \"entry\", + pagePath: \"../../../wrap-builder/builder-in-generic\", + pageFullPath: \"test/demo/mock/wrap-builder/builder-in-generic\", + integratedHsp: \"false\", +} as NavInterface)); +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_message = STATE_MGMT_FACTORY.makeState(this, \"message\", ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.message)})) ?? (\"Hello World\"))); + } + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + private __backing_message?: IStateDecoratedVariable; + public get message(): string { + return this.__backing_message!.get(); + } + public set message(value: string) { + this.__backing_message!.set(value); + } + @memo() public build() { + Row(@memo() ((instance: RowAttribute): void => { + instance.height(\"100%\"); + return; + }), undefined, @memo() (() => { + globalBuilder(this.message, 50); + ForEach(((): Array<@Builder() ((value: string, size: number)=> void)> => { + return builderArr; + }), ((@memo() item: @Builder() ((value: string, size: number)=> void)) => { + item(\"Hello World\", 30); + })); + })); + } + public constructor() {} +} +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() export interface __Options_Index { + set message(message: (string | undefined)) + get message(): (string | undefined) + set __backing_message(__backing_message: (IStateDecoratedVariable | undefined)) + get __backing_message(): (IStateDecoratedVariable | undefined) +} +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + Index._instantiateImpl(undefined, (() => { + return new Index(); + }), undefined, undefined, undefined); + } + public constructor() {} +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from \"arkui.stateManagement.decorator\"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from \"arkui.stateManagement.decorator\"; +import { RowAttribute as RowAttribute } from \"arkui.component.row\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { TextAttribute as TextAttribute } from \"arkui.component.text\"; +import { NavInterface as NavInterface } from \"arkui.UserView\"; +import { PageLifeCycle as PageLifeCycle } from \"arkui.component.customComponent\"; +import { EntryPoint as EntryPoint } from \"arkui.UserView\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Builder as Builder, Text as Text, Color as Color, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Entry as Entry, Component as Component, Row as Row, ForEach as ForEach } from \"@ohos.arkui.component\"; +import { State as State } from \"@ohos.arkui.stateManagement\"; +@memo() let globalBuilder: @Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void); +let builderArr: Array<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)>; +function main() {} +@memo() function MyBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_value = __memo_scope.param(0, value), __memo_parameter_size = __memo_scope.param(1, size); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.fontSize(__memo_parameter_size.value); + { + __memo_scope.recache(); + return; + } + }), __memo_parameter_value.value, undefined, undefined); + { + __memo_scope.recache(); + return; + } +} +@memo() function YourBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_value = __memo_scope.param(0, value), __memo_parameter_size = __memo_scope.param(1, size); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.fontSize(__memo_parameter_size.value).fontColor(Color.Pink); + { + __memo_scope.recache(); + return; + } + }), __memo_parameter_value.value, undefined, undefined); + { + __memo_scope.recache(); + return; + } +} +globalBuilder = MyBuilder; +builderArr = [MyBuilder, YourBuilder]; +__EntryWrapper.RegisterNamedRouter(\"\", new __EntryWrapper(), ({ + bundleName: \"com.example.mock\", + moduleName: \"entry\", + pagePath: \"../../../wrap-builder/builder-in-generic\", + pageFullPath: \"test/demo/mock/wrap-builder/builder-in-generic\", + integratedHsp: \"false\", +} as NavInterface)); +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_message = STATE_MGMT_FACTORY.makeState(this, \"message\", ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.message)})) ?? (\"Hello World\"))); + } + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + private __backing_message?: IStateDecoratedVariable; + public get message(): string { + return this.__backing_message!.get(); + } + public set message(value: string) { + this.__backing_message!.set(value); + } + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Row(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: RowAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.height(\"100%\"); + { + __memo_scope.recache(); + return; + } + }), undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + globalBuilder(__memo_context, ((__memo_id) + ()), this.message, 50); + ForEach(__memo_context, ((__memo_id) + ()), ((): Array<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)> => { + return builderArr; + }), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() item: @Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_item = __memo_scope.param(0, item); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_item.value(__memo_context, ((__memo_id) + ()), \"Hello World\", 30); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() export interface __Options_Index { + set message(message: (string | undefined)) + get message(): (string | undefined) + set __backing_message(__backing_message: (IStateDecoratedVariable | undefined)) + get __backing_message(): (IStateDecoratedVariable | undefined) +} +class __EntryWrapper extends EntryPoint { + @memo() public entry(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Index._instantiateImpl(__memo_context, ((__memo_id) + ()), undefined, (() => { + return new Index(); + }), undefined, undefined, undefined); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test builder with generic builder type', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/wrap-builder/init-with-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/wrap-builder/init-with-builder.test.ts new file mode 100644 index 000000000..e1e714dba --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/init-with-builder.test.ts @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { uiNoRecheck, recheck, memoNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const WRAP_BUILDER_DIR_PATH: string = 'wrap-builder'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, WRAP_BUILDER_DIR_PATH, 'init-with-builder.ets'), +]; + +const pluginTester = new PluginTester('test wrap builder init with @Builder function', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsed transform', + parsed: uiTransform().parsed +}; + +const expectedUIScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; +import { TextAttribute as TextAttribute } from "arkui.component.text"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, Text as Text, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Builder as Builder, Column as Column } from "@kit.ArkUI"; + +let globalBuilder: WrappedBuilder; + +function main() {} + +@memo() function myBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size); + return; + }), value, undefined, undefined); +} + +globalBuilder = wrapBuilder(myBuilder); + +@memo() type MyBuilderFuncType = @Builder() ((value: string, size: number)=> void); + +@Component() final struct ImportStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_ImportStruct | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_ImportStruct | undefined)): void {} + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + globalBuilder.builder("hello", 50); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_ImportStruct { + +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` + +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Builder as Builder, Column as Column } from "@kit.ArkUI"; + +let globalBuilder: WrappedBuilder; + +function main() {} + +@memo() function myBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number) { + const __memo_scope = __memo_context.scope(((__memo_id) + (52041161)), 2); + const __memo_parameter_value = __memo_scope.param(0, value), __memo_parameter_size = __memo_scope.param(1, size); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (175145513)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + (47330804)), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.fontSize(__memo_parameter_size.value); + { + __memo_scope.recache(); + return; + } + }), __memo_parameter_value.value, undefined, undefined); + { + __memo_scope.recache(); + return; + } +} + +globalBuilder = wrapBuilder(myBuilder); + +@memo() type MyBuilderFuncType = @Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void); + +@Component() final struct ImportStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_ImportStruct | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_ImportStruct | undefined)): void {} + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (172572715)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + (213104625)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (211301233)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + globalBuilder.builder(__memo_context, ((__memo_id) + (137225318)), "hello", 50); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} + +@Component() export interface __Options_ImportStruct { + +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test wrap builder init with @Builder function', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-generic.test.ts b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-generic.test.ts new file mode 100644 index 000000000..905a8dad5 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-generic.test.ts @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { uiNoRecheck, recheck, memoNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const WRAP_BUILDER_DIR_PATH: string = 'wrap-builder'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, WRAP_BUILDER_DIR_PATH, 'wrap-builder-in-generic.ets'), +]; + +const pluginTester = new PluginTester('test wrap builder with generic builder type', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsed transform', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from \"arkui.stateManagement.decorator\"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from \"arkui.stateManagement.decorator\"; +import { RowAttribute as RowAttribute } from \"arkui.component.row\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { TextAttribute as TextAttribute } from \"arkui.component.text\"; +import { NavInterface as NavInterface } from \"arkui.UserView\"; +import { PageLifeCycle as PageLifeCycle } from \"arkui.component.customComponent\"; +import { EntryPoint as EntryPoint } from \"arkui.UserView\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Builder as Builder, Text as Text, Color as Color, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Entry as Entry, Component as Component, Row as Row, ForEach as ForEach } from \"@ohos.arkui.component\"; +import { State as State } from \"@ohos.arkui.stateManagement\"; +let globalBuilder: WrappedBuilder<@Builder() ((value: string, size: number)=> void)>; +let builderArr: Array void)>>; +let wrappedBuilder1: WrappedBuilder<@Builder() ((value: string, size: number)=> void)>; +let wrappedBuilder2: WrappedBuilder<@Builder() ((value: string, size: number)=> void)>; +function main() {} +@memo() function MyBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size); + return; + }), value, undefined, undefined); +} +@memo() function YourBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size).fontColor(Color.Pink); + return; + }), value, undefined, undefined); +} +globalBuilder = wrapBuilder(MyBuilder); +builderArr = [wrapBuilder(MyBuilder), wrapBuilder(YourBuilder)]; +wrappedBuilder1 = wrapBuilder<@Builder() ((value: string, size: number)=> void)>(MyBuilder); +wrappedBuilder2 = new WrappedBuilder<@Builder() ((value: string, size: number)=> void)>(MyBuilder); +__EntryWrapper.RegisterNamedRouter(\"\", new __EntryWrapper(), ({ + bundleName: \"com.example.mock\", + moduleName: \"entry\", + pagePath: \"../../../wrap-builder/wrap-builder-in-generic\", + pageFullPath: \"test/demo/mock/wrap-builder/wrap-builder-in-generic\", + integratedHsp: \"false\", +} as NavInterface)); +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_message = STATE_MGMT_FACTORY.makeState(this, \"message\", ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.message)})) ?? (\"Hello World\"))); + } + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + private __backing_message?: IStateDecoratedVariable; + public get message(): string { + return this.__backing_message!.get(); + } + public set message(value: string) { + this.__backing_message!.set(value); + } + @memo() public build() { + Row(@memo() ((instance: RowAttribute): void => { + instance.height(\"100%\"); + return; + }), undefined, @memo() (() => { + globalBuilder.builder(this.message, 50); + ForEach(((): Array void)>> => { + return builderArr; + }), ((item: WrappedBuilder<@Builder() ((value: string, size: number)=> void)>) => { + item.builder(\"Hello World\", 30); + })); + })); + } + public constructor() {} +} +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() export interface __Options_Index { + set message(message: (string | undefined)) + get message(): (string | undefined) + set __backing_message(__backing_message: (IStateDecoratedVariable | undefined)) + get __backing_message(): (IStateDecoratedVariable | undefined) +} +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + Index._instantiateImpl(undefined, (() => { + return new Index(); + }), undefined, undefined, undefined); + } + public constructor() {} +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from \"arkui.stateManagement.decorator\"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from \"arkui.stateManagement.decorator\"; +import { RowAttribute as RowAttribute } from \"arkui.component.row\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { TextAttribute as TextAttribute } from \"arkui.component.text\"; +import { NavInterface as NavInterface } from \"arkui.UserView\"; +import { PageLifeCycle as PageLifeCycle } from \"arkui.component.customComponent\"; +import { EntryPoint as EntryPoint } from \"arkui.UserView\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Builder as Builder, Text as Text, Color as Color, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Entry as Entry, Component as Component, Row as Row, ForEach as ForEach } from \"@ohos.arkui.component\"; +import { State as State } from \"@ohos.arkui.stateManagement\"; +let globalBuilder: WrappedBuilder<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)>; +let builderArr: Array void)>>; +let wrappedBuilder1: WrappedBuilder<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)>; +let wrappedBuilder2: WrappedBuilder<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)>; +function main() {} +@memo() function MyBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_value = __memo_scope.param(0, value), __memo_parameter_size = __memo_scope.param(1, size); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.fontSize(__memo_parameter_size.value); + { + __memo_scope.recache(); + return; + } + }), __memo_parameter_value.value, undefined, undefined); + { + __memo_scope.recache(); + return; + } +} +@memo() function YourBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_value = __memo_scope.param(0, value), __memo_parameter_size = __memo_scope.param(1, size); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.fontSize(__memo_parameter_size.value).fontColor(Color.Pink); + { + __memo_scope.recache(); + return; + } + }), __memo_parameter_value.value, undefined, undefined); + { + __memo_scope.recache(); + return; + } +} +globalBuilder = wrapBuilder(MyBuilder); +builderArr = [wrapBuilder(MyBuilder), wrapBuilder(YourBuilder)]; +wrappedBuilder1 = wrapBuilder<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)>(MyBuilder); +wrappedBuilder2 = new WrappedBuilder<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)>(MyBuilder); +__EntryWrapper.RegisterNamedRouter(\"\", new __EntryWrapper(), ({ + bundleName: \"com.example.mock\", + moduleName: \"entry\", + pagePath: \"../../../wrap-builder/wrap-builder-in-generic\", + pageFullPath: \"test/demo/mock/wrap-builder/wrap-builder-in-generic\", + integratedHsp: \"false\", +} as NavInterface)); +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_message = STATE_MGMT_FACTORY.makeState(this, \"message\", ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.message)})) ?? (\"Hello World\"))); + } + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + private __backing_message?: IStateDecoratedVariable; + public get message(): string { + return this.__backing_message!.get(); + } + public set message(value: string) { + this.__backing_message!.set(value); + } + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Row(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: RowAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.height(\"100%\"); + { + __memo_scope.recache(); + return; + } + }), undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + globalBuilder.builder(__memo_context, ((__memo_id) + ()), this.message, 50); + ForEach(__memo_context, ((__memo_id) + ()), ((): Array void)>> => { + return builderArr; + }), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, item: WrappedBuilder<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)>) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_item = __memo_scope.param(0, item); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_item.value.builder(__memo_context, ((__memo_id) + ()), \"Hello World\", 30); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() export interface __Options_Index { + set message(message: (string | undefined)) + get message(): (string | undefined) + set __backing_message(__backing_message: (IStateDecoratedVariable | undefined)) + get __backing_message(): (IStateDecoratedVariable | undefined) +} +class __EntryWrapper extends EntryPoint { + @memo() public entry(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Index._instantiateImpl(__memo_context, ((__memo_id) + ()), undefined, (() => { + return new Index(); + }), undefined, undefined, undefined); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test wrap builder with generic builder type', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-ui.test.ts b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-ui.test.ts new file mode 100644 index 000000000..30038d9d3 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-ui.test.ts @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { uiNoRecheck, recheck, memoNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const WRAP_BUILDER_DIR_PATH: string = 'wrap-builder'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, WRAP_BUILDER_DIR_PATH, 'wrap-builder-in-ui.ets'), +]; + +const pluginTester = new PluginTester('test wrap builder used in UI', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsed transform', + parsed: uiTransform().parsed +}; + +const expectedUIScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Builder as Builder, Column as Column, ForEach as ForEach } from "@kit.ArkUI"; + +const globalBuilderArr: Array> = [wrapBuilder(myBuilder), wrapBuilder(yourBuilder)]; + +function main() {} + + +@memo() function myBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size); + return; + }), value, undefined, undefined); +} + +@memo() function yourBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size); + return; + }), value, undefined, undefined); +} + +globalBuilderArr = [wrapBuilder(myBuilder), wrapBuilder(yourBuilder)]; + +@memo() type MyBuilderFuncType = @Builder() ((value: string, size: number)=> void); + +@Component() final struct ImportStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_ImportStruct | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_ImportStruct | undefined)): void {} + + @memo() public testBuilder() { + ForEach(((): Array> => { + return globalBuilderArr; + }), ((item: WrappedBuilder) => { + item.builder("hello world", 39); + })); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + this.testBuilder(); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_ImportStruct { + +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Builder as Builder, Column as Column, ForEach as ForEach } from "@kit.ArkUI"; + +const globalBuilderArr: Array> = [wrapBuilder(myBuilder), wrapBuilder(yourBuilder)]; + +function main() {} + + +@memo() function myBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number) { + const __memo_scope = __memo_context.scope(((__memo_id) + (52041161)), 2); + const __memo_parameter_value = __memo_scope.param(0, value), __memo_parameter_size = __memo_scope.param(1, size); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (175145513)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + (47330804)), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.fontSize(__memo_parameter_size.value); + { + __memo_scope.recache(); + return; + } + }), __memo_parameter_value.value, undefined, undefined); + { + __memo_scope.recache(); + return; + } +} + +@memo() function yourBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number) { + const __memo_scope = __memo_context.scope(((__memo_id) + (151467670)), 2); + const __memo_parameter_value = __memo_scope.param(0, value), __memo_parameter_size = __memo_scope.param(1, size); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (211301233)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + (137225318)), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.fontSize(__memo_parameter_size.value); + { + __memo_scope.recache(); + return; + } + }), __memo_parameter_value.value, undefined, undefined); + { + __memo_scope.recache(); + return; + } +} + +globalBuilderArr = [wrapBuilder(myBuilder), wrapBuilder(yourBuilder)]; + +@memo() type MyBuilderFuncType = @Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void); + +@Component() final struct ImportStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_ImportStruct | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_ImportStruct | undefined)): void {} + + @memo() public testBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (194881372)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ForEach(__memo_context, ((__memo_id) + (218979098)), ((): Array> => { + return globalBuilderArr; + }), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, item: WrappedBuilder) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (76711614)), 1); + const __memo_parameter_item = __memo_scope.param(0, item); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_item.value.builder(__memo_context, ((__memo_id) + (46726221)), "hello world", 39); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (142886843)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + (54078781)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (213687742)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + this.testBuilder(__memo_context, ((__memo_id) + (192802443))); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} + +@Component() export interface __Options_ImportStruct { + +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test wrap builder in UI', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-with-lambda.test.ts b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-with-lambda.test.ts new file mode 100644 index 000000000..ab0ee2fdf --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-with-lambda.test.ts @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { uiNoRecheck, recheck, memoNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const WRAP_BUILDER_DIR_PATH: string = 'wrap-builder'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, WRAP_BUILDER_DIR_PATH, 'wrap-builder-with-lambda.ets'), +]; + +const pluginTester = new PluginTester('test wrap builder with lambda', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsed transform', + parsed: uiTransform().parsed +}; + +const expectedUIScript: string = ` + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Observed as Observed, Builder as Builder, Entry as Entry, Component as Component, State as State } from "@kit.ArkUI"; + +import { WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder } from "@kit.ArkUI"; + +import { Column as Column, Text as Text, Button as Button, ClickEvent as ClickEvent } from "@kit.ArkUI"; + +const wBuilder: WrappedBuilder = wrapBuilder(overBuilder); + +function main() {} + + +@memo() function overBuilder(param: (()=> Tmp)) { + Column(undefined, undefined, @memo() (() => { + Text(undefined, \`wrapBuildervalue:\${param().paramA2}\`, undefined, undefined); + })); +} + +wBuilder = wrapBuilder(overBuilder); + +@Observed() class Tmp implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"paramA2"}) private __backing_paramA2: string = "hello"; + + public constructor() {} + + public get paramA2(): string { + this.conditionalAddRef(this.__meta); + return this.__backing_paramA2; + } + + public set paramA2(newValue: string) { + if (((this.__backing_paramA2) !== (newValue))) { + this.__backing_paramA2 = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("paramA2"); + } + } + +} + +@memo() type MyBuilderFuncType = @Builder() ((param: (()=> Tmp))=> void); + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_label = STATE_MGMT_FACTORY.makeState(this, "label", ((({let gensym___171896504 = initializers; + (((gensym___171896504) == (null)) ? undefined : gensym___171896504.label)})) ?? (new Tmp()))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_label?: IStateDecoratedVariable; + + public get label(): Tmp { + return this.__backing_label!.get(); + } + + public set label(value: Tmp) { + this.__backing_label!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + wBuilder.builder((() => { + return { + paramA2: this.label.paramA2, + }; + })); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.label.paramA2 = "ArkUI"; + })); + return; + }), "Click me", undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_Parent { + set label(label: (Tmp | undefined)) + + get label(): (Tmp | undefined) + set __backing_label(__backing_label: (IStateDecoratedVariable | undefined)) + + get __backing_label(): (IStateDecoratedVariable | undefined) + +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` + +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Observed as Observed, Builder as Builder, Entry as Entry, Component as Component, State as State } from "@kit.ArkUI"; + +import { WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder } from "@kit.ArkUI"; + +import { Column as Column, Text as Text, Button as Button, ClickEvent as ClickEvent } from "@kit.ArkUI"; + +const wBuilder: WrappedBuilder = wrapBuilder(overBuilder); + +function main() {} + + +@memo() function overBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, param: (()=> Tmp)) { + const __memo_scope = __memo_context.scope(((__memo_id) + (133793681)), 1); + const __memo_parameter_param = __memo_scope.param(0, param); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + (241913892)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (175145513)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (47330804)), undefined, \`wrapBuildervalue:\${__memo_parameter_param.value().paramA2}\`, undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } +} + +wBuilder = wrapBuilder(overBuilder); + +@Observed() class Tmp implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"paramA2"}) private __backing_paramA2: string = "hello"; + + public constructor() {} + + public get paramA2(): string { + this.conditionalAddRef(this.__meta); + return this.__backing_paramA2; + } + + public set paramA2(newValue: string) { + if (((this.__backing_paramA2) !== (newValue))) { + this.__backing_paramA2 = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("paramA2"); + } + } + +} + +@memo() type MyBuilderFuncType = @Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, param: (()=> Tmp))=> void); + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_label = STATE_MGMT_FACTORY.makeState(this, "label", ((({let gensym___171896504 = initializers; + (((gensym___171896504) == (null)) ? undefined : gensym___171896504.label)})) ?? (new Tmp()))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_label?: IStateDecoratedVariable; + + public get label(): Tmp { + return this.__backing_label!.get(); + } + + public set label(value: Tmp) { + this.__backing_label!.set(value); + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (69406103)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + (218979098)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (76711614)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + wBuilder.builder(__memo_context, ((__memo_id) + (211301233)), (() => { + return { + paramA2: this.label.paramA2, + }; + })); + Button(__memo_context, ((__memo_id) + (46726221)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: ButtonAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + (213104625)), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.onClick(((e: ClickEvent) => { + this.label.paramA2 = "ArkUI"; + })); + { + __memo_scope.recache(); + return; + } + }), "Click me", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} + +@Component() export interface __Options_Parent { + set label(label: (Tmp | undefined)) + + get label(): (Tmp | undefined) + set __backing_label(__backing_label: (IStateDecoratedVariable | undefined)) + + get __backing_label(): (IStateDecoratedVariable | undefined) + +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test wrap builder with lambda', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/utils/artkts-config.ts b/arkui-plugins/test/utils/artkts-config.ts index 3474c59a2..d47091bd7 100644 --- a/arkui-plugins/test/utils/artkts-config.ts +++ b/arkui-plugins/test/utils/artkts-config.ts @@ -21,19 +21,27 @@ import { changeFileExtension, ensurePathExists, getFileName, + getResourcePath, getRootPath, + MOCK_BUNDLE_NAME, + MOCK_DEP_ANALYZER_PATH, MOCK_ENTRY_DIR_PATH, MOCK_ENTRY_FILE_NAME, - MOCK_LOCAL_SDK_DIR_PATH, MOCK_OUTPUT_CACHE_PATH, MOCK_OUTPUT_DIR_PATH, MOCK_OUTPUT_FILE_NAME, + MOCK_PROJECT_ROOT_PATH, + MOCK_RAWFILE_DIR_PATH, + MOCK_RESOURCE_TABLE_FILE_NAME, PANDA_SDK_STDLIB_PATH, - RUNTIME_API_PATH, STDLIB_ESCOMPAT_PATH, STDLIB_PATH, STDLIB_STD_PATH, } from './path-config'; +import { ArkTSConfigContextCache } from './cache'; +import { BuildConfig, CompileFileInfo, DependentModule } from './shared-types'; +import { setUpSoPath } from './global'; +import { ProjectConfig } from '../../common/plugin-context'; export interface ArkTSConfigObject { compilerOptions: { @@ -45,15 +53,6 @@ export interface ArkTSConfigObject { }; } -export interface CompileFileInfo { - fileName: string; - filePath: string; - dependentFiles: string[]; - abcFilePath: string; - arktsConfigFile: string; - stdLibPath: string; -} - export interface ModuleInfo { isMainModule: boolean; packageName: string; @@ -65,45 +64,15 @@ export interface ModuleInfo { dependencies?: string[]; } -export interface DependentModule { - packageName: string; - moduleName: string; - moduleType: string; - modulePath: string; - sourceRoots: string[]; - entryFile: string; -} - -export type ModuleType = 'har' | string; // TODO: module type unclear - -export interface DependentModule { - packageName: string; - moduleName: string; - moduleType: ModuleType; - modulePath: string; - sourceRoots: string[]; - entryFile: string; -} - -export interface BuildConfig { - packageName: string; - compileFiles: string[]; - loaderOutPath: string; - cachePath: string; - pandaSdkPath: string; - buildSdkPath: string; - sourceRoots: string[]; - moduleRootPath: string; - dependentModuleList: DependentModule[]; -} - export interface ArktsConfigBuilder { buildConfig: BuildConfig; entryFiles: Set; + compileFiles: Map; outputDir: string; cacheDir: string; pandaSdkPath: string; - buildSdkPath: string; + apiPath: string; + kitsPath: string; packageName: string; sourceRoots: string[]; moduleRootPath: string; @@ -112,6 +81,12 @@ export interface ArktsConfigBuilder { mergedAbcFile: string; // logger: Logger; // TODO isDebug: boolean; + projectConfig: ProjectConfig; + + withBuildConfig(buildConfig: BuildConfig): this; + withProjectConfig(projectConfig: ProjectConfig): this; + + clear(): void; } function writeArkTSConfigFile( @@ -158,21 +133,6 @@ function getDependentModules(moduleInfo: ModuleInfo, moduleInfoMap: Map) { - const items = fs.readdirSync(currentDir); - for (const item of items) { - const itemPath = path.join(currentDir, item); - const stat = fs.statSync(itemPath); - - if (stat.isFile()) { - const basename = path.basename(item, '.d.ets'); - pathSection[basename] = [changeFileExtension(itemPath, '', '.d.ets')]; - } else if (stat.isDirectory()) { - traverse(itemPath, pathSection); - } - } -} - function traverseSDK(currentDir: string, pathSection: Record, prefix?: string) { const items = fs.readdirSync(currentDir); @@ -203,47 +163,131 @@ function mockBuildConfig(): BuildConfig { loaderOutPath: path.resolve(getRootPath(), MOCK_OUTPUT_DIR_PATH), cachePath: path.resolve(getRootPath(), MOCK_OUTPUT_CACHE_PATH), pandaSdkPath: global.PANDA_SDK_PATH, - buildSdkPath: global.API_PATH, + apiPath: global.API_PATH, + kitsPath: global.KIT_PATH, + depAnalyzerPath: path.resolve(global.PANDA_SDK_PATH, MOCK_DEP_ANALYZER_PATH), sourceRoots: [getRootPath()], moduleRootPath: path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH), dependentModuleList: [], }; } +function mockProjectConfig(): ProjectConfig { + return { + bundleName: MOCK_BUNDLE_NAME, + moduleName: 'entry', + cachePath: path.resolve(getRootPath(), MOCK_OUTPUT_CACHE_PATH), + dependentModuleList: [], + appResource: path.resolve(getResourcePath(), MOCK_RESOURCE_TABLE_FILE_NAME), + rawFileResource: path.resolve(getResourcePath(), MOCK_RAWFILE_DIR_PATH), + buildLoaderJson: '', + hspResourcesMap: false, + compileHar: false, + byteCodeHar: false, + uiTransformOptimization: false, + resetBundleName: false, + allowEmptyBundleName: false, + moduleType: 'entry', + moduleRootPath: path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH), + aceModuleJsonPath: '', + ignoreError: true, + projectPath: '', + projectRootPath: path.resolve(getRootPath(), MOCK_PROJECT_ROOT_PATH), + integratedHsp: false, + frameworkMode: undefined + }; +} + class MockArktsConfigBuilder implements ArktsConfigBuilder { - buildConfig: BuildConfig; - entryFiles: Set; - outputDir: string; - cacheDir: string; - pandaSdkPath: string; - buildSdkPath: string; - packageName: string; - sourceRoots: string[]; - moduleRootPath: string; - dependentModuleList: DependentModule[]; - moduleInfos: Map; - mergedAbcFile: string; + hashId: string; + buildConfig!: BuildConfig; + entryFiles!: Set; + compileFiles!: Map; + outputDir!: string; + cacheDir!: string; + pandaSdkPath!: string; + apiPath!: string; + kitsPath!: string; + packageName!: string; + sourceRoots!: string[]; + moduleRootPath!: string; + dependentModuleList!: DependentModule[]; + moduleInfos!: Map; + mergedAbcFile!: string; isDebug: boolean; + projectConfig: ProjectConfig; + + constructor(hashId: string, buildConfig?: BuildConfig, projectConfig?: ProjectConfig) { + this.hashId = hashId; - constructor(buildConfig?: BuildConfig) { const _buildConfig: BuildConfig = buildConfig ?? mockBuildConfig(); + this._setBuildConfig(_buildConfig); + + const _projectConfig: ProjectConfig = projectConfig ?? mockProjectConfig(); + this.projectConfig = _projectConfig; + + this.isDebug = true; + } + + private _setBuildConfig(buildConfig: BuildConfig): void { + const _buildConfig: BuildConfig = buildConfig; this.buildConfig = _buildConfig; this.entryFiles = new Set(_buildConfig.compileFiles as string[]); this.outputDir = _buildConfig.loaderOutPath as string; this.cacheDir = _buildConfig.cachePath as string; this.pandaSdkPath = path.resolve(_buildConfig.pandaSdkPath as string); - this.buildSdkPath = path.resolve(_buildConfig.buildSdkPath as string); + this.apiPath = path.resolve(_buildConfig.apiPath as string); + this.kitsPath = path.resolve(_buildConfig.kitsPath as string); this.packageName = _buildConfig.packageName as string; this.sourceRoots = _buildConfig.sourceRoots as string[]; this.moduleRootPath = path.resolve(_buildConfig.moduleRootPath as string); this.dependentModuleList = _buildConfig.dependentModuleList as DependentModule[]; - this.isDebug = true; + this.compileFiles = new Map(); this.moduleInfos = new Map(); this.mergedAbcFile = path.resolve(this.outputDir, MOCK_OUTPUT_FILE_NAME); + setUpSoPath(this.pandaSdkPath); this.generateModuleInfos(); this.generateArkTSConfigForModules(); + this.cacheArkTSConfig(); + } + + private cacheArkTSConfig(): void { + const mainModuleInfo: ModuleInfo = this.moduleInfos.get(this.moduleRootPath)!; + const arktsConfigFile: string = mainModuleInfo.arktsConfigFile; + const compileFiles: Map = this.compileFiles; + ArkTSConfigContextCache.getInstance().set(this.hashId, { arktsConfigFile, compileFiles }); + } + + private generateModuleInfosInEntryFile(file: string): void { + const _file = path.resolve(file); + for (const [modulePath, moduleInfo] of this.moduleInfos) { + if (!_file.startsWith(modulePath)) { + throw new Error('Entry File does not belong to any module in moduleInfos.'); + } + const filePathFromModuleRoot: string = path.relative(modulePath, _file); + const filePathInCache: string = path.join( + this.cacheDir, + this.hashId, + moduleInfo.packageName, + filePathFromModuleRoot + ); + const abcFilePath: string = path.resolve(changeFileExtension(filePathInCache, ABC_SUFFIX)); + + const fileInfo: CompileFileInfo = { + fileName: getFileName(_file), + filePath: _file, + dependentFiles: [], + abcFilePath: abcFilePath, + arktsConfigFile: moduleInfo.arktsConfigFile, + stdLibPath: path.resolve(this.pandaSdkPath, PANDA_SDK_STDLIB_PATH, STDLIB_PATH), + }; + moduleInfo.compileFileInfos.push(fileInfo); + if (!this.compileFiles.has(_file)) { + this.compileFiles.set(_file, fileInfo); + } + } } private generateModuleInfos(): void { @@ -256,7 +300,7 @@ class MockArktsConfigBuilder implements ArktsConfigBuilder { moduleRootPath: this.moduleRootPath, sourceRoots: this.sourceRoots, entryFile: '', - arktsConfigFile: path.resolve(this.cacheDir, this.packageName, ARKTS_CONFIG_FILE_PATH), + arktsConfigFile: path.resolve(this.cacheDir, this.hashId, this.packageName, ARKTS_CONFIG_FILE_PATH), compileFileInfos: [], }; this.moduleInfos.set(this.moduleRootPath, mainModuleInfo); @@ -270,36 +314,13 @@ class MockArktsConfigBuilder implements ArktsConfigBuilder { moduleRootPath: module.modulePath, sourceRoots: module.sourceRoots, entryFile: module.entryFile, - arktsConfigFile: path.resolve(this.cacheDir, module.packageName, ARKTS_CONFIG_FILE_PATH), + arktsConfigFile: path.resolve(this.cacheDir, this.hashId, module.packageName, ARKTS_CONFIG_FILE_PATH), compileFileInfos: [], }; this.moduleInfos.set(module.modulePath, moduleInfo); }); this.entryFiles.forEach((file: string) => { - const _file = path.resolve(file); - for (const [modulePath, moduleInfo] of this.moduleInfos) { - if (_file.startsWith(modulePath)) { - const filePathFromModuleRoot: string = path.relative(modulePath, _file); - const filePathInCache: string = path.join( - this.cacheDir, - moduleInfo.packageName, - filePathFromModuleRoot - ); - const abcFilePath: string = path.resolve(changeFileExtension(filePathInCache, ABC_SUFFIX)); - - const fileInfo: CompileFileInfo = { - fileName: getFileName(_file), - filePath: _file, - dependentFiles: [], - abcFilePath: abcFilePath, - arktsConfigFile: moduleInfo.arktsConfigFile, - stdLibPath: path.resolve(this.pandaSdkPath, PANDA_SDK_STDLIB_PATH, STDLIB_PATH), - }; - moduleInfo.compileFileInfos.push(fileInfo); - return; - } - } - throw new Error('Entry File does not belong to any module in moduleInfos.'); + this.generateModuleInfosInEntryFile(file); }); } @@ -307,7 +328,8 @@ class MockArktsConfigBuilder implements ArktsConfigBuilder { const pathSection: Record = {}; pathSection['std'] = [path.resolve(this.pandaSdkPath, PANDA_SDK_STDLIB_PATH, STDLIB_STD_PATH)]; pathSection['escompat'] = [path.resolve(this.pandaSdkPath, PANDA_SDK_STDLIB_PATH, STDLIB_ESCOMPAT_PATH)]; - traverseSDK(this.buildSdkPath, pathSection); + traverseSDK(this.apiPath, pathSection); + traverseSDK(this.kitsPath, pathSection); this.moduleInfos.forEach((moduleInfo: ModuleInfo, moduleRootPath: string) => { pathSection[moduleInfo.packageName] = [path.resolve(moduleRootPath, moduleInfo.sourceRoots[0])]; @@ -329,6 +351,20 @@ class MockArktsConfigBuilder implements ArktsConfigBuilder { dependenciesSection.push(depModuleInfo.arktsConfigFile); }); } + + withBuildConfig(buildConfig: BuildConfig): this { + this._setBuildConfig(buildConfig); + return this; + } + + withProjectConfig(projectConfig: ProjectConfig): this { + this.projectConfig = projectConfig; + return this; + } + + clear(): void { + ArkTSConfigContextCache.getInstance().delete(this.hashId); + } } -export { mockBuildConfig, MockArktsConfigBuilder }; +export { mockBuildConfig, mockProjectConfig, MockArktsConfigBuilder }; diff --git a/arkui-plugins/test/utils/cache.ts b/arkui-plugins/test/utils/cache.ts index b24fcdf64..06942d1c4 100644 --- a/arkui-plugins/test/utils/cache.ts +++ b/arkui-plugins/test/utils/cache.ts @@ -13,21 +13,15 @@ * limitations under the License. */ +import { ArkTSConfigContext, FileDependencyContext, PluginTestContext } from './shared-types'; + class TesterCache { private cacheInfo: Map; - private static instance: TesterCache; - private constructor() { + constructor() { this.cacheInfo = new Map(); } - static getInstance(): TesterCache { - if (!this.instance) { - this.instance = new TesterCache(); - } - return this.instance; - } - public delete(key: string) { if (this.cacheInfo.has(key)) { this.cacheInfo.delete(key); @@ -56,4 +50,37 @@ class TesterCache { } } -export { TesterCache }; +class PluginTestContextCache extends TesterCache { + private static _instance: TesterCache; + + static getInstance(): TesterCache { + if (!this._instance) { + this._instance = new PluginTestContextCache(); + } + return this._instance; + } +} + +class ArkTSConfigContextCache extends TesterCache { + private static _instance: TesterCache; + + static getInstance(): TesterCache { + if (!this._instance) { + this._instance = new ArkTSConfigContextCache(); + } + return this._instance; + } +} + +class FileDependencyContextCache extends TesterCache { + private static _instance: TesterCache; + + static getInstance(): TesterCache { + if (!this._instance) { + this._instance = new FileDependencyContextCache(); + } + return this._instance; + } +} + +export { TesterCache, PluginTestContextCache, ArkTSConfigContextCache, FileDependencyContextCache }; diff --git a/arkui-plugins/test/utils/compile.ts b/arkui-plugins/test/utils/compile.ts index f893dae3b..afa55219a 100644 --- a/arkui-plugins/test/utils/compile.ts +++ b/arkui-plugins/test/utils/compile.ts @@ -13,48 +13,287 @@ * limitations under the License. */ -import { PluginDriver } from './plugin-driver'; -import { PluginContext, PluginExecutor } from '../../common/plugin-context'; -import { EtsglobalRemover } from '../../common/etsglobal-remover'; import * as arkts from '@koalaui/libarkts'; +import EventEmitter from 'events'; +import { + CompileStrategy, + JobInfo, + PluginTestContext, + ProcessEvent, + SingleProgramContext, + TraceOptions, +} from './shared-types'; +import { MockPluginDriver, stateName } from './plugin-driver'; +import { + createContextGenerateAbcForExternalSourceFiles, + createCacheContextFromFile, + destroyContext, + resetConfig, +} from './global'; +import { PluginDriver } from './plugin-driver'; +import { PluginState, PluginContext, PluginExecutor } from '../../common/plugin-context'; +import { concatObject } from './serializable'; -function restartCompilerUptoState(state: arkts.Es2pandaContextState, restart: boolean): boolean { - try { - const ast: arkts.EtsScript | undefined = arkts.EtsScript.fromContext(); - if (!ast) { - return false; - } +function insertPlugin(driver: PluginDriver, plugin: PluginExecutor | undefined): boolean { + const pluginContext: PluginContext = driver.getPluginContext(); + if (plugin) { + plugin.handler.apply(pluginContext); + } + return true; +} + +function collectPluginTextContextFromSourceProgram(program: arkts.Program, tracing: TraceOptions): PluginTestContext { + const pluginTestContext: PluginTestContext = {}; + const script: arkts.EtsScript = program.astNode; + pluginTestContext.scriptSnapshot = script.dumpSrc(); + return pluginTestContext; +} - if (restart) { - const srcText = new EtsglobalRemover().visitor(ast).dumpSrc(); - arkts.arktsGlobal.es2panda._DestroyContext(arkts.arktsGlobal.context); - arkts.arktsGlobal.compilerContext = arkts.Context.createFromString(srcText); +function collectPluginTextContextFromExternalSource( + externalSources: arkts.ExternalSource[], + tracing: TraceOptions, + matchSourceName: (name: string) => boolean, + useCache?: boolean +) { + let pluginTestContext: PluginTestContext = {}; + const filteredExternalSourceNames: string[] = [...tracing.externalSourceNames]; + const filteredExternalSources = externalSources.filter((source) => { + const name = source.getName(); + const sourceProgram: arkts.Program = source.programs[0]; + const shouldCollectByName = filteredExternalSourceNames.includes(name) || matchSourceName(name); + const shouldCollectByProgram = sourceProgram && (!useCache || sourceProgram.isASTLowered()); + return shouldCollectByName && shouldCollectByProgram; + }); + const declContexts: Record = {}; + filteredExternalSources.forEach((source) => { + const name: string = source.getName(); + const sourceProgram: arkts.Program = source.programs[0]; + if (matchSourceName(name)) { + pluginTestContext = concatObject( + pluginTestContext, + collectPluginTextContextFromSourceProgram(sourceProgram, tracing) + ); + } else { + const sourceTestContext: SingleProgramContext = {}; + const script: arkts.EtsScript = sourceProgram.astNode; + const scriptSnapshot = script.dumpSrc(); + sourceTestContext.scriptSnapshot = scriptSnapshot; + declContexts[name] = sourceTestContext; } + }); + pluginTestContext.declContexts = declContexts; + return pluginTestContext; +} - arkts.proceedToState(state); - return true; +function collectPluginTestContext( + context: arkts.Context, + compileStrategy: CompileStrategy, + tracing: TraceOptions, + matchSourceName: (name: string) => boolean +): PluginTestContext { + const useCache: boolean = compileStrategy !== CompileStrategy.ABC_WTIH_EXTERNAL; + const canCollectSource: boolean = !useCache || compileStrategy === CompileStrategy.ABC; + const canCollectExternal: boolean = !useCache || compileStrategy === CompileStrategy.EXTERNAL; + let pluginTestContext: PluginTestContext = {}; + try { + const program: arkts.Program = arkts.getOrUpdateGlobalContext(context.peer).program; + // TODO: add error/warning handling after plugin + if (canCollectSource) { + pluginTestContext = concatObject( + pluginTestContext, + collectPluginTextContextFromSourceProgram(program, tracing) + ); + } + if (canCollectExternal) { + const externalSources: arkts.ExternalSource[] = program.externalSources; + pluginTestContext = concatObject( + pluginTestContext, + collectPluginTextContextFromExternalSource(externalSources, tracing, matchSourceName, useCache) + ); + } } catch (e) { - return false; + // Do nothing + } finally { + return pluginTestContext; } } -function insertPlugin( - driver: PluginDriver, - plugin: PluginExecutor | undefined, - state: arkts.Es2pandaContextState +function buildMatchNameFunc(prefix: string, suffix: string): (name: string) => boolean { + return (name: string): boolean => { + return name.startsWith(`${prefix}.`) && name.endsWith(`.${suffix}`); + }; +} + +/** + * @param emitter event emitter. + * @param jobInfo job info. + * @param state the current state. + * @param context context for the single file. + * @param stopAfter state that should stop after running plugins. + * @returns boolean indicates whether should proceed to the next state. + */ +function runPluginsAtState( + emitter: EventEmitter, + jobInfo: JobInfo, + state: arkts.Es2pandaContextState, + context: arkts.Context, + tracing: TraceOptions, + stopAfter?: PluginState ): boolean { - arkts.proceedToState(state); - const pluginContext: PluginContext = driver.getPluginContext(); - const ast: arkts.EtsScript | undefined = arkts.EtsScript.fromContext(); + const stateStr = stateName(state); + const plugins = MockPluginDriver.getInstance().getSortedPlugins(state); + const packageName = jobInfo.buildConfig!.packageName; + const fileName = jobInfo.compileFileInfo!.fileName; + const matchSourceName = buildMatchNameFunc(packageName, fileName); + const compileStrategy = jobInfo.isCompileAbc; + if (plugins && plugins.length > 0) { + plugins.forEach((plugin) => { + insertPlugin(MockPluginDriver.getInstance(), plugin); + const pluginName: string = plugin.name; + const pluginStateId: `${PluginState}:${string}` = `${stateStr}:${pluginName}`; + const pluginTestContext = collectPluginTestContext(context, compileStrategy, tracing, matchSourceName); + emitter.emit('TASK_COLLECT', { + jobId: jobInfo.id, + pluginStateId, + pluginTestContext: pluginTestContext, + fileName, + }); + }); + } + const pluginStateId: PluginState = `${stateStr}`; + const pluginTestContext = collectPluginTestContext(context, compileStrategy, tracing, matchSourceName); + emitter.emit('TASK_COLLECT', { + jobId: jobInfo.id, + pluginStateId, + pluginTestContext: pluginTestContext, + fileName, + }); + return !!stopAfter && stopAfter === stateStr; +} + +function createContextForAbcCompilation(jobInfo: JobInfo): arkts.Context { + const fileInfo = jobInfo.compileFileInfo!; + const globalContextPtr = jobInfo.globalContextPtr!; + const ets2pandaCmd = [ + '_', + '--extension', + 'ets', + '--arktsconfig', + fileInfo.arktsConfigFile, + '--output', + fileInfo.abcFilePath, + ]; + ets2pandaCmd.push(fileInfo.filePath); + const config = resetConfig(ets2pandaCmd); + const context = createCacheContextFromFile(config, fileInfo.filePath, globalContextPtr, false); + return context; +} + +function createContextForExternalCompilation(jobInfo: JobInfo): arkts.Context { + const fileInfo = jobInfo.compileFileInfo!; + const globalContextPtr = jobInfo.globalContextPtr!; + const ets2pandaCmd = ['-', '--extension', 'ets', '--arktsconfig', fileInfo.arktsConfigFile]; + ets2pandaCmd.push(fileInfo.filePath); + const config = resetConfig(ets2pandaCmd); + const context = createCacheContextFromFile(config, fileInfo.filePath, globalContextPtr, true); + return context; +} - if (!ast) { - return false; +function compileAbcWithExternal(emitter: EventEmitter, jobInfo: JobInfo, tracing: TraceOptions): void { + MockPluginDriver.getInstance().initPlugins(jobInfo.plugins ?? []); + MockPluginDriver.getInstance().getPluginContext().setProjectConfig(jobInfo.projectConfig!); + const context = createContextGenerateAbcForExternalSourceFiles(jobInfo.filePaths!); + MockPluginDriver.getInstance().getPluginContext().setContextPtr(context.peer); + const stopAfter = jobInfo.stopAfter!; + let shouldStop = false; + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, context.peer); + shouldStop = runPluginsAtState( + emitter, + jobInfo, + arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, + context, + tracing, + stopAfter + ); + if (shouldStop) { + destroyContext(context); + MockPluginDriver.getInstance().clear(); + emitter.emit('TASK_FINISH', { jobId: 'compile-abc-with-external' }); + return; } + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, context.peer); + shouldStop = runPluginsAtState( + emitter, + jobInfo, + arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, + context, + tracing, + stopAfter + ); + if (shouldStop) { + destroyContext(context); + MockPluginDriver.getInstance().clear(); + emitter.emit('TASK_FINISH', { jobId: 'compile-abc-with-external' }); + return; + } + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, context.peer); + destroyContext(context); + MockPluginDriver.getInstance().clear(); + emitter.emit('TASK_FINISH', { jobId: 'compile-abc-with-external' }); +} - if (plugin) { - plugin.handler.apply(pluginContext); +function compileAbc(emitter: EventEmitter, jobInfo: JobInfo, tracing: TraceOptions): void { + MockPluginDriver.getInstance().initPlugins(jobInfo.plugins ?? []); + MockPluginDriver.getInstance().getPluginContext().setProjectConfig(jobInfo.projectConfig!); + const context = createContextForAbcCompilation(jobInfo); + MockPluginDriver.getInstance().getPluginContext().setContextPtr(context.peer); + const stopAfter = jobInfo.stopAfter!; + let shouldStop = false; + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, context.peer); + shouldStop = runPluginsAtState( + emitter, + jobInfo, + arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, + context, + tracing, + stopAfter + ); + if (shouldStop) { + destroyContext(context); + MockPluginDriver.getInstance().clear(); + return; } - return true; + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, context.peer); + shouldStop = runPluginsAtState( + emitter, + jobInfo, + arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, + context, + tracing, + stopAfter + ); + if (shouldStop) { + destroyContext(context); + MockPluginDriver.getInstance().clear(); + return; + } + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, context.peer); + destroyContext(context); + MockPluginDriver.getInstance().clear(); +} + +function compileExternalProgram(emitter: EventEmitter, jobInfo: JobInfo, tracing: TraceOptions): void { + MockPluginDriver.getInstance().initPlugins(jobInfo.plugins ?? []); + MockPluginDriver.getInstance().getPluginContext().setProjectConfig(jobInfo.projectConfig!); + const context = createContextForExternalCompilation(jobInfo); + MockPluginDriver.getInstance().getPluginContext().setContextPtr(context.peer); + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, context.peer); + runPluginsAtState(emitter, jobInfo, arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, context, tracing); + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, context.peer); + runPluginsAtState(emitter, jobInfo, arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, context, tracing); + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_LOWERED, context.peer); + destroyContext(context); + MockPluginDriver.getInstance().clear(); } -export { restartCompilerUptoState, insertPlugin }; +export { compileAbcWithExternal, compileAbc, compileExternalProgram }; diff --git a/arkui-plugins/test/utils/global.ts b/arkui-plugins/test/utils/global.ts index cbccd4afa..424d4512e 100644 --- a/arkui-plugins/test/utils/global.ts +++ b/arkui-plugins/test/utils/global.ts @@ -13,11 +13,15 @@ * limitations under the License. */ -import * as fs from 'fs'; -import { CompileFileInfo } from './artkts-config'; +import fs from 'fs' import * as arkts from '@koalaui/libarkts'; +import { CompileFileInfo } from './shared-types'; -function initGlobal(fileInfo: CompileFileInfo, isDebug: boolean = true): void { +function createGlobalConfig( + fileInfo: CompileFileInfo, + isDebug: boolean = true, + isUseCache: boolean = true +): arkts.Config { const config = [ '_', '--extension', @@ -31,13 +35,44 @@ function initGlobal(fileInfo: CompileFileInfo, isDebug: boolean = true): void { if (isDebug) { config.push('--debug-info'); } + if (!isUseCache) { + config.push('--simultaneous'); + } config.push(fileInfo.filePath); + if (isUseCache) { + arkts.MemInitialize(); + } arkts.arktsGlobal.filePath = fileInfo.filePath; - resetConfig(config); + return resetConfig(config); +} + +function destroyGlobalConfig(config: arkts.Config, isUseCache: boolean = true): void { + destroyConfig(config); + if (isUseCache) { + arkts.MemFinalize(); + } +} + +function createGlobalContextPtr(config: arkts.Config, files: string[]): number { + return arkts.CreateGlobalContext(config.peer, files, files.length, false); +} - const source: string = fs.readFileSync(fileInfo.filePath).toString(); - resetContext(source); +function destroyGlobalContextPtr(globalContextPtr: number): void { + arkts.DestroyGlobalContext(globalContextPtr); +} + +function createCacheContextFromFile( + config: arkts.Config, + filePath: string, + globalContextPtr: number, + isExternal: boolean +): arkts.Context { + return arkts.Context.createCacheContextFromFile(config.peer, filePath, globalContextPtr, isExternal); +} + +function createContextGenerateAbcForExternalSourceFiles(filePaths: string[]): arkts.Context { + return arkts.Context.createContextGenerateAbcForExternalSourceFiles(filePaths); } function resetContext(source: string): void { @@ -51,48 +86,49 @@ function resetContext(source: string): void { } } -function resetConfig(cmd: string[]): void { +function resetConfig(cmd: string[]): arkts.Config { try { arkts.arktsGlobal.config; - destroyConfig(); + destroyConfig(arkts.arktsGlobal.config); } catch (e) { // Do nothing } finally { - const arkTSConfig: arkts.Config = arkts.Config.create(cmd); - arkts.arktsGlobal.config = arkTSConfig.peer; + const config = arkts.Config.create(cmd); + arkts.arktsGlobal.config = config.peer; + return config; } } -function destroyContext(): void { +function destroyContext(context: arkts.Context): void { try { - arkts.arktsGlobal.clearContext(); + arkts.arktsGlobal.es2panda._DestroyContext(context.peer); } catch (e) { // Do nothing } } -function destroyConfig(): void { +function destroyConfig(config: arkts.Config): void { try { - arkts.destroyConfig(arkts.arktsGlobal.config); + arkts.destroyConfig(config); } catch (e) { // Do nothing } } -function canProceedToState(state: arkts.Es2pandaContextState): boolean { - const stateToSkip: arkts.Es2pandaContextState[] = [ - arkts.Es2pandaContextState.ES2PANDA_STATE_SCOPE_INITED, - arkts.Es2pandaContextState.ES2PANDA_STATE_BOUND, - arkts.Es2pandaContextState.ES2PANDA_STATE_LOWERED, - arkts.Es2pandaContextState.ES2PANDA_STATE_ASM_GENERATED, - arkts.Es2pandaContextState.ES2PANDA_STATE_ERROR, - ]; - if (state in stateToSkip) { - return false; - } - - const currState = arkts.arktsGlobal.es2panda._ContextState(arkts.arktsGlobal.context); - return currState < state; +function setUpSoPath(pandaSdkPath: string): void { + arkts.arktsGlobal.es2panda._SetUpSoPath(pandaSdkPath); } -export { initGlobal, resetContext, resetConfig, destroyContext, destroyConfig, canProceedToState }; +export { + createGlobalConfig, + destroyGlobalConfig, + createGlobalContextPtr, + destroyGlobalContextPtr, + createCacheContextFromFile, + createContextGenerateAbcForExternalSourceFiles, + resetContext, + resetConfig, + destroyContext, + destroyConfig, + setUpSoPath, +}; diff --git a/arkui-plugins/test/utils/hash-generator.ts b/arkui-plugins/test/utils/hash-generator.ts new file mode 100644 index 000000000..09e0b6d45 --- /dev/null +++ b/arkui-plugins/test/utils/hash-generator.ts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { randomBytes } from 'crypto'; +import { getCommonPath } from '../../path'; +const common = require(getCommonPath()); +const UniqueId = common.UniqueId; + +export class HashGenerator { + static instance: HashGenerator; + + private constructor() {} + + static getInstance(): HashGenerator { + if (!this.instance) { + this.instance = new HashGenerator(); + } + + return this.instance; + } + + dynamicSha1Id(id: string, length: number = 7): string { + const uniqId = new UniqueId(); + uniqId.addString('hashId'); + uniqId.addString(id); + uniqId.addString(randomBytes(length).toString('hex')); + return uniqId.compute().substring(0, length); + } + + staticSha1Id(id: string, length: number = 7): string { + const uniqId = new UniqueId(); + uniqId.addString('hashId'); + uniqId.addString(id); + return uniqId.compute().substring(0, length); + } +} diff --git a/arkui-plugins/test/utils/path-config.ts b/arkui-plugins/test/utils/path-config.ts index 608df6dee..bb7082498 100644 --- a/arkui-plugins/test/utils/path-config.ts +++ b/arkui-plugins/test/utils/path-config.ts @@ -17,23 +17,32 @@ import * as fs from 'fs'; import * as path from 'path'; export const ARKTS_CONFIG_FILE_PATH: string = 'arktsconfig.json'; -export const PANDA_SDK_PATH: string = 'node_modules/@panda/sdk'; export const PANDA_SDK_STDLIB_PATH: string = 'lib'; export const STDLIB_PATH: string = 'stdlib'; export const STDLIB_STD_PATH: string = 'stdlib/std'; export const STDLIB_ESCOMPAT_PATH: string = 'stdlib/escompat'; -export const RUNTIME_API_PATH: string = 'demo/runtime-api'; +export const MOCK_PROJECT_ROOT_PATH: string = 'demo'; export const MOCK_ENTRY_DIR_PATH: string = 'demo/mock'; export const MOCK_ENTRY_FILE_NAME: string = 'entry.ets'; export const MOCK_OUTPUT_CACHE_PATH: string = 'generated/cache'; export const MOCK_OUTPUT_DIR_PATH: string = 'generated/abc'; export const MOCK_OUTPUT_FILE_NAME: string = 'entry.abc'; -export const MOCK_LOCAL_SDK_DIR_PATH: string = 'local'; +export const MOCK_DEP_ANALYZER_PATH: string = 'bin/dependency_analyzer'; +export const MOCK_FILE_DEP_FILE_NAME: string = 'file_dependencies.json'; +export const MOCK_DEP_INPUT_FILE_NAME: string = 'depInput.txt'; +export const MOCK_RESOURCE_TABLE_FILE_NAME: string = 'ResourceTable.txt'; +export const MOCK_BUNDLE_NAME: string = 'com.example.mock'; +export const MOCK_RAWFILE_DIR_PATH: string = 'rawfile'; export const ETS_SUFFIX: string = '.ets'; export const ABC_SUFFIX: string = '.abc'; +export const DECL_ETS_SUFFIX: string = '.d.ets'; function getRootPath(): string { - return path.resolve(__dirname, '..'); + return path.resolve(__dirname, '..', '..', 'test'); +} + +function getResourcePath(): string { + return path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, 'resource'); } function changeFileExtension(file: string, targetExt: string, originExt = ''): string { @@ -61,4 +70,4 @@ function ensurePathExists(filePath: string): void { } } -export { getRootPath, changeFileExtension, getFileName, ensurePathExists }; +export { getRootPath, getResourcePath, changeFileExtension, getFileName, ensurePathExists }; diff --git a/arkui-plugins/test/utils/plugin-driver.ts b/arkui-plugins/test/utils/plugin-driver.ts index 46ce7f562..ef64a91bf 100644 --- a/arkui-plugins/test/utils/plugin-driver.ts +++ b/arkui-plugins/test/utils/plugin-driver.ts @@ -15,12 +15,15 @@ import { isNumber } from './safe-types'; import { Plugins, PluginContext, PluginHandler, PluginState, PluginExecutor } from '../../common/plugin-context'; +import { PluginStateId } from './shared-types'; import * as arkts from '@koalaui/libarkts'; export interface PluginDriver { initPlugins(plugins: Plugins[]): void; getSortedPlugins(state: arkts.Es2pandaContextState): PluginExecutor[] | undefined; getPluginContext(): PluginContext; + getPluginHistory(): PluginStateId[]; + clear(): void; } function toCamelCase(str: string): string { @@ -71,25 +74,45 @@ function selectPlugins(plugins: Plugins[], stage: PluginState): PluginExecutor[] class MockPluginDriver implements PluginDriver { private sortedPlugins: Map; - private context: PluginContext; + private history: Set; + private context: PluginContext | undefined; + + private static instance: PluginDriver | undefined; constructor() { this.sortedPlugins = new Map(); + this.history = new Set(); this.context = new PluginContext(); } + public static getInstance(): PluginDriver { + if (!this.instance) { + this.instance = new MockPluginDriver(); + } + return this.instance; + } + + private collectHistory(state: PluginState, plugins: PluginExecutor[]) { + for (const plugin of plugins) { + this.history.add(`${state}:${plugin.name}`); + } + this.history.add(state); + } + public initPlugins(plugins: Plugins[]): void { const pluginsByState = new Map(); Object.values(arkts.Es2pandaContextState) .filter(isNumber) .forEach((it) => { - const selected = selectPlugins(plugins, stateName(it)); + const state = stateName(it); + const selected = selectPlugins(plugins, state); if (selected.length > 0) { pluginsByState.set(it, selected); } else { pluginsByState.set(it, undefined); } + this.collectHistory(state, selected); }); this.sortedPlugins = pluginsByState; @@ -100,8 +123,20 @@ class MockPluginDriver implements PluginDriver { } public getPluginContext(): PluginContext { + if (!this.context) { + this.context = new PluginContext(); + } return this.context; } + + public getPluginHistory(): PluginStateId[] { + return Array.from(this.history); + } + + public clear(): void { + this.sortedPlugins.clear(); + this.context = undefined; + } } export { stateName, MockPluginDriver }; diff --git a/arkui-plugins/test/utils/plugin-tester.ts b/arkui-plugins/test/utils/plugin-tester.ts index 710456e21..1f1a6a59d 100644 --- a/arkui-plugins/test/utils/plugin-tester.ts +++ b/arkui-plugins/test/utils/plugin-tester.ts @@ -13,29 +13,30 @@ * limitations under the License. */ -import { ArktsConfigBuilder, BuildConfig, CompileFileInfo, MockArktsConfigBuilder, ModuleInfo } from './artkts-config'; -import { MockPluginDriver, PluginDriver, stateName } from './plugin-driver'; -import { isNumber } from './safe-types'; +import { ArktsConfigBuilder, MockArktsConfigBuilder } from './artkts-config'; +import { MockPluginDriver } from './plugin-driver'; import { - canProceedToState, - destroyConfig, - destroyContext, - initGlobal, - resetConfig, - resetContext, -} from './global'; -import { insertPlugin } from './compile'; -import { PluginExecutor, Plugins, PluginState } from '../../common/plugin-context'; -import { TesterCache } from './cache'; -import * as arkts from '@koalaui/libarkts'; -import * as fs from 'fs'; + BuildConfig, + CompileFileInfo, + PluginStateId, + PluginTestContext, + Processor, + SingleProgramContext, + TraceOptions, +} from './shared-types'; +import { HashGenerator } from './hash-generator'; +import { PluginTestContextCache } from './cache'; +import { Plugins, PluginState, ProjectConfig } from '../../common/plugin-context'; +import { concatObject } from './serializable'; +import { ProcessorBuilder } from './processor-builder'; +import { MainProcessor } from './processors/main-processor'; type TestParams = Parameters; type SkipFirstParam = T extends [unknown, ...infer Rest] ? Rest : never; type PluginTestHooks = { - [K in PluginState | `${PluginState}:${string}`]?: SkipFirstParam; + [K in PluginStateId]?: SkipFirstParam; }; type TestHooks = { @@ -44,134 +45,143 @@ type TestHooks = { afterEach?: Parameters; }; -export interface PluginTestContext { - scriptSnapshot?: string; - errors?: string[]; - warnings?: string[]; -} - export interface PluginTesterOptions { stopAfter: PluginState; buildConfig?: BuildConfig; + projectConfig?: ProjectConfig; + tracing?: TraceOptions; } class PluginTester { - private configBuilder: ArktsConfigBuilder; - private pluginDriver: PluginDriver; + private hashId: string; private describe: string; - private cache: TesterCache; + private configBuilder: ArktsConfigBuilder; + private taskProcessor?: Processor; + private resolve?: Promise; - constructor(describe: string, buildConfig?: BuildConfig) { + constructor(describe: string, buildConfig?: BuildConfig, projectConfig?: ProjectConfig) { this.describe = describe; - this.configBuilder = new MockArktsConfigBuilder(buildConfig); - this.pluginDriver = new MockPluginDriver(); - this.cache = TesterCache.getInstance(); + this.hashId = HashGenerator.getInstance().dynamicSha1Id(describe, 13); + this.configBuilder = new MockArktsConfigBuilder(this.hashId, buildConfig, projectConfig); } - private loadPluginDriver(plugins: Plugins[]): void { - this.pluginDriver.initPlugins(plugins); + private clear(): void { + this.clearCache(); + this.configBuilder.clear(); + this.taskProcessor?.clear(); + MockPluginDriver.getInstance().clear(); } - private test( - key: PluginState | `${PluginState}:${string}`, - index: arkts.Es2pandaContextState, - testName: string, - pluginHooks: PluginTestHooks, - plugin?: PluginExecutor - ): void { - let cached: boolean = false; - const cacheKey: string = `${testName}-${key}`; - if (index > arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED) { - return; - } - if (canProceedToState(index)) { - arkts.proceedToState(index); - } - if (plugin) { - insertPlugin(this.pluginDriver, plugin, index); - this.captureContext(cacheKey); - cached = true; - } - const hook: SkipFirstParam | undefined = pluginHooks[key]; - if (!!hook) { - if (!cached) this.captureContext(cacheKey); - test(testName, hook[0]?.bind(this.cache.get(cacheKey)), hook[1]); + private clearCache(): void { + const moduleInfo = this.configBuilder.moduleInfos.get(this.configBuilder.moduleRootPath)!; + const fileHistory = moduleInfo.compileFileInfos.map((fileInfo) => fileInfo.fileName) ?? []; + const pluginHistory = MockPluginDriver.getInstance().getPluginHistory(); + for (const pluginStateId of pluginHistory) { + const abcKey = this.getCacheKey(pluginStateId); + const externalKey = this.getCacheKey(pluginStateId, true); + for (const fileName of fileHistory) { + PluginTestContextCache.getInstance().delete(`${abcKey}:${fileName}`); + PluginTestContextCache.getInstance().delete(`${externalKey}:${fileName}`); + } } } - private captureContext(cacheKey: string): void { - try { - // TODO: add error/warning handling after plugin - const context: PluginTestContext = this.cache.get(cacheKey) ?? {}; - const script: arkts.EtsScript = arkts.EtsScript.fromContext(); - context.scriptSnapshot = script.dumpSrc(); - this.cache.set(cacheKey, context); - } catch (e) { - // Do nothing + private getCacheKey(pluginStateId: PluginStateId, isExternal?: boolean) { + return [this.hashId, !!isExternal ? 'external' : 'abc', pluginStateId].join(':'); + } + + private prepareContext(pluginStateId: PluginStateId, fileInfos: CompileFileInfo[]): PluginTestContext { + const fileNames: string[] = fileInfos.map((fileInfo) => fileInfo.fileName); + + const abcKey = this.getCacheKey(pluginStateId); + const externalKey = this.getCacheKey(pluginStateId, true); + + const sourceContexts: Record = {}; + let declContexts: Record = {}; + fileNames.forEach((fileName) => { + const sourceKey = `${abcKey}:${fileName}`; + const sourceContext = PluginTestContextCache.getInstance().get(sourceKey) ?? {}; + if (!!sourceContext.declContexts) { + declContexts = concatObject(declContexts, sourceContext.declContexts); + delete sourceContext.declContexts; + } + sourceContexts[fileName] = sourceContext; + + const declKey = `${externalKey}:${fileName}`; + const declContext = PluginTestContextCache.getInstance().get(declKey) ?? {}; + declContexts = concatObject(declContexts, declContext.declContexts ?? {}); + }); + + return { sourceContexts, declContexts }; + } + + private findContext(testContext: PluginTestContext | undefined, fileName?: string): PluginTestContext | undefined { + if (!testContext) { + return undefined; } + if (!testContext.sourceContexts) { + return { declContexts: testContext.declContexts }; + } + const sourceContext = fileName + ? testContext.sourceContexts[fileName] + : Object.values(testContext.sourceContexts)[Symbol.iterator]().next().value; + return { ...sourceContext, declContexts: testContext.declContexts }; } - private proceedToState( - state: PluginState, - index: arkts.Es2pandaContextState, + private test( + pluginStateId: PluginStateId, testName: string, - pluginHooks: PluginTestHooks, - plugins?: PluginExecutor[] + hook: SkipFirstParam | undefined, + compileFiles: CompileFileInfo[], + fileName?: string ): void { - if (plugins && plugins.length > 0) { - plugins.forEach((plugin) => { - const pluginName: string = plugin.name; - const key: `${PluginState}:${string}` = `${state}:${pluginName}`; - this.test(key, index, `[${key}] ${testName}`, pluginHooks, plugin); - }); + if (!!hook) { + const that = this; + test( + testName, + async () => { + let context: PluginTestContext | undefined; + await that.resolve?.then(async () => { + const testContext = this.prepareContext(pluginStateId, compileFiles ?? []); + context = this.findContext(testContext, fileName); + }); + hook[0]?.bind(context)(undefined as any); + }, + hook[1] + ); } - this.test(state, index, `[${state}] ${testName}`, pluginHooks); } - private singleFileCompile( - fileInfo: CompileFileInfo, - moduleInfo: ModuleInfo, - testName: string, - pluginHooks: PluginTestHooks, - stopAfter: PluginState - ): void { - let shouldStop: boolean = false; - - Object.values(arkts.Es2pandaContextState) - .filter(isNumber) - .forEach((it) => { - if (shouldStop) { - return; - } - const state: PluginState = stateName(it); - const plugins: PluginExecutor[] | undefined = this.pluginDriver.getSortedPlugins(it); - this.proceedToState( - state, - it, - `${moduleInfo.packageName} - ${fileInfo.fileName}: ${testName}`, - pluginHooks, - plugins - ); - shouldStop = state === stopAfter; - }); + private pluginTests(key: PluginStateId, testName: string, pluginHooks: PluginTestHooks): void { + const moduleInfo = this.configBuilder.moduleInfos.get(this.configBuilder.moduleRootPath)!; + const compileFiles = moduleInfo.compileFileInfos; + compileFiles?.forEach((fileInfo) => { + const fileName = fileInfo.fileName; + const name: string = `[${key}] ${moduleInfo.packageName} - ${fileName}: ${testName}`; + this.test(key, name, pluginHooks[`${key}:${fileName}`], compileFiles, fileName); + }); + const name: string = `[${key}] ${moduleInfo.packageName}: ${testName}`; + this.test(key, name, pluginHooks[key], compileFiles); } - private traverseFile(testName: string, pluginHooks: PluginTestHooks, stopAfter: PluginState): void { - let once: boolean = false; - this.configBuilder.moduleInfos.forEach((moduleInfo) => { - moduleInfo.compileFileInfos.forEach((fileInfo) => { - if (!once) { - initGlobal(fileInfo, this.configBuilder.isDebug); - once = true; - } else { - const source: string = fs.readFileSync(fileInfo.filePath).toString(); - resetContext(source); - } - this.singleFileCompile(fileInfo, moduleInfo, testName, pluginHooks, stopAfter); - }); + private compileTests(testName: string, pluginHooks: PluginTestHooks): void { + const history = MockPluginDriver.getInstance().getPluginHistory(); + history.forEach((key) => { + this.pluginTests(key, testName, pluginHooks); }); } + private async compile(plugins: Plugins[], stopAfter?: PluginState, tracing?: TraceOptions): Promise { + this.taskProcessor = ProcessorBuilder.build( + MainProcessor, + this.hashId, + this.configBuilder.buildConfig, + this.configBuilder.projectConfig, + tracing + ); + return this.taskProcessor.invokeWorkers(plugins, stopAfter); + } + run( testName: string, plugins: Plugins[], @@ -180,11 +190,11 @@ class PluginTester { testHooks?: TestHooks ): void { if (!!options.buildConfig) { - this.configBuilder = new MockArktsConfigBuilder(options.buildConfig); + this.configBuilder = this.configBuilder.withBuildConfig(options.buildConfig); + } + if (!!options.projectConfig) { + this.configBuilder = this.configBuilder.withProjectConfig(options.projectConfig); } - - this.cache.clear(); - this.loadPluginDriver(plugins); const that = this; describe(this.describe, () => { @@ -197,11 +207,13 @@ class PluginTester { if (testHooks?.afterEach) { afterEach(...testHooks.afterEach); } + afterAll(() => { - destroyContext(); - destroyConfig(); + that.clear(); }); - that.traverseFile(testName, pluginHooks, options.stopAfter); + + that.resolve = that.compile(plugins, options.stopAfter, options.tracing); + that.compileTests(testName, pluginHooks); }); } } diff --git a/arkui-plugins/test/utils/plugins/before-memo-no-recheck.ts b/arkui-plugins/test/utils/plugins/before-memo-no-recheck.ts new file mode 100644 index 000000000..f6a2c0c13 --- /dev/null +++ b/arkui-plugins/test/utils/plugins/before-memo-no-recheck.ts @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { PluginContext, Plugins } from '../../../common/plugin-context'; +import { ProgramVisitor } from '../../../common/program-visitor'; +import { EXTERNAL_SOURCE_PREFIX_NAMES } from '../../../common/predefines'; +import { MemoVisitor } from '../../../collectors/memo-collectors/memo-visitor'; + +/** + * AfterCheck before-memo-visit and cache any node that should be unmemoized with no recheck AST. + */ +export const beforeMemoNoRecheck: Plugins = { + name: 'before-memo-no-recheck', + checked(this: PluginContext): arkts.EtsScript | undefined { + let script: arkts.EtsScript | undefined; + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; + if (!!contextPtr) { + let program = arkts.getOrUpdateGlobalContext(contextPtr).program; + script = program.astNode; + const builderLambdaTransformer = new MemoVisitor(); + const programVisitor = new ProgramVisitor({ + pluginName: beforeMemoNoRecheck.name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, + visitors: [builderLambdaTransformer], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this, + }); + program = programVisitor.programVisitor(program); + script = program.astNode; + return script; + } + return script; + }, +}; \ No newline at end of file diff --git a/arkui-plugins/test/utils/plugins/builder-lambda-no-recheck.ts b/arkui-plugins/test/utils/plugins/builder-lambda-no-recheck.ts index 69e2b23ad..a770c96b0 100644 --- a/arkui-plugins/test/utils/plugins/builder-lambda-no-recheck.ts +++ b/arkui-plugins/test/utils/plugins/builder-lambda-no-recheck.ts @@ -26,11 +26,11 @@ export const builderLambdaNoRecheck: Plugins = { name: 'builder-lambda-no-recheck', checked(this: PluginContext): arkts.EtsScript | undefined { let script: arkts.EtsScript | undefined; - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; script = program.astNode; - const builderLambdaTransformer = new BuilderLambdaTransformer(); + const builderLambdaTransformer = new BuilderLambdaTransformer(this.getProjectConfig()); const programVisitor = new ProgramVisitor({ pluginName: builderLambdaNoRecheck.name, state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, @@ -40,7 +40,6 @@ export const builderLambdaNoRecheck: Plugins = { }); program = programVisitor.programVisitor(program); script = program.astNode; - arkts.GlobalInfo.getInfoInstance().reset(); return script; } return script; diff --git a/arkui-plugins/test/utils/plugins/index.ts b/arkui-plugins/test/utils/plugins/index.ts index 4858b84b2..ab232c217 100644 --- a/arkui-plugins/test/utils/plugins/index.ts +++ b/arkui-plugins/test/utils/plugins/index.ts @@ -17,6 +17,7 @@ export * from './struct-to-component'; // AfterCheck +export * from './before-memo-no-recheck'; export * from './builder-lambda-no-recheck'; export * from './memo-no-recheck'; export * from './struct-no-recheck'; diff --git a/arkui-plugins/test/utils/plugins/memo-no-recheck.ts b/arkui-plugins/test/utils/plugins/memo-no-recheck.ts index ff018cec5..9396b34e5 100644 --- a/arkui-plugins/test/utils/plugins/memo-no-recheck.ts +++ b/arkui-plugins/test/utils/plugins/memo-no-recheck.ts @@ -16,12 +16,13 @@ import * as arkts from '@koalaui/libarkts'; import { PluginContext, Plugins } from '../../../common/plugin-context'; import { ProgramVisitor } from '../../../common/program-visitor'; -import { EXTERNAL_SOURCE_PREFIX_NAMES } from '../../../common/predefines'; +import { EXTERNAL_SOURCE_PREFIX_NAMES, EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK } from '../../../common/predefines'; import { PositionalIdTracker } from '../../../memo-plugins/utils'; import { ParameterTransformer } from '../../../memo-plugins/parameter-transformer'; import { ReturnTransformer } from '../../../memo-plugins/return-transformer'; import { SignatureTransformer } from '../../../memo-plugins/signature-transformer'; import { FunctionTransformer } from '../../../memo-plugins/function-transformer'; +import { InternalsTransformer } from '../../../memo-plugins/internal-transformer'; /** * AfterCheck unmemoizeTransform with no recheck AST. @@ -29,8 +30,9 @@ import { FunctionTransformer } from '../../../memo-plugins/function-transformer' export const memoNoRecheck: Plugins = { name: 'memo-no-recheck', checked(this: PluginContext): arkts.EtsScript | undefined { - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { + const isFrameworkMode = !!this.getProjectConfig()?.frameworkMode; let program = arkts.getOrUpdateGlobalContext(contextPtr).program; let script = program.astNode; const positionalIdTracker = new PositionalIdTracker(arkts.getFileName(), false); @@ -39,20 +41,30 @@ export const memoNoRecheck: Plugins = { }); const returnTransformer = new ReturnTransformer(); const signatureTransformer = new SignatureTransformer(); + let internalsTransformer: InternalsTransformer | undefined; + if (isFrameworkMode) { + internalsTransformer = new InternalsTransformer({ positionalIdTracker }); + } const functionTransformer = new FunctionTransformer({ positionalIdTracker, parameterTransformer, returnTransformer, signatureTransformer, + internalsTransformer, + useCache: arkts.NodeCache.getInstance().isCollected() }); + const skipPrefixNames = isFrameworkMode + ? EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK + : EXTERNAL_SOURCE_PREFIX_NAMES; const programVisitor = new ProgramVisitor({ pluginName: memoNoRecheck.name, state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, visitors: [functionTransformer], - skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + skipPrefixNames, pluginContext: this, }); program = programVisitor.programVisitor(program); + arkts.NodeCache.getInstance().clear(); script = program.astNode; return script; } diff --git a/arkui-plugins/test/utils/plugins/recheck.ts b/arkui-plugins/test/utils/plugins/recheck.ts index 085d3731e..2c3559df4 100644 --- a/arkui-plugins/test/utils/plugins/recheck.ts +++ b/arkui-plugins/test/utils/plugins/recheck.ts @@ -23,7 +23,7 @@ export const recheck: Plugins = { name: 'recheck', checked(this: PluginContext): arkts.EtsScript | undefined { let script: arkts.EtsScript | undefined; - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; script = program.astNode; diff --git a/arkui-plugins/test/utils/plugins/struct-no-recheck.ts b/arkui-plugins/test/utils/plugins/struct-no-recheck.ts index c564b71c7..87a7bdbd0 100644 --- a/arkui-plugins/test/utils/plugins/struct-no-recheck.ts +++ b/arkui-plugins/test/utils/plugins/struct-no-recheck.ts @@ -26,7 +26,7 @@ export const structNoRecheck: Plugins = { name: 'struct-no-recheck', checked(this: PluginContext): arkts.EtsScript | undefined { let script: arkts.EtsScript | undefined; - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; script = program.astNode; @@ -40,7 +40,6 @@ export const structNoRecheck: Plugins = { }); program = programVisitor.programVisitor(program); script = program.astNode; - arkts.GlobalInfo.getInfoInstance().reset(); return script; } return script; diff --git a/arkui-plugins/test/utils/plugins/struct-to-component.ts b/arkui-plugins/test/utils/plugins/struct-to-component.ts index 14d494c05..ddb0281d2 100644 --- a/arkui-plugins/test/utils/plugins/struct-to-component.ts +++ b/arkui-plugins/test/utils/plugins/struct-to-component.ts @@ -26,11 +26,13 @@ export const structToComponent: Plugins = { name: 'struct-to-component', parsed(this: PluginContext): arkts.EtsScript | undefined { let script: arkts.EtsScript | undefined; - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; script = program.astNode; - const componentTransformer = new ComponentTransformer(); + const componentTransformer = new ComponentTransformer({ + projectConfig: this.getProjectConfig(), + }); const programVisitor = new ProgramVisitor({ pluginName: structToComponent.name, state: arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, diff --git a/arkui-plugins/test/utils/plugins/ui-no-recheck.ts b/arkui-plugins/test/utils/plugins/ui-no-recheck.ts index dec4b5296..5fbc6c3fb 100644 --- a/arkui-plugins/test/utils/plugins/ui-no-recheck.ts +++ b/arkui-plugins/test/utils/plugins/ui-no-recheck.ts @@ -26,7 +26,7 @@ export const uiNoRecheck: Plugins = { name: 'ui-no-recheck', checked(this: PluginContext): arkts.EtsScript | undefined { let script: arkts.EtsScript | undefined; - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; script = program.astNode; @@ -40,7 +40,6 @@ export const uiNoRecheck: Plugins = { }); program = programVisitor.programVisitor(program); script = program.astNode; - arkts.GlobalInfo.getInfoInstance().reset(); return script; } return script; diff --git a/arkui-plugins/test/utils/processor-builder.ts b/arkui-plugins/test/utils/processor-builder.ts new file mode 100644 index 000000000..7be32e773 --- /dev/null +++ b/arkui-plugins/test/utils/processor-builder.ts @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ProjectConfig } from 'common/plugin-context'; +import { BuildConfig, Processor, TraceOptions } from './shared-types'; + +class ProcessorBuilder { + static build( + Processor: { + new ( + hashId: string, + buildConfig?: BuildConfig, + projectConfig?: ProjectConfig, + tracing?: TraceOptions + ): Processor; + }, + hashId: string, + buildConfig?: BuildConfig, + projectConfig?: ProjectConfig, + tracing?: TraceOptions + ): Processor { + return new Processor(hashId, buildConfig, projectConfig, tracing); + } +} + +export { ProcessorBuilder }; diff --git a/arkui-plugins/test/utils/processors/base-processor.ts b/arkui-plugins/test/utils/processors/base-processor.ts new file mode 100644 index 000000000..53e79ea52 --- /dev/null +++ b/arkui-plugins/test/utils/processors/base-processor.ts @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Plugins, PluginState, ProjectConfig } from '../../../common/plugin-context'; +import { mockBuildConfig, mockProjectConfig } from '../artkts-config'; +import { ArkTSConfigContextCache } from '../cache'; +import { BuildConfig, CompileFileInfo, Processor, TraceOptions } from '../shared-types'; + +abstract class BaseProcessor implements Processor { + hashId: string; + buildConfig: BuildConfig; + projectConfig: ProjectConfig; + tracing: TraceOptions; + cacheDir: string; + arktsConfigFile: string; + compileFiles: Map; + + constructor(hashId: string, buildConfig?: BuildConfig, projectConfig?: ProjectConfig, tracing?: TraceOptions) { + this.hashId = hashId; + this.tracing = tracing ?? { externalSourceNames: [] }; + + const _buildConfig: BuildConfig = buildConfig ?? mockBuildConfig(); + this.buildConfig = _buildConfig; + this.cacheDir = _buildConfig.cachePath; + this.arktsConfigFile = this.getArktsConfigFile(); + this.compileFiles = this.getCompileFiles(); + + const _projectConfig: ProjectConfig = projectConfig ?? mockProjectConfig(); + this.projectConfig = _projectConfig; + } + + private getArktsConfigFile(): string { + const arktsConfigFile = ArkTSConfigContextCache.getInstance().get(this.hashId)?.arktsConfigFile; + if (!arktsConfigFile) { + const err = `[${this.hashId}] TaskProcessor cannot get arktsConfigFile`; + console.error(err); + throw new Error(err); + } + return arktsConfigFile; + } + + private getCompileFiles(): Map { + const compileFiles = ArkTSConfigContextCache.getInstance().get(this.hashId)?.compileFiles; + if (!compileFiles) { + const err = `[${this.hashId}] TaskProcessor cannot get compileFiles`; + console.error(err); + throw new Error(err); + } + return compileFiles; + } + + abstract invokeWorkers(plugins: Plugins[], stopAfter?: PluginState): Promise; + + abstract clear(): void; +} + +export { BaseProcessor }; diff --git a/arkui-plugins/test/utils/processors/main-processor.ts b/arkui-plugins/test/utils/processors/main-processor.ts new file mode 100644 index 000000000..4b8f2399d --- /dev/null +++ b/arkui-plugins/test/utils/processors/main-processor.ts @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import EventEmitter from 'events'; +import { + BuildConfig, + CompileFileInfo, + CompileStrategy, + JobInfo, + PluginTestContext, + ProcessEvent, + TraceOptions, +} from '../shared-types'; +import { BaseProcessor } from './base-processor'; +import { PluginTestContextCache } from '../cache'; +import { concatObject, serializable } from '../serializable'; +import { Plugins, PluginState, ProjectConfig } from '../../../common/plugin-context'; +import { createGlobalConfig, destroyGlobalConfig } from '../global'; +import { compileAbcWithExternal } from '../compile'; + +class MainProcessor extends BaseProcessor { + filePaths: string[]; + + readonly emitter: EventEmitter = new EventEmitter(); + + constructor(hashId: string, buildConfig?: BuildConfig, projectConfig?: ProjectConfig, tracing?: TraceOptions) { + super(hashId, buildConfig, projectConfig, tracing); + this.filePaths = this.getCompileFilePaths(); + } + + private getCompileFilePaths(): string[] { + return Array.from(this.compileFiles.values()).map((fileInfo) => fileInfo.filePath); + } + + private subscribe(): void { + this.emitter.on('TASK_COLLECT', (msg) => { + const sourceType = 'abc'; + const pluginStateId = msg.pluginStateId; + const fileName = msg.fileName; + const pluginTestContext = msg.pluginTestContext as PluginTestContext; + const key = `${this.hashId}:${sourceType}:${pluginStateId}:${fileName}`; + let currentPluginTestContext; + if (PluginTestContextCache.getInstance().has(key)) { + const oldContext = PluginTestContextCache.getInstance().get(key)!; + currentPluginTestContext = concatObject(oldContext, pluginTestContext); + } else { + currentPluginTestContext = pluginTestContext; + } + PluginTestContextCache.getInstance().set(key, currentPluginTestContext); + }); + } + + private assignTask(fileInfo: CompileFileInfo, plugins: Plugins[], stopAfter?: PluginState): void { + const jobInfo: JobInfo = { + id: 'compile-abc-with-external', + isCompileAbc: CompileStrategy.ABC_WTIH_EXTERNAL, + compileFileInfo: fileInfo, + buildConfig: serializable(this.buildConfig), + projectConfig: serializable(this.projectConfig), + plugins, + stopAfter, + filePaths: this.filePaths, + }; + compileAbcWithExternal(this.emitter, jobInfo, this.tracing); + } + + async invokeWorkers(plugins: Plugins[], stopAfter?: PluginState): Promise { + return new Promise((resolve) => { + const fileInfo: CompileFileInfo = this.compileFiles.values().next().value!; + const config = createGlobalConfig(fileInfo, true, false); + this.subscribe(); + this.emitter.on('TASK_FINISH', (msg) => { + console.log('All tasks completed. Exiting...'); + destroyGlobalConfig(config, false); + resolve(); + }); + this.assignTask(fileInfo, plugins, stopAfter); + }); + } + + clear(): void {} +} + +export { MainProcessor }; diff --git a/arkui-plugins/test/utils/processors/task-processor.ts b/arkui-plugins/test/utils/processors/task-processor.ts new file mode 100644 index 000000000..a66b89eb4 --- /dev/null +++ b/arkui-plugins/test/utils/processors/task-processor.ts @@ -0,0 +1,517 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as os from 'os'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as child_process from 'child_process'; +import EventEmitter from 'events'; +import { + CompileStrategy, + type BuildConfig, + type CompileFileInfo, + type JobInfo, + type PluginTestContext, + type ProcessEvent, + type TraceOptions, +} from '../shared-types'; +import { + DECL_ETS_SUFFIX, + ensurePathExists, + getFileName, + MOCK_DEP_INPUT_FILE_NAME, + MOCK_FILE_DEP_FILE_NAME, +} from '../path-config'; +import { FileDependencyContextCache, PluginTestContextCache } from '../cache'; +import { HashGenerator } from '../hash-generator'; +import { createGlobalConfig, createGlobalContextPtr, destroyGlobalConfig, destroyGlobalContextPtr } from '../global'; +import { Plugins, PluginState, ProjectConfig } from '../../../common/plugin-context'; +import { concatObject, serializable } from '../serializable'; +import { compileAbc, compileExternalProgram } from '../compile'; +import { BaseProcessor } from './base-processor'; + +interface Job { + id: string; + isDeclFile: boolean; + isInCycle?: boolean; + fileList: string[]; + dependencies: string[]; + dependants: string[]; + isAbcJob: boolean; +} + +interface Queues { + externalProgramQueue: Job[]; + abcQueue: Job[]; +} + +interface FileDepsInfo { + dependencies: Record; + dependants: Record; +} + +interface WorkerInfo { + isIdle: boolean; +} + +function getDepAnalyzerCmd( + depAnalyzerPath: string, + depInputFile: string, + entryFiles: Set, + outputFile: string, + arktsConfigFile: string +): string[] { + const depAnalyzerCmd: string[] = [`"${depAnalyzerPath}"`]; + + let depInputContent = ''; + entryFiles.forEach((file: string) => { + depInputContent += file + os.EOL; + }); + fs.writeFileSync(depInputFile, depInputContent); + + depAnalyzerCmd.push(`@"${depInputFile}"`); + depAnalyzerCmd.push(`--output=${outputFile}`); + depAnalyzerCmd.push(`--arktsconfig=${arktsConfigFile}`); + + return depAnalyzerCmd; +} + +function dfs(node: string, visited: Set, adjacencyList: Record, order: string[]) { + visited.add(node); + for (const neighbor of adjacencyList[node]) { + if (!visited.has(neighbor)) { + dfs(neighbor, visited, adjacencyList, order); + } + } + order.push(node); +} + +function reverseDfs( + node: string, + component: Set, + visited: Set, + reverseAdjacencyList: Record +) { + visited.add(node); + component.add(node); + for (const neighbor of reverseAdjacencyList[node]) { + if (!visited.has(neighbor)) { + reverseDfs(neighbor, component, visited, reverseAdjacencyList); + } + } +} + +function findStronglyConnectedComponents(graph: FileDepsInfo): Map> { + const adjacencyList: Record = {}; + const reverseAdjacencyList: Record = {}; + const allNodes = new Set(); + for (const node in graph.dependencies) { + allNodes.add(node); + graph.dependencies[node].forEach((dep) => allNodes.add(dep)); + } + for (const node in graph.dependants) { + allNodes.add(node); + graph.dependants[node].forEach((dep) => allNodes.add(dep)); + } + Array.from(allNodes).forEach((node) => { + adjacencyList[node] = graph.dependencies[node] || []; + reverseAdjacencyList[node] = graph.dependants[node] || []; + }); + const visited = new Set(); + const order: string[] = []; + Array.from(allNodes).forEach((node) => { + if (!visited.has(node)) { + dfs(node, visited, adjacencyList, order); + } + }); + visited.clear(); + const components = new Map>(); + for (let i = order.length - 1; i >= 0; i--) { + const node = order[i]; + if (!visited.has(node)) { + const component = new Set(); + reverseDfs(node, component, visited, reverseAdjacencyList); + if (component.size > 1) { + const sortedFiles = Array.from(component).sort(); + const hashKey = HashGenerator.getInstance().staticSha1Id(sortedFiles.join('|'), 13); + components.set(hashKey, component); + } + } + } + return components; +} + +function getJobDependencies(fileDeps: string[], cycleFiles: Map): Set { + const depJobList: Set = new Set(); + fileDeps.forEach((file) => { + if (!cycleFiles.has(file)) { + depJobList.add('0' + file); + } else { + cycleFiles.get(file)?.forEach((f) => { + depJobList.add(f); + }); + } + }); + + return depJobList; +} + +function getJobDependants(fileDeps: string[], cycleFiles: Map): Set { + let depJobList: Set = new Set(); + fileDeps.forEach((file) => { + if (!file.endsWith(DECL_ETS_SUFFIX)) { + depJobList.add('1' + file); + } + if (cycleFiles.has(file)) { + cycleFiles.get(file)?.forEach((f) => { + depJobList.add(f); + }); + } else { + depJobList.add('0' + file); + } + }); + + return depJobList; +} + +function insertJobDependantsButSelf(jobMap: Record, id: string, dependants: Set): Set { + jobMap[id].dependants.forEach((dep) => { + dependants.add(dep); + }); + if (dependants.has(id)) { + dependants.delete(id); + } + return dependants; +} + +function createExternalProgramJob(id: string, fileList: string[], dependencies: Set, isInCycle?: boolean): Job { + return { + id, + fileList, + isDeclFile: true, + isInCycle, + isAbcJob: false, + dependencies: Array.from(dependencies), + dependants: [], + }; +} + +function createAbcJob(id: string, fileList: string[], dependencies: Set, isInCycle?: boolean): Job { + return { + id, + isDeclFile: false, + isInCycle, + isAbcJob: true, + fileList, + dependencies: Array.from(dependencies), + dependants: [], + }; +} + +function addJobToQueues(job: Job, queues: Queues): void { + if (queues.externalProgramQueue.some((j) => j.id === job.id) || queues.abcQueue.some((j) => j.id === job.id)) { + return; + } + if (!job.isAbcJob) { + queues.externalProgramQueue.push(job); + } else { + queues.abcQueue.push(job); + } +} + +class TaskProcessor extends BaseProcessor { + entryFiles: Set; + depAnalyzerPath: string; + depInputFile: string; + fileDepsInfoJson: string; + jobMap: Record; + jobQueues: Queues; + + readonly emitter: EventEmitter = new EventEmitter(); + private worker!: WorkerInfo; + + constructor(hashId: string, buildConfig?: BuildConfig, projectConfig?: ProjectConfig, tracing?: TraceOptions) { + super(hashId, buildConfig, projectConfig, tracing); + this.entryFiles = new Set(this.buildConfig.compileFiles as string[]); + this.depAnalyzerPath = this.buildConfig.depAnalyzerPath; + this.depInputFile = path.resolve(this.buildConfig.cachePath, this.hashId, MOCK_DEP_INPUT_FILE_NAME); + this.fileDepsInfoJson = path.resolve(this.buildConfig.cachePath, this.hashId, MOCK_FILE_DEP_FILE_NAME); + + this.generateFileDependencies(); + this.cacheFileDependencies(); + this.jobMap = this.collectCompileJobs(); + this.jobQueues = this.initCompileQueues(); + } + + private cacheFileDependencies(): void { + const depInputFile: string = this.arktsConfigFile; + const fileDepsInfoJson: string = this.fileDepsInfoJson; + FileDependencyContextCache.getInstance().set(this.hashId, { depInputFile, fileDepsInfoJson }); + } + + private generateFileDependencies(): void { + ensurePathExists(this.depInputFile); + ensurePathExists(this.fileDepsInfoJson); + const depAnalyzerCmd: string[] = getDepAnalyzerCmd( + this.depAnalyzerPath, + this.depInputFile, + this.entryFiles, + this.fileDepsInfoJson, + this.arktsConfigFile + ); + const depAnalyzerCmdStr: string = depAnalyzerCmd.join(' '); + try { + child_process.execSync(depAnalyzerCmdStr).toString(); + } catch (error) { + const err = `[${this.hashId}] TaskProcessor generateFileDependencies failed: ${error}`; + console.error(err); + throw new Error(err); + } + } + + private collectJobsInDependants( + jobMap: Record, + cycleFiles: Map, + dependantMap: [string, string[]] + ): void { + const [key, value] = dependantMap; + const dependants = getJobDependants(value, cycleFiles); + if (cycleFiles.has(key)) { + const externalProgramJobIds = cycleFiles.get(key)!; + externalProgramJobIds.forEach((id) => { + jobMap[id].dependants = Array.from(insertJobDependantsButSelf(jobMap, id, dependants)); + }); + } else { + const id = '0' + key; + jobMap[id].dependants = Array.from(insertJobDependantsButSelf(jobMap, id, dependants)); + } + } + + private collectJobsInDependencies( + jobMap: Record, + cycleFiles: Map, + cycleGroups: Map>, + dependencyMap: [string, string[]] + ): void { + const [key, value] = dependencyMap; + const dependencies = getJobDependencies(value, cycleFiles); + // Create generate abc job + if (!key.endsWith(DECL_ETS_SUFFIX)) { + const abcJobId: string = '1' + key; + jobMap[abcJobId] = createAbcJob(abcJobId, [key], dependencies, cycleFiles.has(key)); + } + // Create cache external job + if (cycleFiles.has(key)) { + const externalProgramJobIds = cycleFiles.get(key)!; + externalProgramJobIds.forEach((id) => { + const fileList: string[] = Array.from(cycleGroups.get(id)!); + if (dependencies.has(id)) { + dependencies.delete(id); + } + jobMap[id] = createExternalProgramJob(id, fileList, dependencies, true); + }); + } else { + const id = '0' + key; + const fileList: string[] = [key]; + if (dependencies.has(id)) { + dependencies.delete(id); + } + jobMap[id] = createExternalProgramJob(id, fileList, dependencies); + } + // register compileFiles for declaration files + if (key.endsWith(DECL_ETS_SUFFIX)) { + const fileInfo: CompileFileInfo = { + filePath: key, + dependentFiles: [], + abcFilePath: '', + arktsConfigFile: this.arktsConfigFile, + fileName: getFileName(key), + stdLibPath: '', + }; + + if (!this.compileFiles.has(key)) { + this.compileFiles.set(key, fileInfo); + } + } + } + + private collectCompileJobs(): Record { + const data = fs.readFileSync(this.fileDepsInfoJson, 'utf-8'); + if (data.length === 0) { + const err = `[${this.hashId}] TaskProcessor cannot read fileDepsInfoJson`; + console.error(err); + throw new Error(err); + } + const fileDepsInfo: FileDepsInfo = JSON.parse(data) as FileDepsInfo; + Object.keys(fileDepsInfo.dependants).forEach((file) => { + if (!(file in fileDepsInfo.dependencies)) { + fileDepsInfo.dependencies[file] = []; + } + }); + const cycleGroups = findStronglyConnectedComponents(fileDepsInfo); + const cycleFiles: Map = new Map(); + cycleGroups.forEach((value: Set, key: string) => { + value.forEach((file) => { + cycleFiles.set(file, [key]); + }); + }); + const jobMap: Record = {}; + Object.entries(fileDepsInfo.dependencies).forEach((dependencyMap) => { + this.collectJobsInDependencies(jobMap, cycleFiles, cycleGroups, dependencyMap); + }); + Object.entries(fileDepsInfo.dependants).forEach((dependantMap) => { + this.collectJobsInDependants(jobMap, cycleFiles, dependantMap); + }); + return jobMap; + } + + private initCompileQueues(): Queues { + const queues: Queues = { externalProgramQueue: [], abcQueue: [] }; + Object.values(this.jobMap).forEach((job) => { + if (job.dependencies.length === 0) { + addJobToQueues(job, queues); + } + }); + return queues; + } + + private assignTaskToIdleWorker( + processingJobs: Set, + globalContextPtr: number, + plugins: Plugins[], + stopAfter?: PluginState + ) { + let job: Job | undefined; + let jobInfo: JobInfo | undefined; + if (this.jobQueues.externalProgramQueue.length > 0) { + job = this.jobQueues.externalProgramQueue.shift()!; + jobInfo = { + id: job.id, + isCompileAbc: CompileStrategy.EXTERNAL, + }; + } else if (this.jobQueues.abcQueue.length > 0) { + job = this.jobQueues.abcQueue.shift()!; + jobInfo = { + id: job.id, + isCompileAbc: CompileStrategy.ABC, + }; + } + + if (!!job && !!jobInfo) { + processingJobs.add(job.id); + jobInfo.compileFileInfo = this.compileFiles.get(job.fileList[0]); + jobInfo.buildConfig = serializable(this.buildConfig); + jobInfo.projectConfig = serializable(this.projectConfig); + jobInfo.plugins = plugins; + jobInfo.globalContextPtr = globalContextPtr; + jobInfo.stopAfter = stopAfter; + + this.worker.isIdle = false; + this.emitter.emit('ASSIGN_TASK', { jobInfo }); + } + } + + private checkAllTasksDone(): boolean { + return this.jobQueues.externalProgramQueue.length === 0 && this.worker.isIdle; + } + + private addDependantJobToQueues(jobId: string): void { + const completedJob = this.jobMap[jobId]; + completedJob.dependants.forEach((depJobId) => { + const depJob = this.jobMap[depJobId]; + const depIndex = depJob.dependencies.indexOf(jobId); + if (depIndex !== -1) { + depJob.dependencies.splice(depIndex, 1); + if (depJob.dependencies.length === 0) { + addJobToQueues(depJob, this.jobQueues); + } + } + }); + } + + private subscribe() { + this.emitter.on('ASSIGN_TASK', (msg) => { + const job = msg.jobInfo; + if (job.isCompileAbc === CompileStrategy.ABC) { + compileAbc(this.emitter, job, this.tracing); + } else if (job.isCompileAbc === CompileStrategy.EXTERNAL) { + compileExternalProgram(this.emitter, job, this.tracing); + } + this.emitter.emit('TASK_FINISH', { jobId: job.id }); + }); + this.emitter.on('EXIT', () => { + this.worker.isIdle = true; + console.log(`worker exiting...`); + }); + this.emitter.on('TASK_COLLECT', (msg) => { + const jobId = msg.jobId; + const job = this.jobMap[jobId]; + const sourceType = job.isAbcJob ? 'abc' : 'external'; + const pluginStateId = msg.pluginStateId; + const fileName = msg.fileName; + const pluginTestContext = msg.pluginTestContext as PluginTestContext; + const key = `${this.hashId}:${sourceType}:${pluginStateId}:${fileName}`; + let currentPluginTestContext; + if (PluginTestContextCache.getInstance().has(key)) { + const oldContext = PluginTestContextCache.getInstance().get(key)!; + currentPluginTestContext = concatObject(oldContext, pluginTestContext); + } else { + currentPluginTestContext = pluginTestContext; + } + PluginTestContextCache.getInstance().set(key, currentPluginTestContext); + }); + } + + async invokeWorkers(plugins: Plugins[], stopAfter?: PluginState): Promise { + const processingJobs = new Set(); + return new Promise((resolve) => { + const files: string[] = []; + Object.values(this.jobMap).forEach((job) => { + for (let i = 0; i < job.fileList.length; i++) { + files.push(job.fileList[i]); + } + }); + const fileInfo: CompileFileInfo = this.compileFiles.values().next().value!; + const config = createGlobalConfig(fileInfo); + const globalContextPtr = createGlobalContextPtr(config, files); + this.subscribe(); + this.emitter.on('TASK_FINISH', (msg) => { + this.worker.isIdle = true; + const jobId = msg.jobId; + processingJobs.delete(jobId); + this.addDependantJobToQueues(jobId); + const hasRemainingTask = + this.jobQueues.externalProgramQueue.length > 0 || this.jobQueues.abcQueue.length > 0; + if (hasRemainingTask) { + this.assignTaskToIdleWorker(processingJobs, globalContextPtr, plugins, stopAfter); + } else if (this.checkAllTasksDone()) { + console.log('All tasks completed. Exiting...'); + this.emitter.emit('EXIT'); + destroyGlobalContextPtr(globalContextPtr); + destroyGlobalConfig(config); + resolve(); + } + }); + this.worker = { isIdle: true }; + this.assignTaskToIdleWorker(processingJobs, globalContextPtr, plugins, stopAfter); + }); + } + + clear(): void { + FileDependencyContextCache.getInstance().delete(this.hashId); + } +} + +export { TaskProcessor }; diff --git a/arkui-plugins/test/utils/safe-types.ts b/arkui-plugins/test/utils/safe-types.ts index 728c06501..e622c5081 100644 --- a/arkui-plugins/test/utils/safe-types.ts +++ b/arkui-plugins/test/utils/safe-types.ts @@ -17,4 +17,12 @@ function isNumber(value: any): value is number { return typeof value === `number`; } -export { isNumber }; +function isString(value: any): value is string { + return typeof value === 'string'; +} + +function isBitInt(value: any): value is bigint { + return typeof value === 'bigint'; +} + +export { isNumber, isString, isBitInt }; diff --git a/arkui-plugins/test/utils/serializable.ts b/arkui-plugins/test/utils/serializable.ts new file mode 100644 index 000000000..7747d78ab --- /dev/null +++ b/arkui-plugins/test/utils/serializable.ts @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { isBitInt } from './safe-types'; + +function serializable(object: T, ignoreKeys?: string[]): T { + const jsonStr = JSON.stringify(object, (key, value) => { + if (isBitInt(value)) { + return undefined; + } + if (ignoreKeys?.includes(key)) { + return undefined; + } + return value; + }); + return JSON.parse(jsonStr); +} + +type JSONObject = { [key: string]: any }; + +function concatObject(obj1: T | undefined, obj2: T | undefined): T { + const _obj1: JSONObject = obj1 ?? {}; + const _obj2: JSONObject = obj2 ?? {}; + const result: JSONObject = { ..._obj1 }; + + Object.keys(_obj2).forEach((key) => { + if (result.hasOwnProperty(key)) { + if ( + typeof _obj2[key] === 'object' && + typeof result[key] === 'object' && + _obj2[key] !== null && + result[key] !== null + ) { + result[key] = concatObject(result[key], _obj2[key]); + } else { + result[key] = _obj2[key]; + } + } else { + result[key] = _obj2[key]; + } + }); + + return result as T; +} + +export { serializable, concatObject }; diff --git a/arkui-plugins/test/utils/shared-types.ts b/arkui-plugins/test/utils/shared-types.ts new file mode 100644 index 000000000..83c365d53 --- /dev/null +++ b/arkui-plugins/test/utils/shared-types.ts @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Plugins, PluginState, ProjectConfig } from '../../common/plugin-context'; + +export type PluginTesterId = string | `${string}:${string}`; + +export type PluginStateId = PluginState | `${PluginState}:${PluginTesterId}`; + +export interface SingleProgramContext { + scriptSnapshot?: string; + errors?: string[]; + warnings?: string[]; +} + +export interface PluginTestContext extends SingleProgramContext { + declContexts?: Record; + sourceContexts?: Record; +} + +export interface ArkTSConfigContext { + arktsConfigFile: string; + compileFiles: Map; +} + +export interface FileDependencyContext { + depInputFile: string; + fileDepsInfoJson: string; +} + +export interface CompileFileInfo { + fileName: string; + filePath: string; + dependentFiles: string[]; + abcFilePath: string; + arktsConfigFile: string; + stdLibPath: string; +} + +export interface BuildConfig { + packageName: string; + compileFiles: string[]; + loaderOutPath: string; + cachePath: string; + pandaSdkPath: string; + apiPath: string; + kitsPath: string; + depAnalyzerPath: string; + sourceRoots: string[]; + moduleRootPath: string; + dependentModuleList: DependentModule[]; + projectConfig?: ProjectConfig; +} + +export type ModuleType = 'har' | 'feature' | 'entry' | string; // TODO: module type unclear + +export interface DependentModule { + packageName: string; + moduleName: string; + moduleType: ModuleType; + modulePath: string; + sourceRoots: string[]; + entryFile: string; +} + +export interface JobInfo { + id: string; + isCompileAbc: CompileStrategy; + compileFileInfo?: CompileFileInfo; + buildConfig?: BuildConfig; + projectConfig?: ProjectConfig; + plugins?: Plugins[]; + globalContextPtr?: number; + stopAfter?: PluginState; + filePaths?: string[]; +} + +export interface TraceOptions { + externalSourceNames: string[]; +} + +export interface ProcessTaskFinishEvent { + type: 'TASK_FINISH'; + jobId: string; +} + +export interface ProcessExitEvent { + type: 'EXIT'; +} + +export interface ProcessAssignTaskEvent { + type: 'ASSIGN_TASK'; + jobInfo: JobInfo; +} + +export interface ProcessTaskCollectEvent { + type: 'TASK_COLLECT'; + jobId: string; + pluginStateId: PluginStateId; + pluginTestContext: PluginTestContext; + fileName: string; +} + +export type ProcessEvents = + | ProcessAssignTaskEvent + | ProcessTaskFinishEvent + | ProcessTaskCollectEvent + | ProcessExitEvent; + +export type ProcessEvent = { + [E in ProcessEvents as E['type']]: Omit[]; +}; + +export interface Processor { + hashId: string; + buildConfig: BuildConfig; + projectConfig?: ProjectConfig; + tracing: TraceOptions; + cacheDir: string; + arktsConfigFile: string; + compileFiles: Map; + + invokeWorkers(plugins: Plugins[], stopAfter?: PluginState): Promise; + clear(): void; +} + +export enum CompileStrategy { + ABC, + EXTERNAL, + ABC_WTIH_EXTERNAL, +} diff --git a/arkui-plugins/tsconfig.build.json b/arkui-plugins/tsconfig.build.json index 02074ad93..a7e077cb8 100644 --- a/arkui-plugins/tsconfig.build.json +++ b/arkui-plugins/tsconfig.build.json @@ -17,6 +17,7 @@ "removeComments": false }, "include": [ + "./collectors/**/*.ts", "./common/**/*.ts", "./memo-plugins/**/*.ts", "./ui-plugins/**/*.ts", diff --git a/arkui-plugins/tsconfig.json b/arkui-plugins/tsconfig.json index 32677ce44..5ea0297ec 100644 --- a/arkui-plugins/tsconfig.json +++ b/arkui-plugins/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "target": "es2018", + "target": "esnext", "lib": ["ESNext", "ESNext.WeakRef", "DOM"], - "module": "CommonJS", + "module": "esnext", "rootDir": ".", "baseUrl": ".", "outDir": "./lib", @@ -16,9 +16,11 @@ "strict": true, "skipLibCheck": true, "removeComments": false, - "isolatedModules": true + "isolatedModules": true, + "esModuleInterop": true }, "include": [ + "./collectors/**/*.ts", "./common/**/*.ts", "./memo-plugins/**/*.ts", "./ui-plugins/**/*.ts", diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/builder-factory.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/builder-factory.ts new file mode 100644 index 000000000..4cd6d774a --- /dev/null +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/builder-factory.ts @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { ConditionScopeVisitor } from './condition-scope-visitor'; +import { coerceToAstNode } from '../../common/arkts-utils'; +import { factory as UIFactory } from '../ui-factory'; + +export class BuilderFactory { + /** + * rewrite `@Builder` function body with `ConditionScopeVisitor`. + * + * @internal + */ + static rewriteBuilderScriptFunction(node: T): arkts.ScriptFunction { + const _node = coerceToAstNode(node); + let funcBody: arkts.AstNode | undefined = _node.body; + if (!funcBody || !arkts.isBlockStatement(funcBody)) { + return _node; + } + const conditionScopeVisitor = ConditionScopeVisitor.getInstance(); + funcBody = arkts.factory.updateBlock( + funcBody, + funcBody.statements.map((st) => { + const newNode = conditionScopeVisitor.visitor(st); + conditionScopeVisitor.reset(); + return newNode; + }) + ); + return UIFactory.updateScriptFunction(_node, { body: funcBody }); + } + + /** + * rewrite `@Builder` method. + */ + static rewriteBuilderMethod(node: T): arkts.MethodDefinition { + const _node = coerceToAstNode(node); + const newFunc = BuilderFactory.rewriteBuilderScriptFunction(_node.scriptFunction); + return arkts.factory.updateMethodDefinition(_node, _node.kind, _node.name, newFunc, _node.modifiers, false); + } + + /** + * rewrite `@Builder` class property with arrow function value. + */ + static rewirteBuilderClassProperty(node: T): arkts.ClassProperty { + const _node = coerceToAstNode(node); + const value = _node.value; + if (!value || !arkts.isArrowFunctionExpression(value)) { + return _node; + } + const newValue = BuilderFactory.rewriteBuilderArrowFunction(value); + return arkts.factory.updateClassProperty( + _node, + _node.key, + newValue, + _node.typeAnnotation, + _node.modifiers, + false + ); + } + + /** + * rewrite `@Builder` property with arrow function value. + */ + static rewriteBuilderProperty(node: T): arkts.Property { + const _node = coerceToAstNode(node); + const value = _node.value; + if (!value || !arkts.isArrowFunctionExpression(value)) { + return _node; + } + const newValue = BuilderFactory.rewriteBuilderArrowFunction(value); + return arkts.factory.updateProperty(_node, _node.key, newValue); + } + + /** + * rewrite `@Builder` arrow function. + */ + static rewriteBuilderArrowFunction( + node: T + ): arkts.ArrowFunctionExpression { + const _node = coerceToAstNode(node); + const newFunc = BuilderFactory.rewriteBuilderScriptFunction(_node.scriptFunction); + return arkts.factory.updateArrowFunction(_node, newFunc); + } + + /** + * rewrite `@Builder` parameter with arrow function value. + */ + static rewriteBuilderParameter( + node: T + ): arkts.ETSParameterExpression { + const _node = coerceToAstNode(node); + const initializer = _node.initializer; + if (!initializer || !arkts.isArrowFunctionExpression(initializer)) { + return _node; + } + const newInitializer = BuilderFactory.rewriteBuilderArrowFunction(initializer); + return arkts.factory.updateParameterDeclaration(_node, _node.identifier, newInitializer); + } +} + +export type BuilderRewriteFn = (node: T) => T; + +export const builderRewriteByType = new Map>([ + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION, BuilderFactory.rewriteBuilderMethod], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY, BuilderFactory.rewirteBuilderClassProperty], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_PROPERTY, BuilderFactory.rewriteBuilderProperty], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION, BuilderFactory.rewriteBuilderArrowFunction], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION, BuilderFactory.rewriteBuilderParameter], +]); diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts index 5e4dc8331..aba68967c 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts @@ -14,12 +14,22 @@ */ import * as arkts from '@koalaui/libarkts'; import { AbstractVisitor } from '../../common/abstract-visitor'; +import { ImportCollector } from '../../common/import-collector'; import { isBuilderLambda, isBuilderLambdaMethodDecl } from './utils'; import { factory } from './factory'; +import { ProjectConfig } from '../../common/plugin-context'; export class BuilderLambdaTransformer extends AbstractVisitor { + projectConfig: ProjectConfig | undefined; + + constructor(projectConfig: ProjectConfig | undefined) { + super(); + this.projectConfig = projectConfig; + } + reset(): void { super.reset(); + ImportCollector.getInstance().reset(); } visitor(beforeChildren: arkts.AstNode): arkts.AstNode { @@ -32,6 +42,9 @@ export class BuilderLambdaTransformer extends AbstractVisitor { return this.visitEachChild(lambda); } const node = this.visitEachChild(beforeChildren); + if (arkts.isEtsScript(node) && ImportCollector.getInstance().importInfos.length > 0) { + ImportCollector.getInstance().insertCurrentImports(this.program); + } return node; } } diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/cache/conditionBreakCache.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/cache/conditionBreakCache.ts new file mode 100644 index 000000000..693053ba8 --- /dev/null +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/cache/conditionBreakCache.ts @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +export class ConditionBreakCache { + private _shouldBreak: boolean; + private _shouldReturn: boolean; + private _shouldContinue: boolean; + private static instance: ConditionBreakCache | null = null; + + private constructor() { + this._shouldBreak = false; + this._shouldReturn = false; + this._shouldContinue = false; + } + + static getInstance(): ConditionBreakCache { + if (!this.instance) { + this.instance = new ConditionBreakCache(); + } + return this.instance; + } + + get shouldBreak(): boolean { + return this._shouldBreak; + } + + get shouldReturn(): boolean { + return this._shouldReturn; + } + + get shouldContinue(): boolean { + return this._shouldContinue; + } + + collectBreak(): void { + if (this._shouldBreak) { + return; + } + this._shouldBreak = true; + } + + collectReturn(): void { + if (this._shouldReturn) { + return; + } + this._shouldReturn = true; + } + + collectContinue(): void { + if (this._shouldContinue) { + return; + } + this._shouldContinue = true; + } + + collect(st: arkts.AstNode): boolean { + if (arkts.isBreakStatement(st)) { + this.collectBreak(); + return true; + } + if (arkts.isReturnStatement(st)) { + this.collectReturn(); + return true; + } + if (arkts.isContinueStatement(st)) { + this.collectContinue(); + return true; + } + return false; + } + + reset(): void { + this._shouldBreak = false; + this._shouldReturn = false; + this._shouldContinue = false; + } +} diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/condition-scope-visitor.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/condition-scope-visitor.ts new file mode 100644 index 000000000..86ec83025 --- /dev/null +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/condition-scope-visitor.ts @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { AbstractVisitor } from '../../common/abstract-visitor'; +import { factory as BuilderLambdaFactory } from './factory'; + +/** + * `ConditionScopeVisitor` is used to visit `@Builder` function body to wrap `ConditionScope`/`ConditionBranch` + * to if-else or switch-case statements. + * + * @internal + */ +export class ConditionScopeVisitor extends AbstractVisitor { + private static instance: ConditionScopeVisitor; + private _enforceUpdateCondition: boolean = false; + private _shouldUpdateCondition: boolean = true; + + private constructor() { + super(); + } + + private get shouldUpdateCondition(): boolean { + if (this._enforceUpdateCondition) { + return true; + } + return this._shouldUpdateCondition; + } + + private set shouldUpdateCondition(newValue: boolean) { + if (this._enforceUpdateCondition) { + this._shouldUpdateCondition = true; + return; + } + this._shouldUpdateCondition = newValue; + } + + static getInstance(): ConditionScopeVisitor { + if (!this.instance) { + this.instance = new ConditionScopeVisitor(); + } + return this.instance; + } + + private enter(node: arkts.AstNode): void { + if (arkts.isVariableDeclarator(node) && arkts.NodeCache.getInstance().has(node)) { + this._enforceUpdateCondition = true; + } + } + + reset(): void { + this._enforceUpdateCondition = false; + this._shouldUpdateCondition = true; + } + + visitor(node: arkts.AstNode): arkts.AstNode { + this.enter(node); + if (arkts.isIfStatement(node) && this.shouldUpdateCondition) { + return BuilderLambdaFactory.updateIfElseContentBodyInBuilderLambda(node, true, true); + } + if (arkts.isSwitchStatement(node) && this.shouldUpdateCondition) { + return BuilderLambdaFactory.updateSwitchCaseContentBodyInBuilderLambda(node, true, true); + } + if (arkts.isBlockStatement(node) && this.shouldUpdateCondition) { + const newStatements = node.statements.map((st) => + BuilderLambdaFactory.updateContentBodyInBuilderLambda(st, true, true) + ); + return arkts.factory.updateBlock(node, newStatements); + } + if ( + arkts.isArrowFunctionExpression(node) && + !arkts.NodeCache.getInstance().has(node) && + !arkts.NodeCache.getInstance().has(node.scriptFunction) + ) { + this.shouldUpdateCondition = false; + this._enforceUpdateCondition = false; + } + return this.visitEachChild(node); + } +} diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts index 9c303f765..dd3a647aa 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts @@ -15,49 +15,78 @@ import * as arkts from '@koalaui/libarkts'; import { BuilderLambdaNames } from '../utils'; -import { annotation, backingField, filterDefined, removeAnnotationByName } from '../../common/arkts-utils'; +import { + backingField, + filterDefined, + isDecoratorAnnotation, + removeAnnotationByName, + forEachArgWithParam, + annotation, +} from '../../common/arkts-utils'; import { BuilderLambdaDeclInfo, builderLambdaFunctionName, builderLambdaMethodDeclType, - builderLambdaTypeName, callIsGoodForBuilderLambda, + collectComponentAttributeImport, findBuilderLambdaDecl, findBuilderLambdaDeclInfo, isBuilderLambda, isBuilderLambdaFunctionCall, isSafeType, replaceBuilderLambdaDeclMethodName, - BindableDecl, - getDecalTypeFromValue, - hasBindableProperty, isDoubleDollarCall, InstanceCallInfo, isStyleChainedCall, isStyleWithReceiverCall, + builderLambdaType, + BuilderLambdaSecondLastArgInfo, + buildSecondLastArgInfo, + checkIsWithInIfConditionScope, + BuilderLambdaConditionBranchInfo, + BuilderLambdaChainingCallArgInfo, + getArgumentType, } from './utils'; -import { DecoratorNames } from '../property-translators/utils'; +import { hasDecorator, isDecoratorIntrinsicAnnotation } from '../property-translators/utils'; import { factory as PropertyFactory } from '../property-translators/factory'; -import { ProjectConfig } from '../../common/plugin-context'; +import { factory as UIFactory } from '../ui-factory'; +import { + AnimationNames, + ARKUI_BUILDER_SOURCE_NAME, + BindableDecl, + ConditionNames, + DecoratorIntrinsicNames, + DecoratorNames, +} from '../../common/predefines'; +import { ImportCollector } from '../../common/import-collector'; +import { addMemoAnnotation, collectMemoableInfoInParameter } from '../../collectors/memo-collectors/utils'; +import { factory as MemoCollectFactory } from '../../collectors/memo-collectors/factory'; +import { BuilderFactory } from './builder-factory'; +import { ConditionBreakCache } from './cache/conditionBreakCache'; export class factory { - /* - * update @ComponentBuilder decorated method. + /** + * update `@ComponentBuilder` decorated method. */ static updateBuilderLambdaMethodDecl( node: arkts.MethodDefinition, - styleArg: arkts.ETSParameterExpression, + prefixArgs: arkts.ETSParameterExpression[], newAnno: arkts.AnnotationUsage[], - newName: string | undefined + newName: string | undefined, + externalSourceName?: string ): arkts.MethodDefinition { const func: arkts.ScriptFunction = node.scriptFunction; + let newParams: arkts.Expression[] = []; + if (func.params.length > 0) { + newParams.push(...prefixArgs, ...func.params); + } const updateFunc = arkts.factory .updateScriptFunction( func, func.body, arkts.FunctionSignature.createFunctionSignature( func.typeParams, - [styleArg, ...func.params], + newParams, arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), false ), @@ -70,40 +99,50 @@ export class factory { node, node.kind, arkts.factory.updateIdentifier(node.name, newName ?? node.name.name), - updateFunc, + node.name.name === BuilderLambdaNames.ORIGIN_METHOD_NAME ? addMemoAnnotation(updateFunc) : updateFunc, node.modifiers, - false // TODO: how do I get it? + false ); } /* * transform arguments in style node. */ - static getTransformedStyle(call: arkts.CallExpression): arkts.Expression[] { + static getTransformedStyle(call: arkts.CallExpression): BuilderLambdaChainingCallArgInfo[] { const decl = arkts.getDecl(call.expression); if (!decl || !arkts.isMethodDefinition(decl)) { - return [...call.arguments]; - } - const type: arkts.AstNode | undefined = arkts.isEtsParameterExpression(decl.scriptFunction.params[0]) - ? decl.scriptFunction.params[0].type - : undefined; - if ( - type && - arkts.isTypeNode(type) && - hasBindableProperty(type, BindableDecl.BINDABLE) && - isDoubleDollarCall(call.arguments[0]) - ) { - const bindableArg: arkts.Expression = (call.arguments[0] as arkts.CallExpression).arguments[0]; - return [factory.updateBindableStyleArguments(bindableArg), ...call.arguments.slice(1)]; + return call.arguments.map((arg) => ({ arg })); } - return [...call.arguments]; + const argInfo: BuilderLambdaChainingCallArgInfo[] = []; + const args = call.arguments; + const params = decl.scriptFunction.params; + const isTrailingCall = call.isTrailingCall; + forEachArgWithParam( + args, + params, + (arg, param, index) => { + const _param = param as arkts.ETSParameterExpression; + let isDoubleDollar: boolean = false; + if (index === 0 && !!arg) { + isDoubleDollar = isDoubleDollarCall(arg); + } + if (isDoubleDollar && !!arg) { + const bindableArg: arkts.Expression = (arg as arkts.CallExpression).arguments[0]; + argInfo.push({ arg: factory.updateBindableStyleArguments(bindableArg) }); + } else if (!!arg) { + argInfo.push({ arg, hasBuilder: hasDecorator(_param, DecoratorNames.BUILDER) }); + } + }, + { isTrailingCall } + ); + return argInfo; } /* * transform bundable arguments in style node, e.g. `Radio().checked($$(this.checked))` => `Radio().checked({value: xxx, onChange: xxx})`. */ static updateBindableStyleArguments(bindableArg: arkts.Expression): arkts.Expression { - const valueType: arkts.TypeNode = getDecalTypeFromValue(bindableArg); + const valueType: arkts.TypeNode = getArgumentType(bindableArg); const objExp: arkts.ObjectExpression = arkts.factory.createObjectExpression( arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, [factory.generateValueProperty(bindableArg), factory.generateOnChangeArrowFunc(bindableArg, valueType)], @@ -115,9 +154,9 @@ export class factory { /* * create style instance call, e.g. `instance.margin(10)`. */ - static createStyleLambdaBody(lambdaBody: arkts.AstNode, callInfo: InstanceCallInfo, projectConfig: ProjectConfig | undefined): arkts.CallExpression { + static createStyleLambdaBody(lambdaBody: arkts.AstNode, callInfo: InstanceCallInfo): arkts.CallExpression { if (!callInfo.isReceiver) { - const newArgs: arkts.Expression[] = factory.getTransformedStyle(callInfo.call); + const argInfos: BuilderLambdaChainingCallArgInfo[] = factory.getTransformedStyle(callInfo.call); return arkts.factory.createCallExpression( arkts.factory.createMemberExpression( lambdaBody, @@ -127,11 +166,11 @@ export class factory { false ), undefined, - newArgs.map((arg) => { - if (arkts.isArrowFunctionExpression(arg)) { - return this.processArgArrowFunction(arg, projectConfig); + argInfos.map((info) => { + if (arkts.isArrowFunctionExpression(info.arg)) { + return this.processArgArrowFunction(info.arg, info.hasBuilder); } - return arg; + return info.arg; }) ); } else { @@ -161,20 +200,25 @@ export class factory { */ static createStyleArgInBuilderLambda( lambdaBody: arkts.Expression | undefined, - typeNode: arkts.TypeNode | undefined + typeNode: arkts.TypeNode | undefined, + moduleName: string ): arkts.UndefinedLiteral | arkts.ArrowFunctionExpression { if (!lambdaBody) { return arkts.factory.createUndefinedLiteral(); } + collectComponentAttributeImport(typeNode, moduleName); const safeType: arkts.TypeNode | undefined = isSafeType(typeNode) ? typeNode : undefined; + const styleLambdaParam: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_ARROW_PARAM_NAME, safeType), undefined ); + const returnStatement = arkts.factory.createReturnStatement(); + arkts.NodeCache.getInstance().collect(returnStatement); const body: arkts.BlockStatement = arkts.factory.createBlock([ arkts.factory.createExpressionStatement(lambdaBody), - arkts.factory.createReturnStatement(), + returnStatement, ]); const func = arkts.factory.createScriptFunction( @@ -189,7 +233,7 @@ export class factory { arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC ); - return arkts.factory.createArrowFunction(func); + return addMemoAnnotation(arkts.factory.createArrowFunction(func)); } /* @@ -212,23 +256,15 @@ export class factory { ), arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW ); + addMemoAnnotation(funcType); let parameter: arkts.ETSParameterExpression; - if (isFunctionCall) { - parameter = arkts.factory - .createParameterDeclaration( - arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_PARAM_NAME, funcType), - undefined - ) - .setOptional(true); - } else { - const optionalFuncType = arkts.factory.createUnionType([funcType, arkts.factory.createETSUndefinedType()]); - parameter = arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_PARAM_NAME, optionalFuncType), - undefined - ); - } - parameter.annotations = [annotation('memo')]; + const optionalFuncType = arkts.factory.createUnionType([funcType, arkts.factory.createETSUndefinedType()]); + parameter = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_PARAM_NAME, optionalFuncType), + undefined + ); + arkts.NodeCache.getInstance().collect(parameter); return parameter; } @@ -236,15 +272,18 @@ export class factory { * If a builder lambda's argument is an arrow function, * then transform any builder lambda in the function body. */ - static processArgArrowFunction(arg: arkts.ArrowFunctionExpression, projectConfig: ProjectConfig | undefined): arkts.ArrowFunctionExpression { + static processArgArrowFunction( + arg: arkts.ArrowFunctionExpression, + hasBuilder?: boolean + ): arkts.ArrowFunctionExpression { const func: arkts.ScriptFunction = arg.scriptFunction; const updateFunc = arkts.factory.updateScriptFunction( func, !!func.body && arkts.isBlockStatement(func.body) ? arkts.factory.updateBlock( - func.body, - func.body.statements.map((st) => this.updateContentBodyInBuilderLambda(st, projectConfig)) - ) + func.body, + func.body.statements.map((st) => this.updateContentBodyInBuilderLambda(st, hasBuilder)) + ) : undefined, arkts.FunctionSignature.createFunctionSignature( func.typeParams, @@ -268,15 +307,16 @@ export class factory { } else if (arkts.isObjectExpression(arg)) { expr = arg; } - if (!expr) { return arg; } - - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(typeName!); - const properties = expr.properties as arkts.Property[]; - properties.forEach((prop, index) => { - this.updateParameterPassingInLinkedProperties(prop, index, currentStructInfo, properties); + const properties = (expr.properties as arkts.Property[]).map((p) => { + let property = p; + MemoCollectFactory.findAndCollectMemoableProperty(p, (currNode: arkts.Property) => { + property = BuilderFactory.rewriteBuilderProperty(currNode); + return property; + }); + return factory.updatePropertiesInOptions(property); }); const updatedExpr: arkts.ObjectExpression = arkts.ObjectExpression.updateObjectExpression( expr, @@ -290,49 +330,37 @@ export class factory { return updatedExpr as T; } - /** - * update any `@Link` parameter passing to the custom-component child. - */ - static updateParameterPassingInLinkedProperties( - prop: arkts.Property, - index: number, - currentStructInfo: arkts.StructInfo, - properties: arkts.Property[] - ): void { - const decl = prop.key ? arkts.getDecl(prop.key) : undefined; - if (decl && arkts.isMethodDefinition(decl)) { - const type: arkts.TypeNode | undefined = decl.scriptFunction.returnTypeAnnotation; - if ( - type && - hasBindableProperty(type, BindableDecl.BINDABLE) && - arkts.isProperty(prop) && - prop.value && - isDoubleDollarCall(prop.value) - ) { - properties[index] = factory.updateBindableProperty(prop); - } + static updatePropertiesInOptions(prop: arkts.Property): arkts.Property { + let decl: arkts.AstNode | undefined; + if (!prop.key || !prop.value || !(decl = arkts.getDecl(prop.key)) || !arkts.isMethodDefinition(decl)) { + return prop; } - if ( - !!prop.key && - !!prop.value && + let isBuilderParam: boolean = false; + let isLinkIntrinsic: boolean = false; + decl.scriptFunction.annotations.forEach((anno) => { + isBuilderParam ||= isDecoratorAnnotation(anno, DecoratorNames.BUILDER_PARAM); + isLinkIntrinsic ||= isDecoratorIntrinsicAnnotation(anno, DecoratorIntrinsicNames.LINK); + }); + + if (isDoubleDollarCall(prop.value)) { + return factory.updateBindableProperty(prop); + } else if (isBuilderParam && arkts.isArrowFunctionExpression(prop.value)) { + addMemoAnnotation(prop.value); + return prop; + } else if ( + isLinkIntrinsic && arkts.isIdentifier(prop.key) && arkts.isMemberExpression(prop.value) && arkts.isThisExpression(prop.value.object) && arkts.isIdentifier(prop.value.property) ) { - const structVariableMetadata = currentStructInfo.metadata[prop.key.name]; - if ( - structVariableMetadata && - structVariableMetadata.properties.length && - structVariableMetadata.properties.includes(DecoratorNames.LINK) - ) { - properties[index] = arkts.Property.updateProperty( - prop, - arkts.factory.createIdentifier(backingField(prop.key.name)), - this.updateBackingMember(prop.value, prop.value.property.name) - ); - } + return arkts.Property.updateProperty( + prop, + arkts.factory.createIdentifier(backingField(prop.key.name)), + factory.updateBackingMember(prop.value, prop.value.property.name) + ); } + return prop; } /** @@ -340,15 +368,20 @@ export class factory { * If the corresponding argument is not provided, fill-in an `undefined` to it. */ static createOrUpdateArgInBuilderLambda( + fallback: arkts.AstNode | undefined, arg: arkts.Expression | undefined, - projectConfig: ProjectConfig | undefined, typeName?: string, - ): arkts.AstNode { + canAddMemo?: boolean + ): arkts.AstNode | undefined { if (!arg) { - return arkts.factory.createUndefinedLiteral(); + return fallback; } if (arkts.isArrowFunctionExpression(arg)) { - return this.processArgArrowFunction(arg, projectConfig); + const newNode = this.processArgArrowFunction(arg, canAddMemo); + if (canAddMemo) { + addMemoAnnotation(newNode); + } + return newNode; } // this is too optimistic to check if this is an options argument... if (arkts.isTSAsExpression(arg) || arkts.isObjectExpression(arg)) { @@ -357,76 +390,221 @@ export class factory { return arg; } + static createSecondLastArgInBuilderLambda(argInfo: BuilderLambdaSecondLastArgInfo): arkts.AstNode | undefined { + if (!!argInfo.isReusable && !!argInfo.reuseId) { + const reuseIdNode = arkts.factory.createStringLiteral(argInfo.reuseId); + return this.createOrUpdateArgInBuilderLambda(reuseIdNode, undefined, undefined); + } else if (!argInfo.isFunctionCall) { + return this.createOrUpdateArgInBuilderLambda(arkts.factory.createUndefinedLiteral(), undefined, undefined); + } + return undefined; + } + /** * transform arguments in a builder lambda call. */ static generateArgsInBuilderLambda( leaf: arkts.CallExpression, lambdaBody: arkts.Identifier | arkts.CallExpression, - declInfo: BuilderLambdaDeclInfo, - projectConfig: ProjectConfig | undefined + declInfo: BuilderLambdaDeclInfo ): (arkts.AstNode | undefined)[] { - const { params, returnType } = declInfo; - const typeName: string | undefined = builderLambdaTypeName(leaf); - const args: (arkts.AstNode | undefined)[] = [this.createStyleArgInBuilderLambda(lambdaBody, returnType)]; - let index = 0; - while (index < params.length) { - args.push(this.createOrUpdateArgInBuilderLambda(leaf.arguments.at(index), projectConfig, typeName)); - index++; - } - const isReusable: boolean = typeName - ? arkts.GlobalInfo.getInfoInstance().getStructInfo(typeName).isReusable : false; - if (isReusable) { - args.splice(-1, 1, arkts.factory.createStringLiteral(typeName!)); - } - else if (typeName === 'XComponent') { - let packageInfo: string = ''; - if (projectConfig?.bundleName && projectConfig?.moduleName) { - packageInfo = projectConfig?.bundleName + '/' + projectConfig?.moduleName; - } - args.splice(args.length - 1, 0, arkts.factory.createStringLiteral(packageInfo)); - } - return args; + const { isFunctionCall, params, returnType, moduleName } = declInfo; + const type: arkts.Identifier | undefined = builderLambdaType(leaf); + const args: (arkts.AstNode | undefined)[] = [ + this.createStyleArgInBuilderLambda(lambdaBody, returnType, moduleName), + ]; + const secondLastArgInfo = buildSecondLastArgInfo(type, isFunctionCall); + const isTrailingCall = leaf.isTrailingCall; + forEachArgWithParam( + leaf.arguments, + params, + (arg, param, index) => { + let modifiedArg: arkts.AstNode | undefined; + if (index === params.length - 2 && !arg) { + modifiedArg = this.createSecondLastArgInBuilderLambda(secondLastArgInfo); + } + if (!modifiedArg) { + const memoableInfo = collectMemoableInfoInParameter(param); + const canAddMemo = + (!!memoableInfo.hasBuilder || !!memoableInfo.hasMemo) && !!memoableInfo.hasProperType; + modifiedArg = this.createOrUpdateArgInBuilderLambda( + arkts.factory.createUndefinedLiteral(), + arg, + type?.name, + canAddMemo + ); + } + args.push(modifiedArg); + }, + { isTrailingCall } + ); + return filterDefined(args); } /** - * update if-else in trailing lambda contents in a builder lambda call. + * update if-else in a builder lambda call's arguments. */ - static updateIfElseContentBodyInBuilderLambda(statement: arkts.AstNode, projectConfig: ProjectConfig | undefined): arkts.AstNode { + static updateIfElseContentBodyInBuilderLambda( + statement: arkts.AstNode, + shouldWrap?: boolean, + stopAtBuilderLambda?: boolean + ): arkts.AstNode { if (arkts.isIfStatement(statement)) { const alternate = !!statement.alternate - ? this.updateIfElseContentBodyInBuilderLambda(statement.alternate, projectConfig) + ? this.updateIfElseContentBodyInBuilderLambda(statement.alternate, shouldWrap, stopAtBuilderLambda) : statement.alternate; - const consequence = this.updateIfElseContentBodyInBuilderLambda(statement.consequent, projectConfig); - return arkts.factory.updateIfStatement(statement, statement.test, consequence!, alternate); + const consequence = this.updateIfElseContentBodyInBuilderLambda( + statement.consequent, + shouldWrap, + stopAtBuilderLambda + ); + const newStatement = arkts.factory.updateIfStatement(statement, statement.test, consequence!, alternate); + return !shouldWrap || checkIsWithInIfConditionScope(statement) + ? newStatement + : this.wrapConditionToBlock([newStatement], ConditionNames.CONDITION_SCOPE); } if (arkts.isBlockStatement(statement)) { - return arkts.factory.updateBlock( - statement, - statement.statements.map((st) => this.updateContentBodyInBuilderLambda(st, projectConfig)) + let { statements, breakIndex } = this.updateConditionBranchInScope( + statement.statements, + shouldWrap, + stopAtBuilderLambda + ); + if (!!shouldWrap && checkIsWithInIfConditionScope(statement)) { + const beforeBreak = this.wrapConditionToBlock( + breakIndex > 0 ? statements.slice(0, breakIndex) : statements, + ConditionNames.CONDITION_BRANCH + ); + const afterBreak = breakIndex > 0 ? statements.slice(breakIndex) : []; + statements = [beforeBreak, ...afterBreak]; + } + return arkts.factory.updateBlock(statement, statements); + } + return statement; + } + + /** + * update switch-case in a builder lambda call's arguments. + */ + static updateSwitchCaseContentBodyInBuilderLambda( + statement: T, + shouldWrap?: boolean, + stopAtBuilderLambda?: boolean + ): T { + if (arkts.isSwitchStatement(statement)) { + const cases = statement.cases.map((c) => + this.updateSwitchCaseContentBodyInBuilderLambda(c, shouldWrap, stopAtBuilderLambda) ); + const newStatement = arkts.factory.updateSwitchStatement(statement, statement.discriminant, cases); + return (!shouldWrap + ? newStatement + : this.wrapConditionToBlock([newStatement], ConditionNames.CONDITION_SCOPE)) as arkts.AstNode as T; + } + if (arkts.isSwitchCaseStatement(statement)) { + let { statements, breakIndex } = this.updateConditionBranchInScope(statement.consequent, shouldWrap); + if (shouldWrap && breakIndex > 0) { + const hasBreak = breakIndex !== statements.length; + const beforeBreak = this.wrapConditionToBlock( + statements.slice(0, breakIndex), + ConditionNames.CONDITION_BRANCH + ); + const afterBreak = statements.slice(hasBreak ? breakIndex + 1 : breakIndex); + const breakStatement = this.createBreakBetweenConditionStatements(); + statements = [beforeBreak, ...breakStatement, ...afterBreak]; + } + ConditionBreakCache.getInstance().reset(); + return arkts.factory.updateSwitchCaseStatement(statement, statement.test, statements) as T; } return statement; } + static createBreakBetweenConditionStatements(): arkts.AstNode[] { + const cache = ConditionBreakCache.getInstance(); + if (cache.shouldBreak) { + return [arkts.factory.createBreakStatement()]; + } + if (cache.shouldReturn) { + return [arkts.factory.createReturnStatement()]; + } + return []; + } + + /** + * update ConditionBranch in an if-else or swith-case body. + * @internal + */ + static updateConditionBranchInScope( + statements: readonly arkts.Statement[], + shouldWrap?: boolean, + stopAtBuilderLambda?: boolean + ): BuilderLambdaConditionBranchInfo { + let breakIndex = statements.length; + const newStatements = statements.map((st, index) => { + if (ConditionBreakCache.getInstance().collect(st)) { + breakIndex = index; + } + return this.updateContentBodyInBuilderLambda(st, shouldWrap, stopAtBuilderLambda); + }); + return { statements: newStatements, breakIndex }; + } + + /** + * wrap `ConditionScope` or `ConditionBranch` builder function to the block statements. + */ + static wrapConditionToBlock(statements: readonly arkts.AstNode[], condition: ConditionNames): arkts.AstNode { + const contentArg = arkts.factory.createArrowFunction( + UIFactory.createScriptFunction({ + body: arkts.factory.createBlock(statements), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + }) + ); + addMemoAnnotation(contentArg); + const newCall = arkts.factory.createCallExpression(arkts.factory.createIdentifier(condition), undefined, [ + contentArg, + ]); + arkts.NodeCache.getInstance().collect(newCall); + ImportCollector.getInstance().collectSource(condition, ARKUI_BUILDER_SOURCE_NAME); + ImportCollector.getInstance().collectImport(condition); + return arkts.factory.createExpressionStatement(newCall); + } + /** * update trailing lambda contents in a builder lambda call. */ - static updateContentBodyInBuilderLambda(statement: arkts.Statement, projectConfig: ProjectConfig | undefined): arkts.Statement { + static updateContentBodyInBuilderLambda( + statement: arkts.Statement, + hasBuilder?: boolean, + stopAtBuilderLambda?: boolean + ): arkts.Statement { if ( arkts.isExpressionStatement(statement) && arkts.isCallExpression(statement.expression) && isBuilderLambda(statement.expression) ) { + if (!!stopAtBuilderLambda) { + return statement; + } return arkts.factory.updateExpressionStatement( statement, - this.transformBuilderLambda(statement.expression, projectConfig) + this.transformBuilderLambda(statement.expression) ); } if (arkts.isIfStatement(statement)) { - return this.updateIfElseContentBodyInBuilderLambda(statement, projectConfig); + return this.updateIfElseContentBodyInBuilderLambda(statement, hasBuilder, stopAtBuilderLambda); + } + if (arkts.isSwitchStatement(statement)) { + return this.updateSwitchCaseContentBodyInBuilderLambda(statement, hasBuilder, stopAtBuilderLambda); + } + if (arkts.isBlockStatement(statement)) { + const newStatements = statement.statements.map((st) => + this.updateContentBodyInBuilderLambda(st, hasBuilder, stopAtBuilderLambda) + ); + return arkts.factory.updateBlock(statement, newStatements); + } + if (arkts.isBreakStatement(statement) && statement.parent && arkts.isBlockStatement(statement.parent)) { + ConditionBreakCache.getInstance().collectBreak(); + return arkts.factory.createReturnStatement(); } - return statement; } @@ -460,21 +638,26 @@ export class factory { /** * transform `@ComponentBuilder` in declared methods. */ - static transformBuilderLambdaMethodDecl(node: arkts.MethodDefinition): arkts.MethodDefinition { + static transformBuilderLambdaMethodDecl( + node: arkts.MethodDefinition, + externalSourceName?: string + ): arkts.MethodDefinition { const func: arkts.ScriptFunction = node.scriptFunction; const isFunctionCall: boolean = isBuilderLambdaFunctionCall(node); const typeNode: arkts.TypeNode | undefined = builderLambdaMethodDeclType(node); - const styleArg: arkts.ETSParameterExpression = this.createStyleArgInBuilderLambdaDecl(typeNode, isFunctionCall); const newOverloads: arkts.MethodDefinition[] = node.overloads.map((method) => factory.transformBuilderLambdaMethodDecl(method) ); - return this.updateBuilderLambdaMethodDecl( + const newNode = this.updateBuilderLambdaMethodDecl( node, - styleArg, + [this.createStyleArgInBuilderLambdaDecl(typeNode, isFunctionCall)], removeAnnotationByName(func.annotations, BuilderLambdaNames.ANNOTATION_NAME), - replaceBuilderLambdaDeclMethodName(node.name.name) + replaceBuilderLambdaDeclMethodName(node.name.name), + externalSourceName ).setOverloads(newOverloads); + arkts.NodeCache.getInstance().collect(newNode); + return newNode; } /** @@ -490,14 +673,14 @@ export class factory { continue; } const property: arkts.Identifier = instanceCalls[curIdx].call.expression as arkts.Identifier; - if (property.name === BuilderLambdaNames.ANIMATION_NAME) { + if (property.name === AnimationNames.ANIMATION) { const aniStart: arkts.CallExpression = arkts.factory.createCallExpression( - arkts.factory.createIdentifier(BuilderLambdaNames.ANIMATION_START), + arkts.factory.createIdentifier(AnimationNames.ANIMATION_START), undefined, instanceCalls[curIdx].call.arguments ); const aniStop: arkts.CallExpression = arkts.factory.createCallExpression( - arkts.factory.createIdentifier(BuilderLambdaNames.ANIMATION_STOP), + arkts.factory.createIdentifier(AnimationNames.ANIMATION_STOP), undefined, instanceCalls[curIdx].call.arguments.map((arg) => arg.clone()) ); @@ -514,7 +697,7 @@ export class factory { /** * transform `@ComponentBuilder` in non-declared calls. */ - static transformBuilderLambda(node: arkts.CallExpression, projectConfig: ProjectConfig | undefined): arkts.AstNode { + static transformBuilderLambda(node: arkts.CallExpression): arkts.AstNode { let instanceCalls: InstanceCallInfo[] = []; let leaf: arkts.CallExpression = node; @@ -556,13 +739,16 @@ export class factory { instanceCalls = instanceCalls.reverse(); this.updateAnimation(instanceCalls); lambdaBody = arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_ARROW_PARAM_NAME); + arkts.NodeCache.getInstance().collect(lambdaBody); instanceCalls.forEach((callInfo) => { - lambdaBody = this.createStyleLambdaBody(lambdaBody!, callInfo, projectConfig); + lambdaBody = this.createStyleLambdaBody(lambdaBody!, callInfo); }); } - const args: (arkts.AstNode | undefined)[] = this.generateArgsInBuilderLambda(leaf, lambdaBody!, declInfo, projectConfig); - return arkts.factory.updateCallExpression(node, replace, leaf.typeArguments, filterDefined(args)); + const args: (arkts.AstNode | undefined)[] = this.generateArgsInBuilderLambda(leaf, lambdaBody!, declInfo); + const newNode = arkts.factory.updateCallExpression(node, replace, leaf.typeArguments, filterDefined(args)); + arkts.NodeCache.getInstance().collect(newNode); + return newNode; } /* @@ -578,7 +764,7 @@ export class factory { prop.value.arguments.length === 1 ) { let bindableArg = prop.value.arguments[0]; - valueType = getDecalTypeFromValue(bindableArg); + valueType = getArgumentType(bindableArg); res.push( factory.generateValueProperty(bindableArg), factory.generateOnChangeArrowFunc(bindableArg, valueType) @@ -638,11 +824,76 @@ export class factory { * generate `Bindable`. */ static createBindableType(valueType: arkts.TypeNode): arkts.ETSTypeReference { + const transformedKey = BindableDecl.BINDABLE; + ImportCollector.getInstance().collectImport(transformedKey); return arkts.factory.createTypeReference( arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier(BindableDecl.BINDABLE), + arkts.factory.createIdentifier(transformedKey), arkts.factory.createTSTypeParameterInstantiation([valueType.clone()]) ) ); } + + /** + * create a `@Builder` function with `@Builder` trailing lambda parameter. + */ + static createBuilderWithTrailingLambdaDecl( + name: string, + returnTypeAnnotation: arkts.TypeNode + ): arkts.MethodDefinition { + const key = arkts.factory.createIdentifier(name); + const modifiers = + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC | + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE | + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT; + const param = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + BuilderLambdaNames.CONTENT_PARAM_NAME, + arkts.factory.createFunctionType( + arkts.factory.createFunctionSignature( + undefined, + [], + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), + false + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW + ) + ), + undefined + ); + param.annotations = [annotation(DecoratorNames.BUILDER)]; + arkts.NodeCache.getInstance().collect(param); + const method = UIFactory.createMethodDefinition({ + key, + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_NONE, + function: { + key, + params: [param], + returnTypeAnnotation, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_NONE, + modifiers, + annotations: [annotation(DecoratorNames.BUILDER)], + }, + modifiers, + }); + arkts.NodeCache.getInstance().collect(method); + return method; + } + + /** + * add following declared methods at header file: + * - `@Builder function ConditionScope(@Builder content: () => void): void;` + * - `@Builder function ConditionBranch(@Builder content: () => void): void;` + */ + static addConditionBuilderDecls(): arkts.MethodDefinition[] { + const conditionScope = this.createBuilderWithTrailingLambdaDecl( + ConditionNames.CONDITION_SCOPE, + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID) + ); + const conditionBranch = this.createBuilderWithTrailingLambdaDecl( + ConditionNames.CONDITION_BRANCH, + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID) + ); + return [conditionScope, conditionBranch]; + } } diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts index f7cd7c6de..1d5d3737c 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts @@ -14,19 +14,19 @@ */ import * as arkts from '@koalaui/libarkts'; -import { isAnnotation } from '../../common/arkts-utils'; -import { BuilderLambdaNames, Dollars } from '../utils'; +import { isAnnotation, matchPrefix } from '../../common/arkts-utils'; +import { BuilderLambdaNames, isCustomComponentAnnotation } from '../utils'; +import { DeclarationCollector } from '../../common/declaration-collector'; +import { ARKUI_IMPORT_PREFIX_NAMES, BindableDecl, Dollars, StructDecoratorNames } from '../../common/predefines'; +import { ImportCollector } from '../../common/import-collector'; export type BuilderLambdaDeclInfo = { isFunctionCall: boolean; // isFunctionCall means it is from $_instantiate. params: readonly arkts.Expression[]; returnType: arkts.TypeNode | undefined; + moduleName: string; }; -export enum BindableDecl { - BINDABLE = 'Bindable', -} - export type BuilderLambdaAstNode = arkts.ScriptFunction | arkts.ETSParameterExpression | arkts.FunctionDeclaration; export type InstanceCallInfo = { @@ -34,6 +34,46 @@ export type InstanceCallInfo = { call: arkts.CallExpression; }; +export type BuilderLambdaArgInfo = { + isFunctionCall: boolean; +}; + +export type BuilderLambdaReusableArgInfo = { + isReusable?: boolean; + reuseId?: string; +}; + +export type BuilderLambdaSecondLastArgInfo = BuilderLambdaArgInfo & BuilderLambdaReusableArgInfo; + +export type BuilderLambdaConditionBranchInfo = { + statements: readonly arkts.Statement[]; + breakIndex: number; +}; + +export type BuilderLambdaChainingCallArgInfo = { + arg: arkts.Expression; + hasBuilder?: boolean; +}; + +export function buildSecondLastArgInfo( + type: arkts.Identifier | undefined, + isFunctionCall: boolean +): BuilderLambdaSecondLastArgInfo { + let isReusable: boolean | undefined; + let reuseId: string | undefined; + if (!isFunctionCall && !!type) { + const customComponentDecl = arkts.getDecl(type); + isReusable = + !!customComponentDecl && + arkts.isClassDefinition(customComponentDecl) && + customComponentDecl.annotations.some((anno) => + isCustomComponentAnnotation(anno, StructDecoratorNames.RESUABLE) + ); + reuseId = isReusable ? type.name : undefined; + } + return { isFunctionCall, isReusable, reuseId }; +} + /** * Used in finding "XXX" in BuilderLambda("XXX") * @deprecated @@ -54,13 +94,8 @@ export function builderLambdaArgumentName(annotation: arkts.AnnotationUsage): st return property.value.str; } -/** - * Determine whether it is a custom component. - * - * @param node class declaration node - */ -export function isBuilderLambda(node: arkts.AstNode, isExternal?: boolean): boolean { - const builderLambdaCall: arkts.AstNode | undefined = getDeclForBuilderLambda(node); +export function isBuilderLambda(node: arkts.AstNode, nodeDecl?: arkts.AstNode | undefined): boolean { + const builderLambdaCall: arkts.AstNode | undefined = getDeclForBuilderLambda(node, nodeDecl); if (!builderLambdaCall) { return arkts.isCallExpression(node) && node.arguments.length > 0 && isBuilderLambda(node.arguments[0]); } @@ -131,15 +166,12 @@ export function replaceBuilderLambdaDeclMethodName(name: string | undefined): st return undefined; } -export function isBuilderLambdaMethodDecl(node: arkts.AstNode, isExternal?: boolean): boolean { +export function isBuilderLambdaMethodDecl(node: arkts.AstNode): boolean { const builderLambdaMethodDecl: arkts.AstNode | undefined = getDeclForBuilderLambdaMethodDecl(node); return !!builderLambdaMethodDecl; } -export function getDeclForBuilderLambdaMethodDecl( - node: arkts.AstNode, - isExternal?: boolean -): arkts.AstNode | undefined { +export function getDeclForBuilderLambdaMethodDecl(node: arkts.AstNode): arkts.AstNode | undefined { if (!node || !arkts.isMethodDefinition(node)) { return undefined; } @@ -154,7 +186,10 @@ export function getDeclForBuilderLambdaMethodDecl( return undefined; } -export function getDeclForBuilderLambda(node: arkts.AstNode, isExternal?: boolean): arkts.AstNode | undefined { +export function getDeclForBuilderLambda( + node: arkts.AstNode, + nodeDecl?: arkts.AstNode | undefined +): arkts.AstNode | undefined { if (!node || !arkts.isCallExpression(node)) { return undefined; } @@ -179,15 +214,21 @@ export function getDeclForBuilderLambda(node: arkts.AstNode, isExternal?: boolea currNode = _node.object; } - if (isBuilderLambdaCall(node)) { + if (isBuilderLambdaCall(node, nodeDecl)) { return node; } return undefined; } -export function isBuilderLambdaCall(node: arkts.CallExpression | arkts.Identifier): boolean { - const expr = arkts.isIdentifier(node) ? node : node.expression; - const decl = arkts.getDecl(expr); +export function isBuilderLambdaCall( + node: arkts.CallExpression | arkts.Identifier, + nodeDecl?: arkts.AstNode | undefined +): boolean { + let decl = nodeDecl; + if (decl === undefined) { + const expr = arkts.isIdentifier(node) ? node : node.expression; + decl = arkts.getDecl(expr); + } if (!decl) { return false; @@ -278,6 +319,11 @@ export function findBuilderLambdaDecl(node: arkts.CallExpression | arkts.Identif if (!decl) { return undefined; } + const moduleName: string = arkts.getProgramFromAstNode(decl).moduleName; + if (!moduleName) { + return undefined; + } + DeclarationCollector.getInstance().collect(decl); return decl; } @@ -301,12 +347,15 @@ export function findBuilderLambdaDeclInfo(decl: arkts.AstNode | undefined): Buil if (!decl) { return undefined; } - + const moduleName: string = arkts.getProgramFromAstNode(decl).moduleName; + if (!moduleName) { + return undefined; + } if (arkts.isMethodDefinition(decl)) { const params = decl.scriptFunction.params.map((p) => p.clone()); const returnType = decl.scriptFunction.returnTypeAnnotation?.clone(); const isFunctionCall = isBuilderLambdaFunctionCall(decl); - return { isFunctionCall, params, returnType }; + return { isFunctionCall, params, returnType, moduleName }; } return undefined; @@ -348,17 +397,17 @@ export function builderLambdaMethodDeclType(method: arkts.MethodDefinition): ark return method.scriptFunction.returnTypeAnnotation; } -export function builderLambdaTypeName(leaf: arkts.CallExpression): string | undefined { +export function builderLambdaType(leaf: arkts.CallExpression): arkts.Identifier | undefined { if (!callIsGoodForBuilderLambda(leaf)) { return undefined; } const node = leaf.expression; - let name: string | undefined; + let name: arkts.Identifier | undefined; if (arkts.isIdentifier(node)) { - name = node.name; + name = node; } if (arkts.isMemberExpression(node) && arkts.isIdentifier(node.object)) { - name = node.object.name; + name = node.object; } return name; } @@ -381,59 +430,49 @@ export function builderLambdaFunctionName(node: arkts.CallExpression): string | return undefined; } -/** - * Determine whether the node `` is `` bindable property. - * - * @param type type node - * @param bindableDecl bindable decalaration name - */ -export function hasBindableProperty(type: arkts.AstNode, bindableDecl: BindableDecl): boolean { - let res: boolean = false; - if (arkts.isETSUnionType(type)) { - type.types.forEach((item: arkts.TypeNode) => { - res = res || hasBindableProperty(item, bindableDecl); - }); - } - if (arkts.isETSTypeReference(type)) { - res = - res || - (!!type.part && - !!type.part.name && - arkts.isIdentifier(type.part.name) && - type.part.name.name === bindableDecl); - } - - return res; -} - /** * Determine whether `` is `$$()` call expression node. * * @param value expression node */ -export function isDoubleDollarCall(value: arkts.Expression): boolean { +export function isDoubleDollarCall( + value: arkts.Expression, + ignoreDecl: boolean = false +): value is arkts.CallExpression { + if (!arkts.isCallExpression(value)) { + return false; + } if ( - arkts.isCallExpression(value) && - value.expression && - arkts.isIdentifier(value.expression) && - value.expression.name + !(!!value.expression && arkts.isIdentifier(value.expression) && value.expression.name === Dollars.DOLLAR_DOLLAR) ) { - return value.expression.name === Dollars.DOLLAR_DOLLAR; + return false; } - return false; + if (!ignoreDecl) { + const decl = arkts.getDecl(value.expression); + if (!decl) { + return false; + } + const moduleName: string = arkts.getProgramFromAstNode(decl).moduleName; + if (!moduleName || !matchPrefix(ARKUI_IMPORT_PREFIX_NAMES, moduleName)) { + return false; + } + DeclarationCollector.getInstance().collect(decl); + } + return true; } /** - * get declaration type from `{xxx: }` or `fun()`. + * get declaration type from function call argument `fun()`. * - * @param value type node + * @param arg first argument in call expression. */ -export function getDecalTypeFromValue(value: arkts.Expression): arkts.TypeNode { - const decl: arkts.AstNode | undefined = arkts.getDecl(value); +export function getArgumentType(arg: arkts.Expression): arkts.TypeNode { + const isArrayElement = arkts.isMemberExpression(arg) && !!arg.property && arkts.isNumberLiteral(arg.property); + const decl: arkts.AstNode | undefined = arkts.getDecl(arg); if (!decl || !arkts.isClassProperty(decl)) { throw new Error('cannot get declaration'); } - if (isArrayType(decl.typeAnnotation!)) { + if (isArrayElement) { return getElementTypeFromArray(decl.typeAnnotation!)!; } return decl.typeAnnotation!; @@ -475,3 +514,34 @@ export function getElementTypeFromArray(arrayType: arkts.TypeNode): arkts.TypeNo } return undefined; } + +export function collectComponentAttributeImport(type: arkts.TypeNode | undefined, moduleName: string): void { + if ( + !type || + !arkts.isETSTypeReference(type) || + !type.part || + !type.part.name || + !arkts.isIdentifier(type.part.name) + ) { + return; + } + + const regex: RegExp = /(?\w+Attribute)(?:<.*>)?$/; + const name: string = type.part.name.name; + const match: RegExpExecArray | null = regex.exec(name); + const attributeName: string | undefined = match?.groups?.source; + if (!!attributeName) { + ImportCollector.getInstance().collectSource(attributeName, moduleName); + ImportCollector.getInstance().collectImport(attributeName); + } +} + +export function checkIsWithInIfConditionScope(statement: arkts.AstNode): boolean { + if (!statement.parent) { + return false; + } + if (arkts.isIfStatement(statement.parent)) { + return arkts.isBlockStatement(statement) || arkts.isIfStatement(statement); + } + return false; +} diff --git a/arkui-plugins/ui-plugins/checked-transformer.ts b/arkui-plugins/ui-plugins/checked-transformer.ts index eddd49be3..ee4daa33a 100644 --- a/arkui-plugins/ui-plugins/checked-transformer.ts +++ b/arkui-plugins/ui-plugins/checked-transformer.ts @@ -17,456 +17,201 @@ import * as arkts from '@koalaui/libarkts'; import { ProjectConfig } from '../common/plugin-context'; import { factory as structFactory } from './struct-translators/factory'; import { factory as builderLambdaFactory } from './builder-lambda-translators/factory'; -import { factory as uiFactory } from './ui-factory'; import { factory as entryFactory } from './entry-translators/factory'; import { AbstractVisitor } from '../common/abstract-visitor'; -import { annotation, collect, filterDefined } from '../common/arkts-utils'; -import { - CustomComponentNames, - getCustomComponentOptionsName, - getTypeNameFromTypeParameter, - getTypeParamsFromClassDecl, - getGettersFromClassDecl, - addMemoAnnotation, -} from './utils'; -import { hasDecorator, DecoratorNames } from './property-translators/utils'; +import { isBuilderLambda, isBuilderLambdaMethodDecl } from './builder-lambda-translators/utils'; +import { isEntryWrapperClass } from './entry-translators/utils'; +import { ImportCollector } from '../common/import-collector'; +import { DeclarationCollector } from '../common/declaration-collector'; +import { PropertyCache } from './property-translators/cache/propertyCache'; +import { LogCollector } from '../common/log-collector'; import { - isCustomComponentClass, - isKnownMethodDefinition, - isEtsGlobalClass, - isReourceNode, - ScopeInfoCollection, CustomComponentScopeInfo, - isMemoCall, - findCanAddMemoFromArrowFunction, + initResourceInfo, + loadBuildJson, + LoaderJson, + ResourceInfo, + ScopeInfoCollection, + isForEachDecl, } from './struct-translators/utils'; -import { isBuilderLambda, isBuilderLambdaMethodDecl } from './builder-lambda-translators/utils'; -import { isEntryWrapperClass } from './entry-translators/utils'; -import { classifyObservedTrack, classifyProperty, PropertyTranslator } from './property-translators'; -import { ObservedTrackTranslator } from './property-translators/observedTrack'; -import { nodeByType } from '@koalaui/libarkts/build/src/reexport-for-generated'; -import { isArkUICompatible, updateArkUICompatible } from './interop'; +import { + collectCustomComponentScopeInfo, + CustomComponentNames, + CustomDialogNames, + isCustomComponentClass, + isSpecificNewClass, +} from './utils'; +import { findAndCollectMemoableNode } from '../collectors/memo-collectors/factory'; +import { InteroperAbilityNames } from './interop/predefines'; +import { generateBuilderCompatible } from './interop/builder-interop'; +import { builderRewriteByType } from './builder-lambda-translators/builder-factory'; +import { MonitorCache } from './property-translators/cache/monitorCache'; export class CheckedTransformer extends AbstractVisitor { - private scopeInfoCollection: ScopeInfoCollection; + private scope: ScopeInfoCollection; + private legacyBuilderSet: Set = new Set(); projectConfig: ProjectConfig | undefined; + aceBuildJson: LoaderJson; + resourceInfo: ResourceInfo; constructor(projectConfig: ProjectConfig | undefined) { super(); this.projectConfig = projectConfig; - this.scopeInfoCollection = { customComponents: [] }; + this.scope = { customComponents: [] }; + this.aceBuildJson = loadBuildJson(this.projectConfig); + this.resourceInfo = initResourceInfo(this.projectConfig, this.aceBuildJson); + this.legacyBuilderSet = new Set(); + this.initBuilderMap(); + } + + initBuilderMap(): void { + const moduleList = this.projectConfig?.dependentModuleList; + if (moduleList === undefined) { + return; + } + for (const module of moduleList) { + const language = module.language; + const moduleName = module.moduleName; + if (language !== InteroperAbilityNames.ARKTS_1_1) { + continue; + } + if (!this.legacyBuilderSet.has(moduleName)) { + this.legacyBuilderSet.add(moduleName); + } + } } reset(): void { super.reset(); - this.scopeInfoCollection = { customComponents: [] }; + this.scope = { customComponents: [] }; + PropertyCache.getInstance().reset(); + MonitorCache.getInstance().reset(); + ImportCollector.getInstance().reset(); + DeclarationCollector.getInstance().reset(); + LogCollector.getInstance().reset(); } enter(node: arkts.AstNode): void { - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - this.scopeInfoCollection.customComponents.push({ name: node.definition!.ident!.name }); + if (arkts.isClassDeclaration(node) && !!node.definition && node.definition.body.length > 0) { + const customComponentInfo = collectCustomComponentScopeInfo(node); + if (!!customComponentInfo) { + this.scope.customComponents.push(customComponentInfo); + } } - if (arkts.isMethodDefinition(node) && this.scopeInfoCollection.customComponents.length > 0) { + if (arkts.isMethodDefinition(node) && this.scope.customComponents.length > 0) { const name = node.name.name; - const scopeInfo = this.scopeInfoCollection.customComponents.pop()!; + const scopeInfo = this.scope.customComponents.pop()!; scopeInfo.hasInitializeStruct ||= name === CustomComponentNames.COMPONENT_INITIALIZE_STRUCT; scopeInfo.hasUpdateStruct ||= name === CustomComponentNames.COMPONENT_UPDATE_STRUCT; - scopeInfo.hasReusableRebind ||= name === CustomComponentNames.REUSABLE_COMPONENT_REBIND_STATE; - this.scopeInfoCollection.customComponents.push(scopeInfo); + this.scope.customComponents.push(scopeInfo); } } exit(node: arkts.AstNode): void { - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - this.scopeInfoCollection.customComponents.pop(); + if ( + arkts.isClassDeclaration(node) && + this.scope.customComponents.length > 0 && + isCustomComponentClass(node, this.scope.customComponents[this.scope.customComponents.length - 1]) + ) { + this.scope.customComponents.pop(); + } + } + + isFromBuilder1_1(decl: arkts.AstNode | undefined): boolean { + if (!decl || this.legacyBuilderSet.size === 0) { + return false; + } + const moduleName = arkts.getProgramFromAstNode(decl).moduleName?.split('/')[0]; + + if (!this.legacyBuilderSet.has(moduleName)) { + return false; } + + let isFrom1_1 = false; + if (arkts.isMethodDefinition(decl)) { + const annotations = decl.scriptFunction.annotations; + const decorators: string[] = annotations.map((annotation) => { + return (annotation.expr as arkts.Identifier).name; + }); + decorators.forEach((element) => { + if (element === 'Builder') { + isFrom1_1 = true; + return; + } + }); + } + return isFrom1_1; + } + + addcompatibleComponentImport(): void { + ImportCollector.getInstance().collectSource('compatibleComponent', 'arkui.component.interop'); + ImportCollector.getInstance().collectImport('compatibleComponent'); } visitor(beforeChildren: arkts.AstNode): arkts.AstNode { this.enter(beforeChildren); - if (arkts.isCallExpression(beforeChildren) && isBuilderLambda(beforeChildren)) { - const lambda = builderLambdaFactory.transformBuilderLambda(beforeChildren, this.projectConfig); - return this.visitEachChild(lambda); + if (arkts.isCallExpression(beforeChildren)) { + const decl = arkts.getDecl(beforeChildren.expression); + if (arkts.isIdentifier(beforeChildren.expression) && this.isFromBuilder1_1(decl)) { + // Builder + this.addcompatibleComponentImport(); + return generateBuilderCompatible(beforeChildren, beforeChildren.expression.name); + } else if (isBuilderLambda(beforeChildren, decl)) { + const lambda = builderLambdaFactory.transformBuilderLambda(beforeChildren); + return this.visitEachChild(lambda); + } } else if (arkts.isMethodDefinition(beforeChildren) && isBuilderLambdaMethodDecl(beforeChildren)) { - const lambda = builderLambdaFactory.transformBuilderLambdaMethodDecl(beforeChildren); + const lambda = builderLambdaFactory.transformBuilderLambdaMethodDecl( + beforeChildren, + this.externalSourceName + ); return this.visitEachChild(lambda); } - const node = this.visitEachChild(beforeChildren); - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - let scope: CustomComponentScopeInfo | undefined; - const scopeInfos: CustomComponentScopeInfo[] = this.scopeInfoCollection.customComponents; - if (scopeInfos.length > 0) { - scope = scopeInfos[scopeInfos.length - 1]; + let node = this.visitEachChild(beforeChildren); + findAndCollectMemoableNode(node, (currNode: arkts.AstNode, nodeType: arkts.Es2pandaAstNodeType) => { + if (builderRewriteByType.has(nodeType)) { + node = builderRewriteByType.get(nodeType)!(currNode); } - const newClass: arkts.ClassDeclaration = tranformClassMembers( - node, - arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE), - scope - ); + return currNode; + }); + if ( + arkts.isClassDeclaration(node) && + this.scope.customComponents.length > 0 && + isCustomComponentClass(node, this.scope.customComponents[this.scope.customComponents.length - 1]) + ) { + const scope: CustomComponentScopeInfo = this.scope.customComponents[this.scope.customComponents.length - 1]; + const newClass: arkts.ClassDeclaration = structFactory.tranformClassMembers(node, scope); this.exit(beforeChildren); return newClass; - } else if (isEntryWrapperClass(node)) { - entryFactory.addMemoToEntryWrapperClassMethods(node); - return node; - } else if (arkts.isClassDeclaration(node) && isEtsGlobalClass(node)) { - return transformEtsGlobalClassMembers(node); - } else if (arkts.isCallExpression(node) && isReourceNode(node)) { - return transformResource(node, this.projectConfig); - } else if (findCanAddMemoFromArrowFunction(node)) { - return addMemoAnnotation(node); - } else if (arkts.isClassDeclaration(node)) { - return transformObservedTracked(node); - } else if (isArkUICompatible(node)) { - return updateArkUICompatible(node as arkts.CallExpression); - } else if (this.externalSourceName) { - return structFactory.transformExternalSource(this.externalSourceName, node); } - return node; - } -} - -export type ClassScopeInfo = { - isObserved: boolean; - classHasTrack: boolean; - getters: arkts.MethodDefinition[]; -}; - -function transformObservedTracked(node: arkts.ClassDeclaration): arkts.ClassDeclaration { - if (!node.definition) { - return node; - } - const isObserved: boolean = hasDecorator(node.definition, DecoratorNames.OBSERVED); - const classHasTrack: boolean = node.definition.body.some( - (member) => arkts.isClassProperty(member) && hasDecorator(member, DecoratorNames.TRACK) - ); - if (!isObserved && !classHasTrack) { - return node; - } - const updateClassDef: arkts.ClassDefinition = arkts.factory.updateClassDefinition( - node.definition, - node.definition.ident, - node.definition.typeParams, - node.definition.superTypeParams, - [ - ...node.definition.implements, - arkts.TSClassImplements.createTSClassImplements( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('IObservedObject')) - ) - ), - ], - undefined, - node.definition.super, - observedTrackPropertyMembers(classHasTrack, node.definition, isObserved), - node.definition.modifiers, - arkts.classDefinitionFlags(node.definition) - ); - return arkts.factory.updateClassDeclaration(node, updateClassDef); -} - -function observedTrackPropertyMembers( - classHasTrack: boolean, - definition: arkts.ClassDefinition, - isObserved: boolean -): arkts.AstNode[] { - const watchMembers: arkts.AstNode[] = createWatchMembers(); - const permissibleAddRefDepth: arkts.ClassProperty = arkts.factory.createClassProperty( - arkts.factory.createIdentifier('_permissibleAddRefDepth'), - arkts.factory.createNumericLiteral(0), - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('int32')) - ), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, - false - ); - - const meta: arkts.ClassProperty = arkts.factory.createClassProperty( - arkts.factory.createIdentifier('__meta'), - arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('MutableStateMeta')) - ), - [arkts.factory.createStringLiteral('@Observe properties (no @Track)')] - ), - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('MutableStateMeta')) - ), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, - false - ); - - const getters: arkts.MethodDefinition[] = getGettersFromClassDecl(definition); - - const classScopeInfo: ClassScopeInfo = { - isObserved: isObserved, - classHasTrack: classHasTrack, - getters: getters, - }; - - const propertyTranslators: ObservedTrackTranslator[] = filterDefined( - definition.body.map((it) => classifyObservedTrack(it, classScopeInfo)) - ); - - const propertyMembers = propertyTranslators.map((translator) => translator.translateMember()); - - const nonClassPropertyOrGetter: arkts.AstNode[] = definition.body.filter( - (member) => - !arkts.isClassProperty(member) && - !( - arkts.isMethodDefinition(member) && - arkts.hasModifierFlag(member, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_GETTER) - ) - ); - - return [ - ...watchMembers, - ...(classHasTrack ? [permissibleAddRefDepth] : [permissibleAddRefDepth, meta]), - ...collect(...propertyMembers), - ...nonClassPropertyOrGetter, - ...classScopeInfo.getters, - ]; -} - -function createWatchMethod( - methodName: string, - returnType: arkts.Es2pandaPrimitiveType, - paramName: string, - paramType: string, - isReturnStatement: boolean -): arkts.MethodDefinition { - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, - arkts.factory.createIdentifier(methodName), - arkts.factory.createScriptFunction( - arkts.factory.createBlock([ - isReturnStatement - ? arkts.factory.createReturnStatement( - arkts.factory.createCallExpression(thisSubscribedWatchesMember(methodName), undefined, [ - arkts.factory.createIdentifier(paramName), - ]) - ) - : arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression(thisSubscribedWatchesMember(methodName), undefined, [ - arkts.factory.createIdentifier(paramName), - ]) - ), - ]), - arkts.factory.createFunctionSignature( - undefined, - [ - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier( - paramName, - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(paramType)) - ) - ), - undefined - ), - ], - arkts.factory.createPrimitiveType(returnType), - false - ), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC - ), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, - false - ); -} - -function createWatchMembers(): arkts.AstNode[] { - const subscribedWatches: arkts.ClassProperty = arkts.factory.createClassProperty( - arkts.factory.createIdentifier('subscribedWatches'), - arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('SubscribedWatches')) - ), - [] - ), - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('SubscribedWatches')) - ), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, - false - ); - - const addWatchSubscriber = createWatchMethod( - 'addWatchSubscriber', - arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID, - 'watchId', - 'WatchIdType', - false - ); - - const removeWatchSubscriber = createWatchMethod( - 'removeWatchSubscriber', - arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_BOOLEAN, - 'watchId', - 'WatchIdType', - true - ); - - const executeOnSubscribingWatches = createWatchMethod( - 'executeOnSubscribingWatches', - arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID, - 'propertyName', - 'string', - false - ); - - return [subscribedWatches, addWatchSubscriber, removeWatchSubscriber, executeOnSubscribingWatches]; -} - -function thisSubscribedWatchesMember(member: string): arkts.MemberExpression { - return arkts.factory.createMemberExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('subscribedWatches'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.factory.createIdentifier(member), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ); -} - -/** - * @deprecated - */ -function tranformClassMembers( - node: arkts.ClassDeclaration, - isDecl?: boolean, - scope?: CustomComponentScopeInfo -): arkts.ClassDeclaration { - if (!node.definition) { - return node; - } - - let classTypeName: string | undefined; - let classOptionsName: string | undefined; - if (isDecl) { - const [classType, classOptions] = getTypeParamsFromClassDecl(node); - classTypeName = getTypeNameFromTypeParameter(classType); - classOptionsName = getTypeNameFromTypeParameter(classOptions); - } - const definition: arkts.ClassDefinition = node.definition; - const className: string | undefined = node.definition.ident?.name; - if (!className) { - throw new Error('Non Empty className expected for Component'); - } - - const propertyTranslators: PropertyTranslator[] = filterDefined( - definition.body.map((it) => classifyProperty(it, className)) - ); - const translatedMembers: arkts.AstNode[] = tranformPropertyMembers( - className, - propertyTranslators, - classOptionsName ?? getCustomComponentOptionsName(className), - isDecl, - scope - ); - const updateMembers: arkts.AstNode[] = definition.body - .filter((member) => !arkts.isClassProperty(member)) - .map((member: arkts.AstNode) => - transformOtherMembersInClass(member, classTypeName, classOptionsName, className, isDecl) - ); - - const updateClassDef: arkts.ClassDefinition = structFactory.updateCustomComponentClass(definition, [ - ...translatedMembers, - ...updateMembers, - ]); - return arkts.factory.updateClassDeclaration(node, updateClassDef); -} - -/** - * @deprecated - */ -function transformOtherMembersInClass( - member: arkts.AstNode, - classTypeName: string | undefined, - classOptionsName: string | undefined, - className: string, - isDecl?: boolean -): arkts.AstNode { - if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.BUILDER)) { - member.scriptFunction.setAnnotations([annotation('memo')]); - return member; - } - if ( - arkts.isMethodDefinition(member) && - isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_CONSTRUCTOR_ORI) && - !isDecl - ) { - return uiFactory.createConstructorMethod(member); + return this.visitorAstNode(node); } - if (arkts.isMethodDefinition(member) && isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_BUILD_ORI)) { - return structFactory.transformBuildMethodWithOriginBuild( - member, - classTypeName ?? className, - classOptionsName ?? getCustomComponentOptionsName(className), - isDecl - ); - } - return member; -} -/** - * @deprecated - */ -function tranformPropertyMembers( - className: string, - propertyTranslators: PropertyTranslator[], - optionsTypeName: string, - isDecl?: boolean, - scope?: CustomComponentScopeInfo -): arkts.AstNode[] { - const propertyMembers = propertyTranslators.map((translator) => translator.translateMember()); - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(className); - const collections = []; - if (!scope?.hasInitializeStruct) { - collections.push(structFactory.createInitializeStruct(currentStructInfo, optionsTypeName, isDecl)); - } - if (!scope?.hasUpdateStruct) { - collections.push(structFactory.createUpdateStruct(currentStructInfo, optionsTypeName, isDecl)); - } - if (currentStructInfo.isReusable) { - collections.push(structFactory.toRecord(optionsTypeName, currentStructInfo.toRecordBody)); - } - return collect(...collections, ...propertyMembers); -} + visitorAstNode(node: arkts.AstNode): arkts.AstNode { + if (isEntryWrapperClass(node)) { + entryFactory.addMemoToEntryWrapperClassMethods(node); + return node; + } else if (arkts.isClassDeclaration(node)) { + return structFactory.transformNormalClass(node, this.externalSourceName); + } else if (arkts.isCallExpression(node)) { + return structFactory.transformCallExpression(node, this.projectConfig, this.resourceInfo); + } else if (arkts.isMethodDefinition(node) && isForEachDecl(node, this.externalSourceName)) { + return structFactory.AddArrowTypeForParameter(node); + } else if (arkts.isTSInterfaceDeclaration(node)) { + return structFactory.tranformInterfaceMembers(node, this.externalSourceName); + } else if ( + arkts.isETSNewClassInstanceExpression(node) && + isSpecificNewClass(node, CustomDialogNames.CUSTOM_DIALOG_CONTROLLER) + ) { + return structFactory.transformCustomDialogController(node); + } -/** - * @deprecated - */ -function transformEtsGlobalClassMembers(node: arkts.ClassDeclaration): arkts.ClassDeclaration { - if (!node.definition) { + if (arkts.isEtsScript(node) && ImportCollector.getInstance().importInfos.length > 0) { + ImportCollector.getInstance().insertCurrentImports(this.program); + LogCollector.getInstance().shouldIgnoreError(this.projectConfig?.ignoreError); + LogCollector.getInstance().emitLogInfo(); + } return node; } - node.definition.body.map((member: arkts.AstNode) => { - if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.BUILDER)) { - member.scriptFunction.setAnnotations([annotation('memo')]); - } - return member; - }); - return node; -} - -/** - * @deprecated - */ -function transformResource( - resourceNode: arkts.CallExpression, - projectConfig: ProjectConfig | undefined -): arkts.CallExpression { - const newArgs: arkts.AstNode[] = [ - arkts.factory.create1StringLiteral(projectConfig?.bundleName ? projectConfig.bundleName : ''), - arkts.factory.create1StringLiteral(projectConfig?.moduleName ? projectConfig.moduleName : ''), - ...resourceNode.arguments, - ]; - return structFactory.generateTransformedResource(resourceNode, newArgs); } diff --git a/arkui-plugins/ui-plugins/component-transformer.ts b/arkui-plugins/ui-plugins/component-transformer.ts index 46c7bfbf2..37db0024f 100644 --- a/arkui-plugins/ui-plugins/component-transformer.ts +++ b/arkui-plugins/ui-plugins/component-transformer.ts @@ -21,37 +21,47 @@ import { AbstractVisitor, VisitorOptions } from '../common/abstract-visitor'; import { CustomComponentNames, getCustomComponentOptionsName, - createOptionalClassProperty, findLocalImport, + CustomComponentInfo, + collectCustomComponentScopeInfo, + isComponentStruct, + isCustomDialogControllerOptions, + getComponentExtendsName, + ComponentType, } from './utils'; -import { isAnnotation, updateStructMetadata, backingField, expectName, annotation } from '../common/arkts-utils'; -import { EntryWrapperNames, findEntryWithStorageInClassAnnotations } from './entry-translators/utils'; -import { factory as entryFactory } from './entry-translators/factory'; import { - hasDecorator, - DecoratorNames, - getStateManagementType, - collectPropertyDecorators, -} from './property-translators/utils'; + backingField, + expectName, + annotation, + filterDefined, + collect, + createAndInsertImportDeclaration, + isDecoratorAnnotation, +} from '../common/arkts-utils'; +import { ProjectConfig } from '../common/plugin-context'; +import { getEntryParams } from './entry-translators/utils'; +import { factory as entryFactory } from './entry-translators/factory'; +import { hasDecoratorName, findDecoratorInfos } from './property-translators/utils'; import { factory } from './ui-factory'; +import { factory as propertyFactory } from './property-translators/factory'; import { StructMap } from '../common/program-visitor'; -import { generateTempCallFunction } from './interop'; -import { stringify } from 'querystring'; +import { + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + DecoratorIntrinsicNames, + DecoratorNames, + DECORATOR_TYPE_MAP, + ENTRY_POINT_IMPORT_SOURCE_NAME, + NavigationNames, + EntryWrapperNames, +} from '../common/predefines'; +import { generateInstantiateInterop } from './interop/interop'; export interface ComponentTransformerOptions extends VisitorOptions { arkui?: string; + projectConfig?: ProjectConfig; } -type ScopeInfo = { - name: string; - isEntry?: boolean; - isComponent?: boolean; - isReusable?: boolean; -}; - -interface ComponentContext { - structMembers: Map; -} +type ScopeInfo = CustomComponentInfo; export interface InteropContext { className: string; @@ -59,25 +69,37 @@ export interface InteropContext { line?: number; col?: number; arguments?: arkts.ObjectExpression; + content?: arkts.ArrowFunctionExpression; } export class ComponentTransformer extends AbstractVisitor { private scopeInfos: ScopeInfo[] = []; private componentInterfaceCollection: arkts.TSInterfaceDeclaration[] = []; private entryNames: string[] = []; - private reusableNames: string[] = []; - private readonly arkui?: string; - private context: ComponentContext = { structMembers: new Map() }; + private structMembersMap: Map = new Map(); private isCustomComponentImported: boolean = false; + private isCustomComponentV2Imported: boolean = false; + private isBaseCustomDialogImported: boolean = false; private isEntryPointImported: boolean = false; - private hasLegacy = false; + private isPageLifeCycleImported: boolean = false; + private isLayoutCallbackImported: boolean = false; + private shouldAddLinkIntrinsic: boolean = false; + private hasLegacy: boolean = false; private legacyStructMap: Map = new Map(); private legacyCallMap: Map = new Map(); + private projectConfig: ProjectConfig | undefined; + private entryRouteName: string | undefined; + private componentType: ComponentType = { + hasComponent: false, + hasComponentV2: false, + hasCustomDialog: false, + hasCustomLayout: false, + }; constructor(options?: ComponentTransformerOptions) { const _options: ComponentTransformerOptions = options ?? {}; super(_options); - this.arkui = _options.arkui; + this.projectConfig = options?.projectConfig; } reset(): void { @@ -85,39 +107,74 @@ export class ComponentTransformer extends AbstractVisitor { this.scopeInfos = []; this.componentInterfaceCollection = []; this.entryNames = []; - this.reusableNames = []; - this.context = { structMembers: new Map() }; + this.structMembersMap = new Map(); this.isCustomComponentImported = false; + this.isCustomComponentV2Imported = false; + this.isBaseCustomDialogImported = false; this.isEntryPointImported = false; + this.isPageLifeCycleImported = false; + this.isLayoutCallbackImported = false; + this.shouldAddLinkIntrinsic = false; this.hasLegacy = false; this.legacyStructMap = new Map(); this.legacyCallMap = new Map(); + this.componentType = { + hasComponent: false, + hasComponentV2: false, + hasCustomDialog: false, + hasCustomLayout: false, + }; } enter(node: arkts.AstNode) { if (arkts.isStructDeclaration(node) && !!node.definition.ident) { - const scopeInfo: ScopeInfo = { name: node.definition.ident.name }; - node.definition.annotations.forEach((anno) => { - scopeInfo.isEntry ||= isAnnotation(anno, CustomComponentNames.ENTRY_ANNOTATION_NAME); - scopeInfo.isComponent ||= isAnnotation(anno, CustomComponentNames.COMPONENT_ANNOTATION_NAME); - scopeInfo.isReusable ||= isAnnotation(anno, CustomComponentNames.RESUABLE_ANNOTATION_NAME); - }); - this.scopeInfos.push(scopeInfo); + const info: ScopeInfo | undefined = collectCustomComponentScopeInfo(node); + if (info) { + this.scopeInfos.push(info); + } } if (arkts.isETSImportDeclaration(node) && !this.isCustomComponentImported) { this.isCustomComponentImported = !!findLocalImport( node, - CustomComponentNames.COMPONENT_DEFAULT_IMPORT, + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, CustomComponentNames.COMPONENT_CLASS_NAME ); } + if (arkts.isETSImportDeclaration(node) && !this.isCustomComponentV2Imported) { + this.isCustomComponentV2Imported = !!findLocalImport( + node, + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.COMPONENT_V2_CLASS_NAME + ); + } + if (arkts.isETSImportDeclaration(node) && !this.isBaseCustomDialogImported) { + this.isBaseCustomDialogImported = !!findLocalImport( + node, + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.BASE_CUSTOM_DIALOG_NAME + ); + } if (arkts.isETSImportDeclaration(node) && !this.isEntryPointImported) { this.isEntryPointImported = !!findLocalImport( node, - EntryWrapperNames.ENTRY_DEFAULT_IMPORT, + ENTRY_POINT_IMPORT_SOURCE_NAME, EntryWrapperNames.ENTRY_POINT_CLASS_NAME ); } + if (arkts.isETSImportDeclaration(node) && !this.isPageLifeCycleImported) { + this.isPageLifeCycleImported = !!findLocalImport( + node, + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.PAGE_LIFE_CYCLE + ); + } + if (arkts.isETSImportDeclaration(node) && !this.isLayoutCallbackImported) { + this.isLayoutCallbackImported = !!findLocalImport( + node, + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.LAYOUT_CALLBACK + ); + } } exit(node: arkts.AstNode) { @@ -129,22 +186,14 @@ export class ComponentTransformer extends AbstractVisitor { } } - isComponentStruct(): boolean { - if (this.scopeInfos.length === 0) return false; - const scopeInfo: ScopeInfo = this.scopeInfos[this.scopeInfos.length - 1]; - return !!scopeInfo.isComponent; - } - - createImportDeclaration(): void { - const source: arkts.StringLiteral = arkts.factory.create1StringLiteral( - this.arkui ?? CustomComponentNames.COMPONENT_DEFAULT_IMPORT - ); - const imported: arkts.Identifier = arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_CLASS_NAME); + createImportDeclaration(sourceName: string, importedName: string): void { + const source: arkts.StringLiteral = arkts.factory.create1StringLiteral(sourceName); + const imported: arkts.Identifier = arkts.factory.createIdentifier(importedName); // Insert this import at the top of the script's statements. if (!this.program) { throw Error('Failed to insert import: Transformer has no program'); } - factory.createAndInsertImportDeclaration( + createAndInsertImportDeclaration( source, imported, imported, @@ -154,20 +203,35 @@ export class ComponentTransformer extends AbstractVisitor { } processEtsScript(node: arkts.EtsScript): arkts.EtsScript { + if (this.isExternal && this.externalSourceName === ENTRY_POINT_IMPORT_SOURCE_NAME) { + const navInterface = entryFactory.createNavInterface(); + navInterface.modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT; + return arkts.factory.updateEtsScript(node, [...node.statements, navInterface]); + } if (this.isExternal && this.componentInterfaceCollection.length === 0 && this.entryNames.length === 0) { return node; } - let updateStatements: arkts.AstNode[] = []; + const updateStatements: arkts.AstNode[] = []; + if (this.shouldAddLinkIntrinsic) { + const expr = arkts.factory.createIdentifier(DecoratorIntrinsicNames.LINK); + updateStatements.push(factory.createIntrinsicAnnotationDeclaration({ expr })); + } if (this.componentInterfaceCollection.length > 0) { - if (!this.isCustomComponentImported) this.createImportDeclaration(); + this.insertComponentImport(); updateStatements.push(...this.componentInterfaceCollection); } if (this.entryNames.length > 0) { if (!this.isEntryPointImported) entryFactory.createAndInsertEntryPointImport(this.program); - // TODO: normally, we should only have at most one @Entry component in a single file. + // normally, we should only have at most one @Entry component in a single file. // probably need to handle error message here. + if (!this.isPageLifeCycleImported) + this.createImportDeclaration(CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, CustomComponentNames.PAGE_LIFE_CYCLE); updateStatements.push(...this.entryNames.map(entryFactory.generateEntryWrapper)); + updateStatements.push( + entryFactory.callRegisterNamedRouter(this.entryRouteName, this.projectConfig, this.program?.absName) + ); + this.createImportDeclaration(ENTRY_POINT_IMPORT_SOURCE_NAME, NavigationNames.NAVINTERFACE); } if (updateStatements.length > 0) { return arkts.factory.updateEtsScript(node, [...node.statements, ...updateStatements]); @@ -175,38 +239,83 @@ export class ComponentTransformer extends AbstractVisitor { return node; } + insertComponentImport(): void { + if (!this.isCustomComponentImported && this.componentType.hasComponent) { + this.createImportDeclaration( + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.COMPONENT_CLASS_NAME + ); + } + if (!this.isCustomComponentV2Imported && this.componentType.hasComponentV2) { + this.createImportDeclaration( + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.COMPONENT_V2_CLASS_NAME + ); + } + if (!this.isBaseCustomDialogImported && this.componentType.hasCustomDialog) { + this.createImportDeclaration( + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.BASE_CUSTOM_DIALOG_NAME + ); + } + if (!this.isLayoutCallbackImported && this.componentType.hasCustomLayout) { + this.createImportDeclaration(CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, CustomComponentNames.LAYOUT_CALLBACK); + } + } + + updateEntryPoint(node: arkts.ClassDeclaration): arkts.ClassDeclaration { + if (!node.definition) { + return node; + } + return arkts.factory.updateClassDeclaration( + node, + arkts.factory.updateClassDefinition( + node.definition, + node.definition.ident, + node.definition.typeParams, + node.definition.superTypeParams, + node.definition.implements, + undefined, + node.definition.super, + [entryFactory.generateRegisterNamedRouter(), ...node.definition.body], + node.definition.modifiers, + arkts.classDefinitionFlags(node.definition) + ) + ); + } + createStaticMethod(definition: arkts.ClassDefinition): arkts.MethodDefinition { + const isDecl: boolean = arkts.hasModifierFlag(definition, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE); + const modifiers = + arkts.classDefinitionFlags(definition) | + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC; + const body = isDecl ? undefined : arkts.factory.createBlock([arkts.factory.createReturnStatement()]); const param: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier( CustomComponentNames.OPTIONS, - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier(getCustomComponentOptionsName(definition.ident!.name)) - ) - ) + factory.createTypeReferenceFromString(getCustomComponentOptionsName(definition.ident!.name)) ), undefined ); - - const script = arkts.factory.createScriptFunction( - arkts.factory.createBlock([arkts.factory.createReturnStatement()]), - arkts.FunctionSignature.createFunctionSignature( - undefined, - [param], - arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), - false - ), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC - ); - - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, - arkts.factory.createIdentifier(CustomComponentNames.BUILDCOMPATIBLENODE), - script, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, - false - ); + const returnTypeAnnotation = arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID); + const flags = arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD; + const kind = arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD; + const key = arkts.factory.createIdentifier(CustomComponentNames.BUILDCOMPATIBLENODE); + + return factory.createMethodDefinition({ + key, + kind, + function: { + key, + body, + params: [param], + returnTypeAnnotation, + flags, + modifiers, + }, + modifiers, + }); } processComponent( @@ -217,29 +326,23 @@ export class ComponentTransformer extends AbstractVisitor { if (!className || scopeInfo?.name !== className) { return node; } - - arkts.GlobalInfo.getInfoInstance().add(className); - if (arkts.isStructDeclaration(node)) { this.collectComponentMembers(node, className); } - - if (scopeInfo.isReusable) { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(className); - currentStructInfo.isReusable = true; - arkts.GlobalInfo.getInfoInstance().setStructInfo(className, currentStructInfo); - } - - this.componentInterfaceCollection.push(this.generateComponentInterface(className, node.modifiers)); - + const customComponentInterface = this.generateComponentInterface( + className, + node.modifiers | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT, + Object.values(scopeInfo.annotations ?? {}).map((anno) => anno.clone()) + ); + this.componentInterfaceCollection.push(customComponentInterface); const definition: arkts.ClassDefinition = node.definition!; const newDefinitionBody: arkts.AstNode[] = []; - if (scopeInfo.isEntry) { + if (!!scopeInfo.annotations?.entry) { this.entryNames.push(className); - const entryWithStorage: arkts.ClassProperty | undefined = - findEntryWithStorageInClassAnnotations(definition); - if (!!entryWithStorage) { - newDefinitionBody.push(entryFactory.createEntryLocalStorageInClass(entryWithStorage)); + const { storage, useSharedStorage, routeName } = getEntryParams(definition); + entryFactory.transformStorageParams(storage, useSharedStorage, definition); + if (routeName && routeName.value && arkts.isStringLiteral(routeName.value)) { + this.entryRouteName = routeName.value.str; } } const newDefinition: arkts.ClassDefinition = this.createNewDefinition( @@ -250,8 +353,11 @@ export class ComponentTransformer extends AbstractVisitor { ); if (arkts.isStructDeclaration(node)) { + arkts.classDefinitionSetFromStructModifier(newDefinition); const _node = arkts.factory.createClassDeclaration(newDefinition); _node.modifiers = node.modifiers; + _node.startPosition = node.startPosition; + _node.endPosition = node.endPosition; return _node; } else { return arkts.factory.updateClassDeclaration(node, newDefinition); @@ -274,95 +380,104 @@ export class ComponentTransformer extends AbstractVisitor { staticMethodBody.push(buildCompatibleNode); } } - return arkts.factory.updateClassDefinition( - definition, - definition.ident, - undefined, - undefined, // superTypeParams doen't work - definition.implements, - undefined, - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_CLASS_NAME), - arkts.factory.createTSTypeParameterInstantiation([ - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(className)) - ), - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier( - `${CustomComponentNames.COMPONENT_INTERFACE_PREFIX}${className}` - ) - ) - ), - ]) - ) - ), - [ - ...newDefinitionBody, - ...definition.body.map((st: arkts.AstNode) => factory.PreprocessClassPropertyModifier(st)), - ...staticMethodBody, - ], - definition.modifiers, - arkts.classDefinitionFlags(definition) | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_FINAL - ); + const scopeInfo = this.scopeInfos[this.scopeInfos.length - 1]; + const extendsName: string = getComponentExtendsName(scopeInfo.annotations, this.componentType); + return arkts.factory + .createClassDefinition( + definition.ident, + undefined, + undefined, // superTypeParams doen't work + [...definition.implements, ...factory.generateImplementsForStruct(scopeInfo.annotations)], + undefined, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(extendsName), + arkts.factory.createTSTypeParameterInstantiation([ + factory.createTypeReferenceFromString(className), + factory.createTypeReferenceFromString( + `${CustomComponentNames.COMPONENT_INTERFACE_PREFIX}${className}` + ), + ]) + ) + ), + [ + ...newDefinitionBody, + ...definition.body.map((st: arkts.AstNode) => + factory.PreprocessClassPropertyModifier(st, scopeInfo.isDecl) + ), + ...staticMethodBody, + ], + definition.modifiers, + arkts.classDefinitionFlags(definition) | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_FINAL + ) + .setAnnotations(definition.annotations); } - generateComponentInterface(name: string, modifiers: number): arkts.TSInterfaceDeclaration { - const interfaceNode = arkts.factory.createInterfaceDeclaration( - [], - arkts.factory.createIdentifier(getCustomComponentOptionsName(name)), - nullptr, // TODO: wtf - arkts.factory.createInterfaceBody([...(this.context.structMembers.get(name) || [])]), - false, - false - ); + generateComponentInterface( + name: string, + modifiers: number, + annotations?: readonly arkts.AnnotationUsage[] + ): arkts.TSInterfaceDeclaration { + const interfaceNode = arkts.factory + .createInterfaceDeclaration( + [], + arkts.factory.createIdentifier(getCustomComponentOptionsName(name)), + nullptr, + arkts.factory.createInterfaceBody([...(this.structMembersMap.get(name) || [])]), + false, + false + ) + .setAnnotations(annotations ?? []); interfaceNode.modifiers = modifiers; return interfaceNode; } collectComponentMembers(node: arkts.StructDeclaration, className: string): void { - const structInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(className); - if (!this.context.structMembers.has(className)) { - this.context.structMembers.set(className, []); - } - node.definition.body.map((it) => { - if (arkts.isClassProperty(it)) { - if (hasDecorator(it, DecoratorNames.PROVIDE)) { - factory.processNoAliasProvideVariable(it); - } - this.context.structMembers.get(className)!.push(...this.createInterfaceInnerMember(it, structInfo)); - } - }); - arkts.GlobalInfo.getInfoInstance().setStructInfo(className, structInfo); + const members = filterDefined( + collect( + ...node.definition.body.filter(arkts.isClassProperty).map((it) => { + if (hasDecoratorName(it, DecoratorNames.PROVIDE)) { + factory.processNoAliasProvideVariable(it); + } + return this.createInterfaceInnerMember(it); + }) + ) + ); + this.structMembersMap.set(className, members); } - createInterfaceInnerMember(member: arkts.ClassProperty, structInfo: arkts.StructInfo): arkts.ClassProperty[] { + createInterfaceInnerMember(member: arkts.ClassProperty): arkts.ClassProperty[] { const originalName: string = expectName(member.key); - const newName: string = backingField(originalName); - - const properties = collectPropertyDecorators(member); - const hasStateManagementType = properties.length > 0; - updateStructMetadata(structInfo, originalName, properties, member.modifiers, hasStateManagementType); - - const originMember: arkts.ClassProperty = createOptionalClassProperty( + const originMember: arkts.ClassProperty = propertyFactory.createOptionalClassProperty( originalName, member, - '', + undefined, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC ); - if (member.annotations.length > 0 && !hasDecorator(member, DecoratorNames.BUILDER_PARAM)) { - const newMember: arkts.ClassProperty = createOptionalClassProperty( + const infos = findDecoratorInfos(member); + const buildParamInfo = infos.find((it) => + isDecoratorAnnotation(it.annotation, DecoratorNames.BUILDER_PARAM, true) + ); + if (!!buildParamInfo) { + originMember.setAnnotations([buildParamInfo.annotation.clone()]); + return [originMember]; + } + const targetInfo = infos.find((it) => DECORATOR_TYPE_MAP.has(it.name)); + if (!!targetInfo) { + const newName: string = backingField(originalName); + const newMember: arkts.ClassProperty = propertyFactory.createOptionalClassProperty( newName, member, - getStateManagementType(member), + undefined, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC ); + newMember.setAnnotations(member.annotations); + if (isDecoratorAnnotation(targetInfo.annotation, DecoratorNames.LINK, true)) { + this.shouldAddLinkIntrinsic = true; + originMember.setAnnotations([annotation(DecoratorIntrinsicNames.LINK)]); + } return [originMember, newMember]; } - if (hasDecorator(member, DecoratorNames.BUILDER_PARAM) && !!originMember.typeAnnotation) { - originMember.typeAnnotation.setAnnotations([annotation('memo')]); - } return [originMember]; } @@ -371,16 +486,16 @@ export class ComponentTransformer extends AbstractVisitor { this.hasLegacy = true; } - processImport(node: arkts.ETSImportDeclaration): void { + processInteropImport(node: arkts.ETSImportDeclaration): void { const source = node.source?.str!; - const specifiers = node.specifiers; + const specifiers = node.specifiers as arkts.ImportSpecifier[]; if (this.legacyStructMap.has(source)) { const structMap = this.legacyStructMap.get(source); if (!structMap) { return; } for (const specifier of specifiers) { - const name = specifier.local.name; + const name = (specifier as arkts.ImportSpecifier)!.local!.name; if (structMap[name]) { this.legacyCallMap.set(name, structMap[name]); } @@ -388,42 +503,71 @@ export class ComponentTransformer extends AbstractVisitor { } } + processInteropCall(node: arkts.CallExpression): arkts.CallExpression { + const ident = node.expression; + if (!(ident instanceof arkts.Identifier)) { + return node; + } + const className = ident.name; + const trailingBlock = node.trailingBlock; + const content = trailingBlock ? arkts.factory.createArrowFunction( + factory.createScriptFunction({ + body: trailingBlock, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + }) + ) : undefined; + if (this.legacyCallMap.has(className)) { + const path = this.legacyCallMap.get(className)!; + const args = node.arguments; + const context: InteropContext = { + className: className, + path: path, + arguments: args && args.length === 1 && args[0] instanceof arkts.ObjectExpression ? args[0] : undefined, + content: content + }; + return generateInstantiateInterop(context); + } + return node; + } + visitor(node: arkts.AstNode): arkts.AstNode { this.enter(node); const newNode = this.visitEachChild(node); if (arkts.isEtsScript(newNode)) { return this.processEtsScript(newNode); } - if (arkts.isStructDeclaration(newNode) && this.isComponentStruct()) { + if ( + arkts.isStructDeclaration(newNode) && + this.scopeInfos.length > 0 && + isComponentStruct(newNode, this.scopeInfos[this.scopeInfos.length - 1]) + ) { const updateNode = this.processComponent(newNode); this.exit(newNode); return updateNode; } + if ( + arkts.isClassDeclaration(newNode) && + this.externalSourceName === ENTRY_POINT_IMPORT_SOURCE_NAME && + newNode.definition?.ident?.name === EntryWrapperNames.ENTRY_POINT_CLASS_NAME + ) { + return this.updateEntryPoint(newNode); + } + if ( + arkts.isTSInterfaceDeclaration(newNode) && + isCustomDialogControllerOptions(newNode, this.externalSourceName) + ) { + return factory.updateCustomDialogOptionsInterface(newNode); + } + // process interop code if (!this.hasLegacy) { return newNode; } if (arkts.isETSImportDeclaration(newNode)) { - this.processImport(newNode); + this.processInteropImport(newNode); } if (arkts.isCallExpression(newNode)) { - const ident = newNode.expression; - if (!(ident instanceof arkts.Identifier)) { - return newNode; - } - const className = ident.name; - if (this.legacyCallMap.has(className)) { - const path = this.legacyCallMap.get(className)!; - // const pathName = 'path/har1'; - const args = newNode.arguments; - const context: InteropContext = { - className: className, - path: path, - arguments: args && args.length === 1 && args[0] instanceof arkts.ObjectExpression - ? args[0] - : undefined - }; - return generateTempCallFunction(context); - } + return this.processInteropCall(newNode); } return newNode; } diff --git a/arkui-plugins/ui-plugins/entry-translators/factory.ts b/arkui-plugins/ui-plugins/entry-translators/factory.ts index fca4fe867..e4dd29d22 100644 --- a/arkui-plugins/ui-plugins/entry-translators/factory.ts +++ b/arkui-plugins/ui-plugins/entry-translators/factory.ts @@ -14,12 +14,13 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getInteropPath } from '../../path'; -const interop = require(getInteropPath()); -const nullptr = interop.nullptr; -import { EntryWrapperNames } from './utils'; -import { annotation } from '../../common/arkts-utils'; +import * as path from 'path'; +import { annotation, createAndInsertImportDeclaration } from '../../common/arkts-utils'; +import { ENTRY_POINT_IMPORT_SOURCE_NAME, EntryWrapperNames, NavigationNames } from '../../common/predefines'; +import { ProjectConfig } from '../../common/plugin-context'; import { factory as uiFactory } from '../ui-factory'; +import { getRelativePagePath } from './utils'; +import { addMemoAnnotation } from '../../collectors/memo-collectors/utils'; export class factory { /** @@ -83,11 +84,7 @@ export class factory { */ static generateEntryFunction(name: string): arkts.MethodDefinition { const exp = arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createIdentifier(name), - undefined, - [arkts.factory.createUndefinedLiteral()] // TODO: Add this undefined later - ) + arkts.factory.createCallExpression(arkts.factory.createIdentifier(name), undefined, []) ); const key: arkts.Identifier = arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_FUNC); const block = arkts.factory.createBlock([exp]); @@ -148,11 +145,7 @@ export class factory { */ static generateEntryProperty(name: string): arkts.ClassProperty { const exp = arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createIdentifier(name), - undefined, - [arkts.factory.createUndefinedLiteral()] // TODO: Add this undefined later - ) + arkts.factory.createCallExpression(arkts.factory.createIdentifier(name), undefined, []) ); const key: arkts.Identifier = arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_FUNC); const block: arkts.BlockStatement = arkts.factory.createBlock([exp]); @@ -226,7 +219,8 @@ export class factory { !!member.scriptFunction.id && member.scriptFunction.id.name === EntryWrapperNames.ENTRY_FUNC ) { - member.scriptFunction.setAnnotations([annotation('memo')]); + addMemoAnnotation(member.scriptFunction); + arkts.NodeCache.getInstance().collect(member); } }); } @@ -274,13 +268,13 @@ export class factory { * to the top of script's statements. */ static createAndInsertEntryPointImport(program?: arkts.Program) { - const source: arkts.StringLiteral = arkts.factory.create1StringLiteral(EntryWrapperNames.ENTRY_DEFAULT_IMPORT); + const source: arkts.StringLiteral = arkts.factory.create1StringLiteral(ENTRY_POINT_IMPORT_SOURCE_NAME); const imported: arkts.Identifier = arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_POINT_CLASS_NAME); // Insert this import at the top of the script's statements. if (!program) { throw Error('Failed to insert import: Transformer has no program'); } - uiFactory.createAndInsertImportDeclaration( + createAndInsertImportDeclaration( source, imported, imported, @@ -288,4 +282,186 @@ export class factory { program ); } + + /** + * transform `@Entry` storage params, e.g. `@Entry`({useSharedStorage: ..., storage: ...}) + */ + static transformStorageParams( + storage: arkts.ClassProperty | undefined, + useSharedStorage: arkts.ClassProperty | undefined, + definition: arkts.ClassDefinition + ): void { + if (!storage && !useSharedStorage) { + return; + } + const ctor: arkts.MethodDefinition | undefined = definition.body.find( + (member) => + arkts.isMethodDefinition(member) && + member.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR + ) as arkts.MethodDefinition | undefined; + if (!ctor) { + return; + } + let sharedArg = arkts.factory.createBooleanLiteral(false); + if (useSharedStorage && useSharedStorage.value && arkts.isBooleanLiteral(useSharedStorage.value)) { + sharedArg = useSharedStorage.value; + } + let storageArg = arkts.factory.createUndefinedLiteral(); + if (storage && storage.value && arkts.isStringLiteral(storage.value)) { + storageArg = arkts.factory.createCallExpression( + arkts.factory.createIdentifier(storage.value.str), + undefined, + undefined + ); + } + const superCall = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression(arkts.factory.createSuperExpression(), undefined, [ + sharedArg, + storageArg, + ]) + ); + if (ctor.scriptFunction.body && arkts.isBlockStatement(ctor.scriptFunction.body)) { + ctor.scriptFunction.setBody( + arkts.factory.updateBlock(ctor.scriptFunction.body, [...ctor.scriptFunction.body.statements, superCall]) + ); + } + } + + /** + * helper for callRegisterNamedRouter to generate NavInterface arg + */ + static navInterfaceArg( + projectConfig: ProjectConfig | undefined, + fileAbsName: string | undefined + ): arkts.TSAsExpression { + const projectRoot = projectConfig?.moduleRootPath + ? path.join(projectConfig.moduleRootPath, 'src', 'main', 'ets') + : ''; + const pageFullPath = getRelativePagePath(projectConfig?.projectPath ?? '', fileAbsName ?? ''); + const pagePath = getRelativePagePath(projectRoot, fileAbsName ?? ''); + return arkts.factory.createTSAsExpression( + arkts.factory.createObjectExpression( + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [ + factory.createNavProperty(NavigationNames.BUNDLE_NAME, projectConfig?.bundleName), + factory.createNavProperty(NavigationNames.MODULE_NAME, projectConfig?.moduleName), + factory.createNavProperty(NavigationNames.PAGE_PATH, pagePath), + factory.createNavProperty(NavigationNames.PAGE_FULL_PATH, pageFullPath), + factory.createNavProperty(NavigationNames.INTEGRATED_HSP, projectConfig?.integratedHsp?.toString()), + ], + false + ), + uiFactory.createTypeReferenceFromString(NavigationNames.NAVINTERFACE), + false + ); + } + + /** + * helper for navInterfaceArg to generate class properties, e.g. buneleName: '...' + */ + static createNavProperty(key: NavigationNames, value: string | undefined): arkts.Property { + return arkts.factory.createProperty( + arkts.factory.createIdentifier(key), + arkts.factory.createStringLiteral(value ?? '') + ); + } + + /** + * generate __EntryWrapper.RegisterNamedRouter(...) + */ + static callRegisterNamedRouter( + entryRouteName: string | undefined, + projectConfig: ProjectConfig | undefined, + fileAbsName: string | undefined + ): arkts.ExpressionStatement { + return arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(EntryWrapperNames.WRAPPER_CLASS_NAME), + arkts.factory.createIdentifier(EntryWrapperNames.REGISTER_NAMED_ROUTER), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createStringLiteral(entryRouteName ?? ''), + arkts.factory.createETSNewClassInstanceExpression( + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(EntryWrapperNames.WRAPPER_CLASS_NAME) + ) + ), + [] + ), + factory.navInterfaceArg(projectConfig, fileAbsName), + ] + ) + ); + } + + /** + * generate interface NavInterface in header arkui.UserView + */ + static createNavInterface(): arkts.TSInterfaceDeclaration { + return arkts.factory.createInterfaceDeclaration( + [], + arkts.factory.createIdentifier(NavigationNames.NAVINTERFACE), + undefined, + arkts.factory.createInterfaceBody([ + this.createClassProp(NavigationNames.BUNDLE_NAME), + this.createClassProp(NavigationNames.MODULE_NAME), + this.createClassProp(NavigationNames.PAGE_PATH), + this.createClassProp(NavigationNames.PAGE_FULL_PATH), + this.createClassProp(NavigationNames.INTEGRATED_HSP), + ]), + false, + false + ); + } + + /** + * helper for createNavInterface to generate class properties + */ + static createClassProp(propName: string): arkts.ClassProperty { + return arkts.factory.createClassProperty( + arkts.factory.createIdentifier(propName), + undefined, + uiFactory.createTypeReferenceFromString('string'), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + false + ); + } + + /** + * helper for generateRegisterNamedRouter to generate param decl, e.g: `routerName: string` + */ + static registerRouteParam(name: EntryWrapperNames, type: string): arkts.ETSParameterExpression { + return arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(name, uiFactory.createTypeReferenceFromString(type)), + undefined + ); + } + + /** + * generate generateRegisterNamedRouter method in header arkui.UserView + */ + static generateRegisterNamedRouter(): arkts.MethodDefinition { + const params = [ + factory.registerRouteParam(EntryWrapperNames.ROUTER_NAME, 'string'), + factory.registerRouteParam(EntryWrapperNames.INSTANCE, EntryWrapperNames.ENTRY_POINT_CLASS_NAME), + factory.registerRouteParam(EntryWrapperNames.PARAM, NavigationNames.NAVINTERFACE), + ]; + return uiFactory.createMethodDefinition({ + key: arkts.factory.createIdentifier(EntryWrapperNames.REGISTER_NAMED_ROUTER), + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + function: { + body: arkts.factory.createBlock([]), + params: params, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, + }, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, + }); + } } diff --git a/arkui-plugins/ui-plugins/entry-translators/utils.ts b/arkui-plugins/ui-plugins/entry-translators/utils.ts index 61a03783c..5783f94ca 100644 --- a/arkui-plugins/ui-plugins/entry-translators/utils.ts +++ b/arkui-plugins/ui-plugins/entry-translators/utils.ts @@ -14,18 +14,10 @@ */ import * as arkts from '@koalaui/libarkts'; +import * as path from 'path'; import { factory } from './factory'; import { isAnnotation } from '../../common/arkts-utils'; -import { CustomComponentNames } from '../utils'; - -export enum EntryWrapperNames { - ENTRY_FUNC = 'entry', - WRAPPER_CLASS_NAME = '__EntryWrapper', - ENTRY_STORAGE_ANNOTATION_KEY = 'storage', - ENTRY_STORAGE_LOCAL_STORAGE_PROPERTY_NAME = '_entry_local_storage_', - ENTRY_DEFAULT_IMPORT = 'arkui.UserView', - ENTRY_POINT_CLASS_NAME = 'EntryPoint', -} +import { StructDecoratorNames, EntryParamNames, EntryWrapperNames } from '../../common/predefines'; /** * @deprecated @@ -67,18 +59,37 @@ export function isEntryWrapperClass(node: arkts.AstNode): node is arkts.ClassDec } /** - * find `{storage: ""}` in `@Entry({storage: ""})` (i.e. annotation's properties). + * get annotation's properties in `@Entry()`: storage, useSharedStorage, routeName. * * @param node class definition node */ -export function findEntryWithStorageInClassAnnotations(node: arkts.ClassDefinition): arkts.ClassProperty | undefined { - const annotation = node.annotations.find((anno) => { - if (!isAnnotation(anno, CustomComponentNames.ENTRY_ANNOTATION_NAME)) return false; - const property = anno.properties?.at(0); - if (!property || !arkts.isClassProperty(property)) return false; - if (!property.key || !arkts.isIdentifier(property.key)) return false; - if (!property.value || !arkts.isStringLiteral(property.value)) return false; - return property.key.name === EntryWrapperNames.ENTRY_STORAGE_ANNOTATION_KEY; - }); - return annotation?.properties?.at(0) as arkts.ClassProperty | undefined; -} +export function getEntryParams(node: arkts.ClassDefinition): Record { + const annotation = node.annotations.find((anno) => isAnnotation(anno, StructDecoratorNames.ENTRY)); + const result = { + storage: undefined, + useSharedStorage: undefined, + routeName: undefined, + } as Record; + if (!annotation || !annotation.properties) { + return result; + } + for (const prop of annotation.properties) { + if (arkts.isClassProperty(prop) && prop.key && arkts.isIdentifier(prop.key)) { + const name = prop.key.name as EntryParamNames; + if (name in result) { + result[name] = prop; + } + } + } + return result; +} + +/** + * Computes and formats a relative path by removing `.ets` extension and normalizing path separators to `/`. + */ +export function getRelativePagePath(from: string, to: string): string { + return path + .relative(from, to) + .replace(/\\/g, '/') + .replace(/\.ets$/, ''); +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/index.ts b/arkui-plugins/ui-plugins/index.ts index 1397e89c2..1785e4854 100644 --- a/arkui-plugins/ui-plugins/index.ts +++ b/arkui-plugins/ui-plugins/index.ts @@ -15,7 +15,6 @@ import * as arkts from '@koalaui/libarkts'; import { ComponentTransformer } from './component-transformer'; -import { PreprocessorTransformer } from './preprocessor-transform'; import { CheckedTransformer } from './checked-transformer'; import { Plugins, PluginContext, ProjectConfig } from '../common/plugin-context'; import { ProgramVisitor } from '../common/program-visitor'; @@ -36,21 +35,25 @@ export function uiTransform(): Plugins { function parsedTransform(this: PluginContext): arkts.EtsScript | undefined { let script: arkts.EtsScript | undefined; console.log('[UI PLUGIN] AFTER PARSED ENTER'); - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + arkts.Performance.getInstance().memoryTrackerPrintCurrent('ArkTS:Parse'); + arkts.Performance.getInstance().memoryTrackerReset(); + arkts.Performance.getInstance().startMemRecord('Node:UIPlugin:AfterParse'); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; script = program.astNode; const cachePath: string | undefined = this.getProjectConfig()?.cachePath; + const canSkipPhases = program.canSkipPhases(); debugLog('[BEFORE PARSED SCRIPT] script: ', script.dumpSrc()); debugDump( script.dumpSrc(), getDumpFileName(0, 'SRC', 1, 'UI_AfterParse_Begin'), true, cachePath, - program.programFileNameWithExtension + program.fileNameWithExtension ); arkts.Performance.getInstance().createEvent('ui-parsed'); - program = parsedProgramVisit(program, this); + program = parsedProgramVisit(program, this, canSkipPhases); script = program.astNode; arkts.Performance.getInstance().stopEvent('ui-parsed', true); debugLog('[AFTER PARSED SCRIPT] script: ', script.dumpSrc()); @@ -59,9 +62,12 @@ function parsedTransform(this: PluginContext): arkts.EtsScript | undefined { getDumpFileName(0, 'SRC', 2, 'UI_AfterParse_End'), true, cachePath, - program.programFileNameWithExtension + program.fileNameWithExtension ); this.setArkTSAst(script); + arkts.Performance.getInstance().memoryTrackerGetDelta('UIPlugin:AfterParse'); + arkts.Performance.getInstance().memoryTrackerReset(); + arkts.Performance.getInstance().stopMemRecord('Node:UIPlugin:AfterParse'); console.log('[UI PLUGIN] AFTER PARSED EXIT'); return script; } @@ -78,12 +84,13 @@ function parsedProgramVisit( debugLog('[SKIP PHASE] phase: ui-parsed, moduleName: ', program.moduleName); } else { debugLog('[CANT SKIP PHASE] phase: ui-parsed, moduleName: ', program.moduleName); - const componentTransformer = new ComponentTransformer(); - const preprocessorTransformer = new PreprocessorTransformer(); + const componentTransformer = new ComponentTransformer({ + projectConfig: context.getProjectConfig(), + }); const programVisitor = new ProgramVisitor({ pluginName: uiTransform.name, state: arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, - visitors: [componentTransformer, preprocessorTransformer], + visitors: [componentTransformer], skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, pluginContext: context, }); @@ -95,21 +102,26 @@ function parsedProgramVisit( function checkedTransform(this: PluginContext): arkts.EtsScript | undefined { let script: arkts.EtsScript | undefined; console.log('[UI PLUGIN] AFTER CHECKED ENTER'); - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + arkts.Performance.getInstance().memoryTrackerPrintCurrent('ArkTS:Check'); + arkts.Performance.getInstance().memoryTrackerGetDelta('ArkTS:Check'); + arkts.Performance.getInstance().memoryTrackerReset(); + arkts.Performance.getInstance().startMemRecord('Node:UIPlugin:UI-AfterCheck'); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; script = program.astNode; const cachePath: string | undefined = this.getProjectConfig()?.cachePath; + const canSkipPhases = program.canSkipPhases(); debugLog('[BEFORE STRUCT SCRIPT] script: ', script.dumpSrc()); debugDump( script.dumpSrc(), getDumpFileName(0, 'SRC', 3, 'UI_AfterCheck_Begin'), true, cachePath, - program.programFileNameWithExtension + program.fileNameWithExtension ); arkts.Performance.getInstance().createEvent('ui-checked'); - program = checkedProgramVisit(program, this); + program = checkedProgramVisit(program, this, canSkipPhases); script = program.astNode; arkts.Performance.getInstance().stopEvent('ui-checked', true); debugLog('[AFTER STRUCT SCRIPT] script: ', script.dumpSrc()); @@ -118,14 +130,11 @@ function checkedTransform(this: PluginContext): arkts.EtsScript | undefined { getDumpFileName(0, 'SRC', 4, 'UI_AfterCheck_End'), true, cachePath, - program.programFileNameWithExtension + program.fileNameWithExtension ); - arkts.GlobalInfo.getInfoInstance().reset(); - arkts.Performance.getInstance().createEvent('ui-recheck'); - arkts.recheckSubtree(script); - arkts.Performance.getInstance().stopEvent('ui-recheck', true); - arkts.Performance.getInstance().clearAllEvents(); this.setArkTSAst(script); + arkts.Performance.getInstance().memoryTrackerGetDelta('UIPlugin:UI-AfterCheck'); + arkts.Performance.getInstance().stopMemRecord('Node:UIPlugin:UI-AfterCheck'); console.log('[UI PLUGIN] AFTER CHECKED EXIT'); return script; } @@ -143,6 +152,9 @@ function checkedProgramVisit( } else { debugLog('[CANT SKIP PHASE] phase: ui-checked, moduleName: ', program.moduleName); const projectConfig: ProjectConfig | undefined = context.getProjectConfig(); + if (projectConfig && !projectConfig.appResource) { + projectConfig.ignoreError = true; + } const checkedTransformer = new CheckedTransformer(projectConfig); const programVisitor = new ProgramVisitor({ pluginName: uiTransform.name, @@ -154,4 +166,4 @@ function checkedProgramVisit( program = programVisitor.programVisitor(program); } return program; -} \ No newline at end of file +} diff --git a/arkui-plugins/ui-plugins/initstatevar.ts b/arkui-plugins/ui-plugins/initstatevar.ts deleted file mode 100644 index 3264db1a2..000000000 --- a/arkui-plugins/ui-plugins/initstatevar.ts +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (c) 2022-2025 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - - -import * as arkts from '@koalaui/libarkts'; -import { InteroperAbilityNames } from '../common/predefines'; -import { annotation, backingField, isAnnotation } from '../common/arkts-utils'; -import { getPropertyESValue, getWrapValue, setPropertyESValue } from './interop'; - - -export function processNormal(keyName: string, value: arkts.AstNode): arkts.Statement[] { - const result: arkts.Statement[] = []; - const setProperty = setPropertyESValue( - InteroperAbilityNames.PARAM, - keyName, - getWrapValue(value) - ); - result.push(setProperty); - return result; -} - -export function createVariableLet(varName: string, expression: arkts.AstNode): arkts.VariableDeclaration { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(varName), - expression - )] - ); -} -export function setValueCallback(name: string, type: arkts.TypeNode, block: arkts.BlockStatement): arkts.AstNode { - return createVariableLet(name, - arkts.factory.createArrowFunction( - arkts.factory.createScriptFunction( - block, - arkts.factory.createFunctionSignature( - undefined, - [ - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier('value', type), - undefined, - ), - ], - undefined, - false - ), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - ) - ) - ); -} - -function createProxyBlock(varName: string): arkts.BlockStatement { - return arkts.factory.createBlock( - [ - arkts.factory.createExpressionStatement( - arkts.factory.createAssignmentExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(varName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - arkts.factory.createIdentifier('value') - ) - ) - ] - ); -} - -export function setCallbackForProxy(varName: string, type: arkts.TypeNode): arkts.Statement[] { - const createCallback = setValueCallback(addStatePrefix(varName, 'SetSource'), type, createProxyBlock(varName)); - const createProxyState = createVariableLet(addStatePrefix(varName, 'ProxyState'), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier('createState'), - arkts.factory.createIdentifier('invoke'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [ - getWrapValue( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(varName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ) - ), - getWrapValue(arkts.factory.createIdentifier(addStatePrefix(varName, 'SetSource'))) - ] - ) - ); - const setProxy = arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createTSNonNullExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(backingField(varName)), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ) - ), - arkts.factory.createIdentifier('setProxy'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [arkts.factory.createIdentifier(addStatePrefix(varName, 'ProxyState'))], - ) - ); - return [createCallback, createProxyState, setProxy]; -} - -function createSourceBlock(varName: string): arkts.BlockStatement { - return arkts.factory.createBlock( - [ - arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(addStatePrefix(varName, 'ProxyState')), - arkts.factory.createIdentifier('invokeMethod'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [ - arkts.factory.createStringLiteral('set'), - getWrapValue( - arkts.factory.createIdentifier('value') - ) - ] - ) - ) - ] - ); -} - -function createNotifyBlock(varName: string): arkts.BlockStatement { - return arkts.factory.createBlock( - [ - arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(addStatePrefix(varName, 'ProxyState')), - arkts.factory.createIdentifier('invokeMethod'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [ - arkts.factory.createStringLiteral('notifyPropertyHasChangedPU') - ] - ) - ) - ] - ); -} - -function setNotifyForSource(varName: string): arkts.Statement[] { - const block = createNotifyBlock(varName); - const createCallback = createVariableLet(addStatePrefix(varName, 'NotifyCallback'), - arkts.factory.createArrowFunction( - arkts.factory.createScriptFunction( - block, - arkts.factory.createFunctionSignature( - undefined, - [ - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier('propertyName', - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('string') - ) - ) - ), - undefined, - ), - ], - undefined, - false - ), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - ) - ) - ); - const setCallback = arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createTSNonNullExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(backingField(varName)), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ) - ), - arkts.factory.createIdentifier('setNotifyCallback'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [arkts.factory.createIdentifier(addStatePrefix(varName, 'NotifyCallback'))], - ) - ); - return [createCallback, setCallback]; -} - -export function setCallbackForSource(varName: string, type: arkts.TypeNode): arkts.Statement[] { - const createCallback = setValueCallback(addStatePrefix(varName, 'SetProxy'), type, createSourceBlock(varName)); - const setFunc = arkts.factory.createExpressionStatement( - arkts.factory.createAssignmentExpression( - arkts.factory.createMemberExpression( - arkts.factory.createTSNonNullExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(backingField(varName)), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ) - ), - arkts.factory.createIdentifier('setProxyValue'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - arkts.factory.createIdentifier(addStatePrefix(varName, 'SetProxy')) - ) - ); - const setNotify = setNotifyForSource(varName); - return [createCallback, setFunc, ...setNotify]; -} - -export function processLink(keyName: string, value: arkts.AstNode, type: arkts.TypeNode, proxySet: Set): arkts.Statement[] { - const varName = ((value as arkts.MemberExpression).property as arkts.Identifier).name; - const result: arkts.Statement[] = []; - if (!proxySet.has(varName)) { - proxySet.add(varName); - const setProxy = setCallbackForProxy(varName, type); - result.push(...setProxy); - const setSource = setCallbackForSource(varName, type); - result.push(...setSource); - } - const setParam = setPropertyESValue( - 'param', - keyName, - arkts.factory.createIdentifier(addStatePrefix(varName, 'ProxyState')) - ); - result.push(setParam); - return result; -} - -export function hasLink(decorators: string[]): boolean { - return decorators.some(decorator => decorator === 'Link'); -} - -function addStatePrefix(stateVarName: string, name: string): string { - return `${stateVarName}_${name}`; -} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop/builder-interop.ts b/arkui-plugins/ui-plugins/interop/builder-interop.ts new file mode 100644 index 000000000..f2073b52d --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/builder-interop.ts @@ -0,0 +1,538 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { ESValueMethodNames, BuilderMethodNames, InteroperAbilityNames, BuilderParams } from './predefines'; +import { + createELMTID, + createEmptyESValue, + createGlobal, + createInitReturn, + getPropertyESValue, + getWrapValue, + setPropertyESValue +} from './utils'; + +interface builderParam { + args: arkts.AstNode[], + paramsInfo: arkts.Statement[] +} + +function invokeFunctionWithParam(functionName: string, result: string, className: string, args: arkts.AstNode[]): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(result), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(functionName), + arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + getWrapValue(arkts.factory.createIdentifier(className)), + arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID), + ...args + ] + ) + )] + ); +} + +function invokeRunPendingJobs(): arkts.Statement { + return arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderMethodNames.RUNPENDINGJOBS), + arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + ); +} + +function invokeComponent(): arkts.Statement[] { + const viewPU = getPropertyESValue('viewPUCreate', InteroperAbilityNames.GLOBAL, 'viewPUCreate'); + const create = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('viewPUCreate'), + arkts.factory.createIdentifier('invoke'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT) + ] + ) + ); + return [viewPU, create]; +} + +function createBuilderInitializer(className: string, functionName: string, param: builderParam): arkts.ArrowFunctionExpression { + const block = arkts.factory.createBlock( + [ + createGlobal(), + ...createELMTID(), + getPropertyESValue(BuilderMethodNames.CREATECOMPATIBLENODE, InteroperAbilityNames.GLOBAL, functionName), + ...param.paramsInfo, + invokeFunctionWithParam(BuilderMethodNames.CREATECOMPATIBLENODE, InteroperAbilityNames.COMPONENT, className, param.args), + ...invokeComponent(), + createInitReturn(className) + ] + ); + return arkts.factory.createArrowFunction( + arkts.factory.createScriptFunction( + block, + arkts.factory.createFunctionSignature( + undefined, + [], + undefined, + false, + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + ) + ); +} + +/** + * getInstanceParam + * @returns let instanceParam = (instance.getProperty("arg1") as ESValue); + */ +function getInstanceParam(): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(BuilderParams.INSTANCEPARAM), + arkts.factory.createTSAsExpression( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(InteroperAbilityNames.INSTANCE), + arkts.factory.createIdentifier(ESValueMethodNames.GETPROPERTY), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createStringLiteral('arg1') + ] + ), + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE) + ) + ), + false + ) + ) + ] + ); +} + +/** + * instanceParamTypeOf + * @returns + * if (((instanceParam.typeOf()) != ("object"))) { + return; + } + */ +function instanceParamTypeOf(): arkts.Statement { + return arkts.factory.createIfStatement( + arkts.factory.createBinaryExpression( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.INSTANCEPARAM), + arkts.factory.createIdentifier('typeOf'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ), arkts.factory.createStringLiteral('object'), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NOT_EQUAL + ), + arkts.factory.createBlock([ + arkts.factory.createReturnStatement() + ]) + ); +} + +/** + * getParamWrapped + * @param argument param + * @returns let param_wrapped = ESValue.wrap(param); + */ +function getParamWrapped(argument: arkts.Identifier): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('ESValue'), + arkts.factory.createIdentifier('wrap'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + argument + ] + ) + ) + ] + ); +} + +/** + * getItem + * @returns let param_wrapped_it = param_wrapped.keys(); + */ +function getItem(): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED_IT), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED), + arkts.factory.createIdentifier('keys'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + ) + ] + ); +} + +function getResult(): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_CONST, + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED_KEY), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED_IT), + arkts.factory.createIdentifier('next'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + ) + ] + ); +} + +function getResultDone(): arkts.Statement { + return arkts.factory.createIfStatement( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED_KEY), + arkts.factory.createIdentifier('done'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + arkts.factory.createBlock([ + arkts.BreakStatement.createBreakStatement() + ]) + ); +} + +function getParamWrappedProperty(): arkts.Statement { + return arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED), + arkts.factory.createIdentifier(ESValueMethodNames.GETPROPERTY), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createTSAsExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED_KEY), + arkts.factory.createIdentifier('value'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE) + ) + ), + false + ), + ] + ); +} + +function setInstanceParam(): arkts.Statement { + return arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.INSTANCEPARAM), + arkts.factory.createIdentifier(ESValueMethodNames.SETPROPERTY), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createTSAsExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED_KEY), + arkts.factory.createIdentifier('value'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE) + ) + ), + false + ), + getParamWrappedProperty() + ] + ) + ); +} + +/** + * getWhile + * @returns + * while (true) { + let param_wrapped_key = param_wrapped_it.next(); + if (param_wrapped_key.done) { + break; + } + instanceParam.setProperty((param_wrapped_key.value as ESValue), + param_wrapped.getProperty((param_wrapped_key.value as ESValue))); + } + */ +function getWhile(): arkts.Statement { + return arkts.WhileStatement.createWhileStatement( + arkts.factory.createBooleanLiteral(true), + arkts.factory.createBlock( + [ + getResult(), + getResultDone(), + setInstanceParam() + ] + ) + ); +} + +function getUpdateArgs(node: arkts.CallExpression): arkts.Statement[] { + if (node.arguments.length !== 1) { + return []; + } + let body: arkts.Statement[] = []; + let argument = node.arguments[0]; + if (arkts.isObjectExpression(argument)) { + body?.push(getPropertyESValue('param', InteroperAbilityNames.INSTANCE, 'arg1')); + for (const property of argument.properties) { + if (!(property instanceof arkts.Property)) { + continue; + } + const key = property.key; + const value = property.value; + if (!(key instanceof arkts.Identifier) || value === undefined) { + throw Error('Error arguments in Legacy Builder Function'); + } + body?.push(setPropertyESValue('param', key.name, getWrapValue(value))); + } + const endBody = + [ + createGlobal(), + getPropertyESValue(BuilderMethodNames.RUNPENDINGJOBS, + InteroperAbilityNames.GLOBAL, + BuilderMethodNames.RUNPENDINGJOBS), + invokeRunPendingJobs() + ]; + body?.push(...endBody); + } else if (arkts.isIdentifier(argument)) { + const functionBody = + [ + getInstanceParam(), + instanceParamTypeOf(), + getParamWrapped(argument), + getItem(), + getWhile(), + createGlobal(), + getPropertyESValue(BuilderMethodNames.RUNPENDINGJOBS, + InteroperAbilityNames.GLOBAL, + BuilderMethodNames.RUNPENDINGJOBS), + invokeRunPendingJobs() + ]; + body?.push(...functionBody); + } + + return body; +} + +function createBuilderUpdate(node: arkts.CallExpression): arkts.ArrowFunctionExpression { + return arkts.factory.createArrowFunction( + arkts.factory.createScriptFunction( + arkts.factory.createBlock( + [ + ...getUpdateArgs(node) + ] + ), + arkts.factory.createFunctionSignature( + undefined, + [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(InteroperAbilityNames.INSTANCE, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE) + ) + ) + ), + undefined, + ), + ], + undefined, + false, + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + ) + ); +} + +function getInitArgs(node: arkts.CallExpression): builderParam { + const args: arkts.AstNode[] = []; + let ObjectExpressionNum: number = 0; + const body: arkts.Statement[] = []; + node.arguments.forEach((argument) => { + if (arkts.isObjectExpression(argument)) { + processArgument(argument, ObjectExpressionNum, body, args); + ObjectExpressionNum++; + } else { + args.push(getWrapValue(argument)); + } + }); + return { args: args, paramsInfo: body }; +} + +function processArgument(argument: arkts.ObjectExpression, ObjectExpressionNum:number, + body: arkts.Statement[], args: arkts.AstNode[]): void { + const paramName: string = 'paramObject' + ObjectExpressionNum; + body.push(createEmptyESValue(paramName)); + for (const property of argument.properties) { + if (!(property instanceof arkts.Property)) { + continue; + } + const key = property.key; + const value = property.value; + if (!(key instanceof arkts.Identifier) || value === undefined) { + throw Error('Error arguments in Legacy Builder Function'); + } + body.push(setPropertyESValue(paramName, key.name, getWrapValue(value))); + } + args.push(arkts.factory.createIdentifier(paramName)); +} + +/** + * + * @param node node + * @param moduleName moduleName + * @returns After Checked, transform builder/WrappedBuilder -> compatibleComponent + */ +export function generateBuilderCompatible(node: arkts.CallExpression, moduleName: string): arkts.CallExpression { + let functionName = getFunctionName(node); + let param: builderParam = getInitArgs(node); + const initializer = createBuilderInitializer(moduleName, functionName, param); + const updater: arkts.ArrowFunctionExpression = createBuilderUpdate(node); + const result = arkts.factory.updateCallExpression( + node, + arkts.factory.createIdentifier(InteroperAbilityNames.ARKUICOMPATIBLE), + undefined, + [ + initializer, + updater, + ] + ); + arkts.NodeCache.getInstance().collect(result); + return result; +} + +function getFunctionName(node: arkts.CallExpression): string { + switch (node.arguments.length) { + case 0: + return 'createCompatibleNodeWithFuncVoid'; + case 1: + return 'createCompatibleNodeWithFunc'; + case 2: + return 'createCompatibleNodeWithFunc2'; + case 3: + return 'createCompatibleNodeWithFunc3'; + case 4: + return 'createCompatibleNodeWithFunc4'; + case 5: + return 'createCompatibleNodeWithFunc5'; + case 6: + return 'createCompatibleNodeWithFunc6'; + case 7: + return 'createCompatibleNodeWithFunc7'; + case 8: + return 'createCompatibleNodeWithFunc8'; + case 9: + return 'createCompatibleNodeWithFunc9'; + case 10: + return 'createCompatibleNodeWithFunc10'; + default: + throw Error('Error arguments in Legacy Builder Function'); + } +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop/initstatevar.ts b/arkui-plugins/ui-plugins/interop/initstatevar.ts new file mode 100644 index 000000000..14225718e --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/initstatevar.ts @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + + +import * as arkts from '@koalaui/libarkts'; +import { BuilderMethodNames, InteroperAbilityNames } from './predefines'; +import { annotation, backingField, isAnnotation } from '../../common/arkts-utils'; +import { stateProxy, getWrapValue, setPropertyESValue, createEmptyESValue } from './utils'; +import { hasDecorator } from '../property-translators/utils'; +import { DecoratorNames } from '../../common/predefines'; + + +export function initialArgs(args: arkts.ObjectExpression, varMap: Map, updateProp: arkts.Property[]): arkts.Statement[] { + const result: arkts.Statement[] = []; + const proxySet = new Set(); + + + for (const property of args.properties) { + if (!(property instanceof arkts.Property)) { + continue; + } + const key = property.key; + const value = property.value!; + if (!(key instanceof arkts.Identifier)) { + throw Error('Error arguments in Legacy Component'); + } + const keyName = key.name; + const keyProperty = varMap.get(keyName); + if (keyProperty === undefined) { + throw Error('Error arguments in Legacy Component'); + } + const keyType = keyProperty.typeAnnotation!; + const annotations = keyProperty.annotations; + if (annotations.length === 0) { + const valueProperty = arkts.getDecl(value); + if (valueProperty instanceof arkts.ClassProperty && (hasDecorator(valueProperty, DecoratorNames.PROVIDE) || + hasDecorator(valueProperty, DecoratorNames.CONSUME))) { + throw Error('Cannot assign @Provide or @Consume decorated data to regular property.'); + } + const initParam = processNormal(keyName, value); + result.push(...initParam); + } else if (hasDecorator(keyProperty, DecoratorNames.LINK)) { + const initParam = processLink(keyName, value, keyType, proxySet); + result.push(...initParam); + } else if (hasDecorator(keyProperty, DecoratorNames.CONSUME)) { + throw Error('The @Consume property cannot be assigned.'); + } else if (hasDecorator(keyProperty, DecoratorNames.PROP) || hasDecorator(keyProperty, DecoratorNames.OBJECT_LINK)) { + updateProp.push(property); + const initParam = processNormal(keyName, value); + result.push(...initParam); + } else if (hasDecorator(keyProperty, DecoratorNames.STATE) || hasDecorator(keyProperty, DecoratorNames.PROVIDE)) { + const initParam = processNormal(keyName, value); + result.push(...initParam); + } else if (hasDecorator(keyProperty, DecoratorNames.CONSUME)) { + throw Error('The @Consume property cannot be assigned.'); + } else if (hasDecorator(keyProperty, DecoratorNames.BUILDER_PARAM)) { + const initParam = processBuilderParam(keyName, value); + result.push(...initParam); + } else { + const initParam = processNormal(keyName, value); + result.push(...initParam); + } + } + return result; +} + +export function createVariableLet(varName: string, expression: arkts.AstNode): arkts.VariableDeclaration { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(varName), + expression + )] + ); +} + +function createBackingFieldExpression(varName: string): arkts.TSNonNullExpression { + return arkts.factory.createTSNonNullExpression( + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(backingField(varName)), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ) + ); +} + + +function getStateProxy(proxyName: string, stateVar: () => arkts.Expression): arkts.Statement { + return createVariableLet( + proxyName, + arkts.factory.createCallExpression( + arkts.factory.createIdentifier(InteroperAbilityNames.GETCOMPATIBLESTATE), + undefined, + [ + stateVar() + ] + ) + ); +} + +/** + * Processes a nested object literal and generates code to instantiate and populate it using ESValue methods. + * + * Converts a nested object structure into a sequence of statements that: + * 1. Instantiate empty objects via `ESValue.instantiateEmptyObject()` + * 2. Sets properties on these objects using `setProperty()` + * 3. Nests objects by assigning child objects as properties of parent objects + * + * @param target - A nested object literal (e.g., { b: { c: { d: '1' }, cc: { d: '1' } } }) + * @returns Generated code statements that reconstruct the input object using ESValue APIs + * @example + * Input: { b: { c: { d: '1' }, cc: { d: '1' } } } + * Output: + * let param0 = ESValue.instantiateEmptyObject(); + * let param00 = ESValue.instantiateEmptyObject(); + * param00.setProperty("d", ESValue.wrap("1")); + * param0.setProperty("c", param00); + * let param01 = ESValue.instantiateEmptyObject(); + * param01.setProperty("d", ESValue.wrap("1")); + * param0.setProperty("cc", param01); + * param.setProperty("b", param0); + */ +function processObjectLiteral(target: arkts.ObjectExpression, curParam: string, result: arkts.Statement[], keyName: string): void { + if (curParam !== InteroperAbilityNames.PARAM) { + const createParam = createEmptyESValue(curParam); + result.push(createParam); + } + target.properties.forEach((property: { key: arkts.Expression; value: arkts.Expression; }) => { + const paramName = curParam + keyName; + const key = property.key; + const value = property.value; + if (arkts.isObjectExpression(value)) { + processObjectLiteral(value, paramName, result, keyName); + const setProperty = setPropertyESValue( + curParam, + key.name, + arkts.factory.createIdentifier(paramName) + ); + result.push(setProperty); + } else { + const setProperty = setPropertyESValue( + curParam, + key.name, + getWrapValue(value) + ); + result.push(setProperty); + } + }); +} + + +/** + * + * @param keyName - The name of the state variable (e.g., state) + * @returns generate code to process @Link data interoperability + * @example + * Input: {link: this.state} + * Output: + * let __Proxy_state = getCompatibleState(this.state); + * param.setProperty("link", __Proxy_state); + */ +export function processLink(keyName: string, value: arkts.Expression, type: arkts.TypeNode, proxySet: Set): arkts.Statement[] { + const valueDecl = arkts.getDecl(value); + const result: arkts.Statement[] = []; + if (valueDecl instanceof arkts.ClassProperty) { + let varName = ((value as arkts.MemberExpression).property as arkts.Identifier).name; + let proxyName = stateProxy(varName); + let stateVar = (): arkts.TSNonNullExpression => createBackingFieldExpression(varName); + if (!proxySet.has(varName)) { + proxySet.add(varName); + const getProxy = getStateProxy(proxyName, stateVar); + result.push(getProxy); + } + const setParam = setPropertyESValue( + 'param', + keyName, + arkts.factory.createIdentifier(proxyName) + ); + result.push(setParam); + } else { + throw Error('unsupported data for Link'); + } + return result; +} + +/** + * + * @param keyName - The name of the state variable (e.g., state) + * @returns generate code to process regular data interoperability + */ +export function processNormal(keyName: string, value: arkts.AstNode): arkts.Statement[] { + const result: arkts.Statement[] = []; + if (arkts.isObjectExpression(value)) { + processObjectLiteral(value, InteroperAbilityNames.PARAM, result, keyName); + } else { + const setProperty = setPropertyESValue( + InteroperAbilityNames.PARAM, + keyName, + getWrapValue(value) + ); + result.push(setProperty); + } + return result; +} + +/** + * + * @param keyName + * @param value + * @returns generate code to process @BuilderParam interoperability + * @example + * Input: {builderParam: this.builder} + * Output: param.setProperty("builderParam", transferCompatibleBuilder(this.builder)); + */ +export function processBuilderParam(keyName: string, value: arkts.AstNode): arkts.Statement[] { + const result: arkts.Statement[] = []; + const newValue = arkts.factory.createCallExpression( + arkts.factory.createIdentifier(BuilderMethodNames.TRANSFERCOMPATIBLEBUILDER), + undefined, + [ + value + ] + ); + const setProperty = setPropertyESValue( + InteroperAbilityNames.PARAM, + keyName, + newValue + ); + result.push(setProperty); + return result; +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop.ts b/arkui-plugins/ui-plugins/interop/interop.ts similarity index 36% rename from arkui-plugins/ui-plugins/interop.ts rename to arkui-plugins/ui-plugins/interop/interop.ts index d6c43d1c7..8da4bc6d5 100644 --- a/arkui-plugins/ui-plugins/interop.ts +++ b/arkui-plugins/ui-plugins/interop/interop.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2025 Huawei Device Co., Ltd. + * Copyright (c) 2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,155 +16,27 @@ import * as arkts from '@koalaui/libarkts'; -import { InteroperAbilityNames } from '../common/predefines'; -import { getCustomComponentOptionsName } from './utils'; -import { InteropContext } from './component-transformer'; -import { annotation, backingField, isAnnotation } from '../common/arkts-utils'; -import { hasLink, processLink, processNormal } from './initstatevar'; +import { BuilderMethodNames, ESValueMethodNames, InteroperAbilityNames } from './predefines'; +import { getCustomComponentOptionsName } from '../utils'; +import { InteropContext } from '../component-transformer'; +import { createVariableLet, initialArgs} from './initstatevar'; +import { + getPropertyESValue, + getWrapValue, + setPropertyESValue, + createEmptyESValue, + createGlobal, + createELMTID, + createInitReturn +} from './utils'; +import { ImportCollector } from '../../common/import-collector'; +import { factory as uiFactory } from '../ui-factory'; +import { DecoratorNames } from '../../common/predefines'; +import { hasDecorator } from '../property-translators/utils'; -interface propertyInfo { - decorators: string[], - type: arkts.TypeNode, -} - -export function createEmptyESValue(name: string): arkts.VariableDeclaration { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(name), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE), - arkts.factory.createIdentifier(InteroperAbilityNames.INITEMPTYOBJECT), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - undefined - ) - ) - ] - ); -} - -export function getWrapValue(value: arkts.AstNode): arkts.AstNode { - return arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE), - arkts.factory.createIdentifier(InteroperAbilityNames.WRAP), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [value] - ); -} - -export function setPropertyESValue(name: string, key: string, wrapValue: arkts.AstNode): arkts.ExpressionStatement { - return arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(name), - arkts.factory.createIdentifier(InteroperAbilityNames.SETPROPERTY), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [ - arkts.factory.createStringLiteral(key), - wrapValue - ] - ) - ); -} - -export function getPropertyESValue(result: string, object: string, key: string): arkts.VariableDeclaration { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(result), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(object), - arkts.factory.createIdentifier(InteroperAbilityNames.GETPROPERTY), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [arkts.factory.create1StringLiteral(key)] - ) - ) - ] - ); -} - -function initialArgs(args: arkts.ObjectExpression, varMap: Map): arkts.Statement[] { - const result: arkts.Statement[] = [ - createEmptyESValue(InteroperAbilityNames.PARAM), - getPropertyESValue('createState', 'global', 'createStateVariable') - ]; - - const proxySet = new Set(); - - for (const property of args.properties) { - if (!(property instanceof arkts.Property)) { - continue; - } - const key = property.key; - const value = property.value; - if (!(key instanceof arkts.Identifier)) { - throw Error('Error arguments in Legacy Component'); - } - const name = key.name; - const decorators = varMap.get(name)?.decorators; - const type = varMap.get(name)?.type!; - if (decorators !== undefined && hasLink(decorators)) { - const initParam = processLink(key.name, value!, type, proxySet); - result.push(...initParam); - } else { - const initParam = processNormal(key.name, value!); - result.push(...initParam); - } - } - return result; -} - -function instantiateComponent(params: arkts.AstNode[]): arkts.VariableDeclaration { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(InteroperAbilityNames.STRUCTOBJECT), - arkts.factory.createIdentifier(InteroperAbilityNames.INSTANTIATE), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - params - ) - ) - ] - ); -} function paramsLambdaDeclaration(name: string, args?: arkts.ObjectExpression): arkts.Statement[] { - const result = []; + const result: arkts.Statement[] = []; result.push( arkts.factory.createVariableDeclaration( arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, @@ -204,25 +76,6 @@ function paramsLambdaDeclaration(name: string, args?: arkts.ObjectExpression): a return result; } -function createInitReturn(componentName: string): arkts.ReturnStatement { - return arkts.factory.createReturnStatement( - arkts.ObjectExpression.createObjectExpression( - arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, - [ - arkts.Property.createProperty( - arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT), - arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT) - ), - arkts.Property.createProperty( - arkts.factory.createIdentifier('name'), - arkts.factory.createStringLiteral(componentName) - ) - ], - false - ), - ); -} - function createExtraInfo(properties: string[], value: string[]): arkts.Statement[] { const body: arkts.AstNode[] = []; body.push(createEmptyESValue(InteroperAbilityNames.EXTRAINFO)); @@ -237,183 +90,74 @@ function createExtraInfo(properties: string[], value: string[]): arkts.Statement return body; } -function createESParent(): arkts.Statement { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier('esparent'), - getWrapValue(arkts.factory.createIdentifier(InteroperAbilityNames.PARENT)) - ) - ] - ); -} - -function createESUndefined(): arkts.Statement { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier('esundefined'), - getWrapValue(arkts.factory.createUndefinedLiteral()) - ) - ] - ); -} - -function createESBlank(): arkts.Statement[] { - const body: arkts.Statement[] = []; - const blank = arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier('blank'), - arkts.factory.createArrowFunction( - arkts.factory.createScriptFunction( - arkts.factory.createBlock([]), - arkts.factory.createFunctionSignature( - undefined, - [], - undefined, - false - ), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - ) - ) - ) - ] - ); - body.push(blank); - const asExpression = arkts.factory.createTSAsExpression( - arkts.factory.createIdentifier('blank'), +function generateTSASExpression(expression: arkts.AstNode): arkts.Expression { + return arkts.factory.createTSAsExpression( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + expression, + arkts.factory.createIdentifier('unwrap'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ), arkts.factory.createTypeReference( arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('object') + arkts.factory.createIdentifier('Object') ) ), false ); - const esblank = arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier('esblank'), - getWrapValue(asExpression) - ) - ] - ); - body.push(esblank); - return body; -} - -function createGlobal(): arkts.Statement { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier('global'), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE), - arkts.factory.createIdentifier('getGlobal'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - undefined - ) - )] - ); -} - -function createELMTID(): arkts.Statement[] { - const body: arkts.Statement[] = []; - const viewStackProcessor = getPropertyESValue('viewStackProcessor', 'global', 'ViewStackProcessor'); - body.push(viewStackProcessor); - const createId = getPropertyESValue('createId', 'viewStackProcessor', 'AllocateNewElmetIdForNextComponent'); - body.push(createId); - const elmtId = arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier('createId'), - arkts.factory.createIdentifier('invoke'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - undefined - ) - )] - ); - body.push(elmtId); - return body; } -function createComponent(moduleName: string, className: string): arkts.Statement[] { - const body: arkts.Statement[] = []; - const module = arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(moduleName), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE), - arkts.factory.createIdentifier(InteroperAbilityNames.LOAD), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, +function newComponent(className: string): arkts.Statement { + return createVariableLet( + InteroperAbilityNames.COMPONENT, + getWrapValue( + arkts.factory.createETSNewClassInstanceExpression( + arkts.factory.createIdentifier(className), + [ + arkts.factory.createUndefinedLiteral(), + generateTSASExpression(arkts.factory.createIdentifier(InteroperAbilityNames.PARAM)), + arkts.factory.createUndefinedLiteral(), + generateTSASExpression(arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID)), + arkts.factory.createTSAsExpression( + arkts.factory.createArrowFunction( + arkts.factory.createScriptFunction( + arkts.factory.createBlock([]), + arkts.factory.createFunctionSignature( + undefined, + [], + undefined, + false + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + ) + ), + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('Object') + ) + ), false ), - undefined, - [arkts.factory.create1StringLiteral(InteroperAbilityNames.OHMURL)] - ) + generateTSASExpression(arkts.factory.createIdentifier(InteroperAbilityNames.EXTRAINFO)) + ] ) - ] - ); - body.push(module); - const structObject = getPropertyESValue('structObject', moduleName, className); - body.push(structObject); - const component = instantiateComponent( - [ - arkts.factory.createIdentifier('esundefined'), - arkts.factory.createIdentifier(InteroperAbilityNames.PARAM), - arkts.factory.createIdentifier('esundefined'), - arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID), - arkts.factory.createIdentifier('esblank'), - arkts.factory.createIdentifier(InteroperAbilityNames.EXTRAINFO) - ] + ) ); - body.push(component); - return body; } -function invokeViewPUCreate(): arkts.Statement[] { - const body: arkts.Statement[] = []; - const createMethod = getPropertyESValue('create', 'structObject', 'create'); - body.push(createMethod); - const viewPUCreate = arkts.factory.createExpressionStatement( +function createComponent(className: string): arkts.Statement[] { + const component = newComponent(className); + const ViewPU = getPropertyESValue('viewPUCreate', InteroperAbilityNames.GLOBAL, 'viewPUCreate'); + const create = arkts.factory.createExpressionStatement( arkts.factory.createCallExpression( arkts.factory.createMemberExpression( - arkts.factory.createIdentifier('create'), + arkts.factory.createIdentifier('viewPUCreate'), arkts.factory.createIdentifier('invoke'), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, @@ -421,47 +165,43 @@ function invokeViewPUCreate(): arkts.Statement[] { ), undefined, [ - arkts.factory.createIdentifier('component') + arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT) ] ) ); - body.push(viewPUCreate); - return body; + return [component, ViewPU, create]; } -function createWrapperBlock(context: InteropContext, varMap: Map): arkts.BlockStatement { - const className = context.className; - const path = context.path; - const args = context.arguments; - const index = path.indexOf('/'); + +function createWrapperBlock(context: InteropContext, varMap: Map, + updateProp: arkts.Property[]): arkts.BlockStatement { + const className: string = context.className; + const path: string = context.path; + const args: arkts.ObjectExpression | undefined = context.arguments; + const index: number = path.indexOf('/'); if (index === -1) { throw new Error('Error path of Legacy Component.'); } - const moduleName = path.substring(0, index); - const initialArgsStatement = args ? initialArgs(args, varMap) : []; + const initial = [ + createGlobal(), + createEmptyESValue(InteroperAbilityNames.PARAM) + ]; + const initialArgsStatement = args ? initialArgs(args, varMap, updateProp) : []; return arkts.factory.createBlock( [ - createGlobal(), + ...initial, ...initialArgsStatement, ...createExtraInfo(['page'], [path]), - createESUndefined(), - ...createESBlank(), ...createELMTID(), - ...createComponent(moduleName, className), - ...invokeViewPUCreate(), - // ...paramsLambdaDeclaration(className, args), - // setPropertyESValue( - // 'component', - // 'paramsGenerator_', - // arkts.factory.createIdentifier(InteroperAbilityNames.PARAMSLAMBDA) - // ), + ...createComponent(className), createInitReturn(className) ] ); } -function createInitializer(context: InteropContext, varMap: Map): arkts.ArrowFunctionExpression { - const block = createWrapperBlock(context, varMap); +function createInitializer(context: InteropContext, varMap: Map, + updateProp: arkts.Property[]): arkts.ArrowFunctionExpression { + const block = createWrapperBlock(context, varMap, updateProp); return arkts.factory.createArrowFunction( arkts.factory.createScriptFunction( block, @@ -477,19 +217,66 @@ function createInitializer(context: InteropContext, varMap: Map): arkts.ArrowFunctionExpression { +function createUpdateProp(updateProp: arkts.Property[]): arkts.Statement[] { + const result: arkts.Statement[] = []; + const updateParam = createEmptyESValue('updateParam'); + result.push(updateParam); + updateProp.forEach((prop) => { + const key = prop.key as arkts.Identifier; + const value = prop.value; + const insertProperty = setPropertyESValue('updateParam', key.name, value!); + result.push(insertProperty); + }); + return result; +} + +function updateStateVars(updateProp: arkts.Property[]): arkts.Statement[] { + const insertProp = createUpdateProp(updateProp); + return [ + ...insertProp, + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(InteroperAbilityNames.INSTANCE), + arkts.factory.createIdentifier(ESValueMethodNames.INVOKEMETHOD), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createStringLiteral('updateStateVars'), + arkts.factory.createIdentifier('updateParam') + ] + ) + ]; +} + + +/** + * + * @param updateProp + * @returns (instance: ESValue) => { instance.invokeMethod('updateStateVars', updateParam) } + */ +function createUpdater(updateProp: arkts.Property[]): arkts.ArrowFunctionExpression { + const updateState = (updateProp.length !== 0) ? updateStateVars(updateProp) : []; return arkts.factory.createArrowFunction( arkts.factory.createScriptFunction( arkts.factory.createBlock( [ - + ...updateState ] ), arkts.factory.createFunctionSignature( undefined, [ arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(InteroperAbilityNames.INSTANCE, esvalue), + arkts.factory.createIdentifier(InteroperAbilityNames.INSTANCE, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE) + ) + ) + ), undefined, ), ], @@ -502,76 +289,91 @@ function createUpdater(esvalue: arkts.ETSTypeReference, varMap: Map { +function updateArguments(context: InteropContext, name: string): arkts.ObjectExpression { + return context.arguments ? arkts.factory.updateObjectExpression( + context.arguments, + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [ + ...(context.arguments?.properties as arkts.Property[]), + arkts.factory.createProperty( + arkts.factory.createIdentifier(name), + arkts.factory.createTSAsExpression( + context.content, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('Function') + ) + ), + false + ) + ) + ], + false + ) : arkts.factory.createObjectExpression( + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [ + arkts.factory.createProperty( + arkts.factory.createIdentifier(name), + arkts.factory.createTSAsExpression( + context.content, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('Function') + ) + ), + false + ) + ) + ], + false + ); +} + +function generateVarMap(context: InteropContext, node: arkts.Identifier): Map { + let needBuilderParam = !!context.content; const decl = arkts.getDecl(node); if (!(decl instanceof arkts.ClassDefinition)) { throw Error("can't find legacy class declaration"); } - const result = new Map(); + const result = new Map(); const definition = decl; const body = definition.body; body.forEach(node => { if (node instanceof arkts.ClassProperty && node.key instanceof arkts.Identifier) { const key = node.key.name; - const annotations = node.annotations; - const decorators: string[] = annotations.map(annotation => { - return (annotation.expr as arkts.Identifier).name; - }); - const type: arkts.TypeNode = node.typeAnnotation!; - result.set(key, {decorators: decorators, type: type}); + result.set(key, node); + if (needBuilderParam && hasDecorator(node, DecoratorNames.BUILDER_PARAM)) { + context.arguments = updateArguments(context, key); + needBuilderParam = false; + } } }); return result; } -export function updateArkUICompatible(node: arkts.CallExpression): arkts.CallExpression { - const classInterop = (node.expression as arkts.MemberExpression).object as arkts.Identifier; - const className = classInterop.name; - const args = node.arguments; - const path = (args[0] as arkts.StringLiteral).str; - const line = args[1] instanceof arkts.UndefinedLiteral ? undefined : (args[1] as arkts.NumberLiteral).value; - const col = args[2] instanceof arkts.UndefinedLiteral ? undefined : (args[2] as arkts.NumberLiteral).value; - const options = args[3] instanceof arkts.UndefinedLiteral ? undefined : args[3] as arkts.ObjectExpression; - const context: InteropContext = { - className: className, - path: path, - line: line, - col: col, - arguments: options - }; - - const varMap: Map = generateVarMap(classInterop); - const esvalue = arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE) - ) - ); - const initializer = createInitializer(context, varMap); - const updater = createUpdater(esvalue, varMap); - return arkts.factory.updateCallExpression( - node, - arkts.factory.createIdentifier(InteroperAbilityNames.ARKUICOMPATIBLE), - undefined, - [ - initializer, - updater, - ] - ); -} - - function generateStructInfo(context: InteropContext): arkts.AstNode[] { const result: arkts.AstNode[] = [ arkts.factory.createStringLiteral(context.path), context.line ? arkts.factory.createIdentifier(context.line.toString()) : arkts.factory.createUndefinedLiteral(), context.col ? arkts.factory.createIdentifier(context.col.toString()) : arkts.factory.createUndefinedLiteral(), - context.arguments ?? arkts.factory.createUndefinedLiteral() + context.arguments ?? arkts.factory.createUndefinedLiteral(), + context.content ?? arkts.factory.createUndefinedLiteral(), ]; return result; } -export function generateTempCallFunction(context: InteropContext): arkts.CallExpression { +/** + * + * @param {Object} context - Context information about the parsed CustomComponent. + * @param {string} context.className - Name of the CustomComponent class. + * @param {string} context.path - File path where the CustomComponent is located. + * @param {number} [context.line] - Line number of the CustomComponent in the file (optional). + * @param {number} [context.col] - Column number of the CustomComponent in the file (optional). + * @param {Object} [context.arguments] - Additional arguments passed to the CustomComponent (optional). + * @returns {Object} .instantiate_Interop. + */ +export function generateInstantiateInterop(context: InteropContext): arkts.CallExpression { return arkts.factory.createCallExpression( arkts.factory.createMemberExpression( arkts.factory.createIdentifier(context.className), @@ -585,11 +387,64 @@ export function generateTempCallFunction(context: InteropContext): arkts.CallExp ); } +/** + * + * @param node + * @returns {boolean} Checks if a given CallExpression represents a call to .instantiate_Interop. + */ export function isArkUICompatible(node: arkts.AstNode): boolean { if (node instanceof arkts.CallExpression && node.expression instanceof arkts.MemberExpression && node.expression.property instanceof arkts.Identifier && node.expression.property.name === 'instantiate_Interop') { + ImportCollector.getInstance().collectSource(InteroperAbilityNames.ARKUICOMPATIBLE, InteroperAbilityNames.INTEROP); + ImportCollector.getInstance().collectImport(InteroperAbilityNames.ARKUICOMPATIBLE); + ImportCollector.getInstance().collectSource(InteroperAbilityNames.GETCOMPATIBLESTATE, InteroperAbilityNames.INTEROP); + ImportCollector.getInstance().collectImport(InteroperAbilityNames.GETCOMPATIBLESTATE); + ImportCollector.getInstance().collectSource(BuilderMethodNames.TRANSFERCOMPATIBLEBUILDER, InteroperAbilityNames.INTEROP); + ImportCollector.getInstance().collectImport(BuilderMethodNames.TRANSFERCOMPATIBLEBUILDER); return true; } return false; +} + + +/** + * + * @param node + * @returns After Checked, transform instantiate_Interop -> ArkUICompatible + */ +export function generateArkUICompatible(node: arkts.CallExpression): arkts.CallExpression { + const classInterop = (node.expression as arkts.MemberExpression).object as arkts.Identifier; + const className = classInterop.name; + const args = node.arguments; + const path = (args[0] as arkts.StringLiteral).str; + const line = args[1] instanceof arkts.UndefinedLiteral ? undefined : (args[1] as arkts.NumberLiteral).value; + const col = args[2] instanceof arkts.UndefinedLiteral ? undefined : (args[2] as arkts.NumberLiteral).value; + const options = args[3] instanceof arkts.UndefinedLiteral ? undefined : args[3] as arkts.ObjectExpression; + const content = args[4] instanceof arkts.UndefinedLiteral ? undefined : args[4] as arkts.ArrowFunctionExpression; + const context: InteropContext = { + className: className, + path: path, + line: line, + col: col, + arguments: options, + content: content, + }; + + const varMap: Map = generateVarMap(context, classInterop); + const updateProp: arkts.Property[] = []; + const initializer = createInitializer(context, varMap, updateProp); + const updater = createUpdater(updateProp); + const result = arkts.factory.updateCallExpression( + node, + arkts.factory.createIdentifier(InteroperAbilityNames.ARKUICOMPATIBLE), + undefined, + [ + initializer, + updater, + arkts.factory.createThisExpression(), + ] + ); + arkts.NodeCache.getInstance().collect(result); + return result; } \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/legacy-transformer.ts b/arkui-plugins/ui-plugins/interop/legacy-transformer.ts similarity index 55% rename from arkui-plugins/ui-plugins/legacy-transformer.ts rename to arkui-plugins/ui-plugins/interop/legacy-transformer.ts index 10047677a..1b831a9b0 100644 --- a/arkui-plugins/ui-plugins/legacy-transformer.ts +++ b/arkui-plugins/ui-plugins/interop/legacy-transformer.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2025 Huawei Device Co., Ltd. + * Copyright (c) 2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -13,21 +13,32 @@ * limitations under the License. */ + + import * as arkts from '@koalaui/libarkts'; -import { getInteropPath } from '../path'; +import { getInteropPath } from '../../path'; const interop = require(getInteropPath()); const nullptr = interop.nullptr; -import { AbstractVisitor, VisitorOptions } from '../common/abstract-visitor'; -import { InteroperAbilityNames } from '../common/predefines'; -import { getCustomComponentOptionsName } from './utils'; +import { AbstractVisitor, VisitorOptions } from '../../common/abstract-visitor'; +import { InteroperAbilityNames } from './predefines'; +import { getCustomComponentOptionsName } from '../utils'; +import { factory } from '../ui-factory'; interface LegacyTransformerOptions extends VisitorOptions { structList?: string[] } +type ScopeInfo = { + name: string; + isEntry?: boolean; + isComponent?: boolean; + isReusable?: boolean; +}; + export class LegacyTransformer extends AbstractVisitor { private structList: string[] = []; private componentInterfaceCollection: arkts.TSInterfaceDeclaration[] = []; + private scopeInfos: ScopeInfo[] = []; constructor(options?: LegacyTransformerOptions) { const _options: LegacyTransformerOptions = options ?? {}; @@ -35,9 +46,12 @@ export class LegacyTransformer extends AbstractVisitor { this.structList = _options.structList ?? []; } + // TODO: check reset reset(): void { super.reset(); + this.structList = []; this.componentInterfaceCollection = []; + this.scopeInfos = []; } getList(): string[] { @@ -66,12 +80,14 @@ export class LegacyTransformer extends AbstractVisitor { col.setOptional(true); const options = this.createParam('options', getCustomComponentOptionsName(name)); options.setOptional(true); + const trailingBlock = this.createParam('trailingBlock', 'Object'); + trailingBlock.setOptional(true); const script = arkts.factory.createScriptFunction( arkts.factory.createBlock([]), arkts.FunctionSignature.createFunctionSignature( undefined, - [path, line, col, options], + [path, line, col, options, trailingBlock], arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), false ), @@ -111,7 +127,7 @@ export class LegacyTransformer extends AbstractVisitor { const interfaceNode = arkts.factory.createInterfaceDeclaration( [], arkts.factory.createIdentifier(getCustomComponentOptionsName(name)), - nullptr, // TODO: wtf + nullptr, arkts.factory.createInterfaceBody([...(this.generateMember(map) || [])]), false, false @@ -142,13 +158,10 @@ export class LegacyTransformer extends AbstractVisitor { definition.super, [...definition.body, instantiate_Interop], definition.modifiers, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaLanguage.JS ); - console.log('print legacyclass definition' + newDefinition.dumpSrc()); - - - //TODO: need check if (arkts.isStructDeclaration(node)) { const _node = arkts.factory.createClassDeclaration(newDefinition); _node.modifiers = node.modifiers; @@ -159,44 +172,45 @@ export class LegacyTransformer extends AbstractVisitor { } processConstructor(node: arkts.MethodDefinition): arkts.MethodDefinition { - const esvalue = arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE) - ) - ); - const script = arkts.factory.createScriptFunction( - arkts.factory.createBlock([]), - arkts.factory.createFunctionSignature( - undefined, - [ - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(InteroperAbilityNames.PARENT, esvalue), - undefined, - ), - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(InteroperAbilityNames.PARAM, esvalue), - undefined, - ), - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier('localStorage', esvalue), - undefined, - ), - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID, esvalue), - undefined, - ), - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(InteroperAbilityNames.PARAMSLAMBDA, esvalue), - undefined, - ), - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(InteroperAbilityNames.EXTRAINFO, esvalue), - undefined, - ) - ], undefined, false), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_CONSTRUCTOR, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, - ); + const valueType = arkts.factory.createUnionType([ + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('Object') + ) + ), + arkts.factory.createETSUndefinedType() + ]); + const script = factory.createScriptFunction({ + body: arkts.factory.createBlock([]), + params: [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(InteroperAbilityNames.PARENT, valueType), + undefined, + ), + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(InteroperAbilityNames.PARAM, valueType), + undefined, + ), + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier('localStorage', valueType), + undefined, + ), + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID, valueType), + undefined, + ), + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(InteroperAbilityNames.PARAMSLAMBDA, valueType), + undefined, + ), + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(InteroperAbilityNames.EXTRAINFO, valueType), + undefined, + ) + ], + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_CONSTRUCTOR, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + }) return arkts.factory.updateMethodDefinition( node, node.kind, @@ -230,25 +244,103 @@ export class LegacyTransformer extends AbstractVisitor { return node; } + enter(node: arkts.AstNode): void { + if (arkts.isStructDeclaration(node) && !!node.definition.ident) { + const scopeInfo: ScopeInfo = { name: node.definition.ident.name }; + this.scopeInfos.push(scopeInfo); + } + } + + exit(node: arkts.AstNode): void { + if (arkts.isStructDeclaration(node) || arkts.isClassDeclaration(node)) { + if (!node.definition || !node.definition.ident || this.scopeInfos.length === 0) { + return; + } + if (this.scopeInfos[this.scopeInfos.length - 1]?.name === node.definition.ident.name) { + this.scopeInfos.pop(); + } + } + } + + handleWrappedBuilderNode(node: arkts.ETSTypeReference): arkts.ETSTypeReference { + if (node.part && arkts.isETSTypeReferencePart(node.part) && node.part.name && + arkts.isIdentifier(node.part.name) && node.part.name.name === 'WrappedBuilder') { + return arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('Any') + ) + ); + } + return node; + } + + // handle WrappedBuilder + handleWrappedBuilder(node: arkts.VariableDeclarator): arkts.VariableDeclarator { + if (arkts.isIdentifier(node.name) && node.name.typeAnnotation) { + let typeAnnotation = node.name.typeAnnotation; + // WrappedBuilder<[aa]>[] => Any[] + if (arkts.isTSArrayType(typeAnnotation) && typeAnnotation.elementType && + arkts.isETSTypeReference(typeAnnotation.elementType)) { + return arkts.factory.updateVariableDeclarator( + node, + node.flag, + arkts.factory.updateIdentifier( + node.name, + node.name.name, + arkts.TSArrayType.updateTSArrayType( + typeAnnotation, + this.handleWrappedBuilderNode(typeAnnotation.elementType) + ) + ), + node.initializer + ); + } + // WrappedBuilder<[aa]> => Any + if (arkts.isETSTypeReference(typeAnnotation)) { + return arkts.factory.updateVariableDeclarator( + node, + node.flag, + arkts.factory.updateIdentifier( + node.name, + node.name.name, + this.handleWrappedBuilderNode(typeAnnotation) + ), + node.initializer + ); + } + } + return node; + } + visitor(node: arkts.AstNode): arkts.AstNode { + this.enter(node); const newNode = this.visitEachChild(node); if (arkts.isEtsScript(newNode)) { return this.processEtsScript(newNode); } if (arkts.isStructDeclaration(newNode)) { - const className = node.definition?.ident?.name; - const memberMap = this.collectComponentMembers(node as arkts.StructDeclaration, className); + const definition = newNode.definition!; + const annotations = definition.annotations; + if (annotations.some(annotation => annotation instanceof arkts.Identifier && annotation.name === 'Component')) { + return newNode; + } + const className = newNode.definition?.ident?.name!; + const memberMap = this.collectComponentMembers(newNode as arkts.StructDeclaration, className); this.componentInterfaceCollection.push(this.generateComponentInterface(className, node.modifiers, memberMap)); const updateNode = this.processComponent(newNode); + this.exit(newNode); return updateNode; } - if (arkts.isMethodDefinition(newNode)) { + if (this.scopeInfos.length > 0 && arkts.isMethodDefinition(newNode)) { const kind = newNode.kind; if (kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR) { const updateNode = this.processConstructor(newNode); return updateNode; } } + if (arkts.isVariableDeclarator(newNode)) { + return this.handleWrappedBuilder(newNode); + } return newNode; } } \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop/predefines.ts b/arkui-plugins/ui-plugins/interop/predefines.ts new file mode 100644 index 000000000..1a8f5deab --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/predefines.ts @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +export enum InteroperAbilityNames { + ARKTS_1_1 = '1.1', + ARKTS_1_2 = '1.2', + ARKUICOMPATIBLE = 'compatibleComponent', + ELMTID = 'elmtId', + NUMBER = 'number', + PARENT = 'parent', + INSTANCE = 'instance', + PARAM = 'param', + EXTRAINFO = 'extraInfo', + COMPONENT = 'component', + CONSTRUCTOR = 'constructor', + MODULE = 'module', + STRUCTOBJECT = 'structObject', + GLOBAL = 'global', + PARAMSLAMBDA = 'paramsLambda', + INTEROPCOMPONENT = 'interopComponent', + GETCOMPATIBLESTATE = 'getCompatibleState', + CREATESTATE = 'createStateVariable', + INTEROP = 'arkui.component.interop', +} + + +export enum ESValueMethodNames { + ESVALUE = 'ESValue', + INITEMPTYOBJECT = 'instantiateEmptyObject', + SETPROPERTY = 'setProperty', + GETPROPERTY = 'getProperty', + INSTANTIATE = 'instantiate', + INVOKE = 'invoke', + INVOKEMETHOD = 'invokeMethod', + LOAD = 'load', + WRAP = 'wrap', + WRAPINT = 'wrapInt', + WRAPSTRING = 'wrapString', + UNWRAP = 'unwrap', +} + +export enum InteropProvideNames { + STATICPROVIDE = 'provide', + FINDPROVIDE = 'findProvide', + PROVIDEDPROPNAME = 'providedPropName', + SETFINDPROVIDE = 'setFindProvideInterop', + SETVIEWPUFINDPROVIDE = 'setViewPUFindProvideInterop', + FINDPROVIDECALLBACK = 'findProvideInterop', +} + +export enum BuilderMethodNames { + RUNPENDINGJOBS = 'runPendingJobs', + CREATECOMPATIBLENODE = 'createCompatibleNode', + TRANSFERCOMPATIBLEBUILDER = 'transferCompatibleBuilder', +} + +export enum BuilderParams { + PARAM_WRAPPED_KEY = 'param_wrapped_key', + INSTANCEPARAM = 'instanceParam', + PARAM_WRAPPED = 'param_wrapped', + PARAM_WRAPPED_IT = 'param_wrapped_it' +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop/utils.ts b/arkui-plugins/ui-plugins/interop/utils.ts new file mode 100644 index 000000000..3dff5e7af --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/utils.ts @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + + +import * as arkts from '@koalaui/libarkts'; +import { ESValueMethodNames, InteroperAbilityNames } from './predefines'; + + +/** + * + * @param result + * @returns let result = ESValue.instantiateEmptyObject() + */ +export function createEmptyESValue(result: string): arkts.VariableDeclaration { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(result), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE), + arkts.factory.createIdentifier(ESValueMethodNames.INITEMPTYOBJECT), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + ) + ] + ); +} + +/** + * + * @param value + * @returns ESValue.wrap(value) + */ +export function getWrapValue(value: arkts.AstNode): arkts.AstNode { + return arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE), + arkts.factory.createIdentifier(ESValueMethodNames.WRAP), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [value] + ); +} + +/** + * + * @param object + * @param key + * @param value + * @returns object.setProperty(key, value) + */ +export function setPropertyESValue(object: string, key: string, value: arkts.AstNode): arkts.ExpressionStatement { + return arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(object), + arkts.factory.createIdentifier(ESValueMethodNames.SETPROPERTY), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createStringLiteral(key), + value.clone() + ] + ) + ); +} + +/** + * + * @param result + * @param obj + * @param key + * @returns let result = object.getProperty(key) + */ +export function getPropertyESValue(result: string, obj: string, key: string): arkts.VariableDeclaration { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(result), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(obj), + arkts.factory.createIdentifier(ESValueMethodNames.GETPROPERTY), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [arkts.factory.create1StringLiteral(key)] + ) + ) + ] + ); +} + +/** + * + * @param {string} stateVarName - Original state variable name to be proxied. + * @returns {string} Proxied variable name in the format: "__Proxy_{stateVarName}". + */ +export function stateProxy(stateVarName: string): string { + return `__Proxy_${stateVarName}`; +} + +/** + * get elmtId + * @returns + * let viewStackProcessor = global.getProperty("ViewStackProcessor"); + * let createId = viewStackProcessor.getProperty("AllocateNewElmetIdForNextComponent"); + * let elmtId = createId.invoke(); + */ +export function createELMTID(): arkts.Statement[] { + const body: arkts.Statement[] = []; + const viewStackProcessor = getPropertyESValue('viewStackProcessor', InteroperAbilityNames.GLOBAL, 'ViewStackProcessor'); + body.push(viewStackProcessor); + const createId = getPropertyESValue('createId', 'viewStackProcessor', 'AllocateNewElmetIdForNextComponent'); + body.push(createId); + const elmtId = arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('createId'), + arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + )] + ); + body.push(elmtId); + return body; +} + +/** + * + * @param componentName + * @returns return { + * component: component, + * name: componentName, + * }; + */ +export function createInitReturn(componentName: string): arkts.ReturnStatement { + return arkts.factory.createReturnStatement( + arkts.ObjectExpression.createObjectExpression( + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [ + arkts.Property.createProperty( + arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT), + arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT) + ), + arkts.Property.createProperty( + arkts.factory.createIdentifier('name'), + arkts.factory.createStringLiteral(componentName) + ) + ], + false + ), + ); +} + +/** + * createGlobal + * @returns let global = ESValue.getGlobal(); + */ +export function createGlobal(): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(InteroperAbilityNames.GLOBAL), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE), + arkts.factory.createIdentifier('getGlobal'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + )] + ); +} diff --git a/arkui-plugins/ui-plugins/preprocessor-transform.ts b/arkui-plugins/ui-plugins/preprocessor-transform.ts deleted file mode 100644 index 139a00d95..000000000 --- a/arkui-plugins/ui-plugins/preprocessor-transform.ts +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (c) 2022-2025 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as arkts from '@koalaui/libarkts'; -import { AbstractVisitor, VisitorOptions } from '../common/abstract-visitor'; -import { CustomComponentNames } from './utils'; -import { factory } from './ui-factory'; -import { - ARKUI_COMPONENT_IMPORT_NAME, - IMPORT_SOURCE_MAP, - OUTPUT_DEPENDENCY_MAP, - ARKUI_STATEMANAGEMENT_IMPORT_NAME, - KIT_ARKUI_NAME, -} from '../common/predefines'; -import { NameCollector } from './name-collector'; - -interface MemoImportCollection { - memo: boolean; - memoContextType: boolean; - memoIdType: boolean; -} - -export class PreprocessorTransformer extends AbstractVisitor { - private outNameArr: string[] = []; - private memoNameArr: string[] = []; - private structInterfaceImport: arkts.ETSImportDeclaration[] = []; - private memoImportCollection: Partial = {}; - private localComponentNames: string[] = []; - private isMemoImportOnce: boolean = false; - - private readonly nameCollector: NameCollector; - - constructor(options?: VisitorOptions) { - super(options); - this.nameCollector = NameCollector.getInstance(); - } - - reset(): void { - super.reset(); - this.outNameArr = []; - this.memoNameArr = []; - this.structInterfaceImport = []; - this.memoImportCollection = {}; - this.localComponentNames = []; - this.isMemoImportOnce = false; - IMPORT_SOURCE_MAP.clear(); - IMPORT_SOURCE_MAP.set('arkui.stateManagement.runtime', new Set(['memo', '__memo_context_type', '__memo_id_type'])); - } - - isCustomConponentDecl(node: arkts.CallExpression): boolean { - const structCollection: Set = arkts.GlobalInfo.getInfoInstance().getStructCollection(); - const nodeName: string = node.expression.dumpSrc(); - if (structCollection.has(nodeName)) { - return true; - } - return false; - } - - isComponentFunctionCall(node: arkts.CallExpression): boolean { - if (!node || !arkts.isIdentifier(node.expression)) return false; - return this.localComponentNames.includes(node.expression.name); - } - - transformComponentCall(node: arkts.CallExpression): arkts.TSAsExpression | arkts.CallExpression { - if (node.arguments.length === 0 && node.trailingBlock) { - return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ - arkts.factory.createUndefinedLiteral(), - ]); - } else if (arkts.isObjectExpression(node.arguments[0])) { - const componentName: string = `${ - CustomComponentNames.COMPONENT_INTERFACE_PREFIX - }${node.expression.dumpSrc()}`; - const newArg = arkts.factory.createTSAsExpression( - node.arguments[0].clone(), - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(componentName)) - ), - true - ); - return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ - newArg, - ...node.arguments.slice(1), - ]); - } else { - return node; - } - } - - transformComponentFunctionCall(node: arkts.CallExpression) { - if (!node || !arkts.isIdentifier(node.expression)) return node; - - const componentInfo = this.nameCollector.getComponentInfo(node.expression.name); - if (!componentInfo) return node; - if (componentInfo.argsNum === 0) return node; - if (node.arguments.length >= componentInfo.argsNum - 1) return node; - - const defaultArgs: arkts.UndefinedLiteral[] = []; - let count = 0; - while (count < componentInfo.argsNum - node.arguments.length - 1) { - defaultArgs.push(arkts.factory.createUndefinedLiteral()); - count++; - } - return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ - ...node.arguments, - ...defaultArgs, - ]); - } - - addDependencesImport(node: arkts.ETSImportDeclaration): void { - if (!node.source) return; - - const isFromCompImport: boolean = node.source.str === ARKUI_COMPONENT_IMPORT_NAME || node.source.str === KIT_ARKUI_NAME; - const structCollection: Set = arkts.GlobalInfo.getInfoInstance().getStructCollection(); - node.specifiers.forEach((item: arkts.AstNode) => { - if (!arkts.isImportSpecifier(item) || !item.imported?.name) return; - - const importName: string = item.imported.name; - this.memoImportCollection.memo ||= importName === 'memo'; - this.memoImportCollection.memoContextType ||= importName === '__memo_context_type'; - this.memoImportCollection.memoIdType ||= importName === '__memo_id_type'; - if (isFromCompImport && this.nameCollector.getComponents().includes(importName)) { - this.localComponentNames.push(item.local?.name ?? importName); - } - - if (structCollection.has(importName)) { - const interfaceName: string = CustomComponentNames.COMPONENT_INTERFACE_PREFIX + importName; - const newImport: arkts.ETSImportDeclaration = arkts.factory.createImportDeclaration( - node.source?.clone(), - [factory.createAdditionalImportSpecifier(interfaceName, interfaceName)], - arkts.Es2pandaImportKinds.IMPORT_KINDS_VALUE, - this.program!, - arkts.Es2pandaImportFlags.IMPORT_FLAGS_NONE - ); - this.structInterfaceImport.push(newImport); - } else { - this.addImportWithSpecifier(item, node.source!); - } - }); - } - - getSourceDependency(sourceName: string): string { - let dependencyName: string = ''; - IMPORT_SOURCE_MAP.forEach((value: Set, key: string) => { - if (value.has(sourceName)) { - dependencyName = key; - } - }); - return dependencyName; - } - - updateSourceDependencyMap(key: string, value: string[]): void { - const newValue: Set = IMPORT_SOURCE_MAP.get(key) ?? new Set(); - for (const v of value) { - newValue.add(v); - } - IMPORT_SOURCE_MAP.set(key, newValue); - } - - getOutDependencyName(inputName: string): string[] { - const sourceName: string[] = []; - if (OUTPUT_DEPENDENCY_MAP.has(inputName)) { - OUTPUT_DEPENDENCY_MAP.get(inputName)!.forEach((item: string) => { - sourceName.push(item); - }); - } - return sourceName; - } - - updateOutDependencyMap(key: string, value: string[]): void { - const oldValue: string[] = OUTPUT_DEPENDENCY_MAP.get(key) ?? []; - const newValue: string[] = [...value, ...oldValue]; - OUTPUT_DEPENDENCY_MAP.set(key, newValue); - } - - clearGenSymInOutDependencyMap(genSymKey: string): void { - if (OUTPUT_DEPENDENCY_MAP.has(genSymKey)) { - OUTPUT_DEPENDENCY_MAP.delete(genSymKey); - } - } - - prepareDependencyMap(node: arkts.ImportSpecifier, source: arkts.StringLiteral): void { - if (!node.imported?.name) return; - - // Handling component imports - const importName: string = node.imported.name; - const sourceName: string = source.str; - if ( - this.nameCollector.getComponents().includes(importName) && - (sourceName === ARKUI_COMPONENT_IMPORT_NAME || sourceName === KIT_ARKUI_NAME) - ) { - const newDependencies = [`${importName}Attribute`]; - this.updateOutDependencyMap(importName, newDependencies); - this.updateSourceDependencyMap(sourceName, newDependencies); - } else if ( - OUTPUT_DEPENDENCY_MAP.get(importName) && - (sourceName === ARKUI_COMPONENT_IMPORT_NAME || - sourceName === ARKUI_STATEMANAGEMENT_IMPORT_NAME || - sourceName === KIT_ARKUI_NAME) - ) { - const newDependencies: string[] = OUTPUT_DEPENDENCY_MAP.get(importName) ?? []; - this.updateSourceDependencyMap(sourceName, newDependencies); - } - } - - prepareMemoImports(): void { - const newDependencies = []; - if (!this.memoImportCollection.memo) { - newDependencies.push('memo'); - } - if (!this.memoImportCollection.memoContextType) { - newDependencies.push('__memo_context_type'); - } - if (!this.memoImportCollection.memoIdType) { - newDependencies.push('__memo_id_type'); - } - if (newDependencies.length > 0) { - this.memoNameArr.push(...newDependencies); - this.isMemoImportOnce = true; - } - } - - addImportWithSpecifier(node: arkts.ImportSpecifier, source: arkts.StringLiteral): void { - if (!node.imported?.name) return; - - this.prepareDependencyMap(node, source); - const outName: string[] = this.getOutDependencyName(node.imported?.name); - this.outNameArr.push(...outName); - } - - updateScriptWithImport(): void { - if (!this.program) { - throw Error('Failed to insert import: Transformer has no program'); - } - - const outNames = new Set([...this.outNameArr, ...this.memoNameArr]); - outNames.forEach((item: string) => { - const source: string = this.getSourceDependency(item); - const newImport: arkts.ETSImportDeclaration = arkts.factory.createImportDeclaration( - arkts.factory.create1StringLiteral(source), - [factory.createAdditionalImportSpecifier(item, item)], - arkts.Es2pandaImportKinds.IMPORT_KINDS_VALUE, - this.program!, - arkts.Es2pandaImportFlags.IMPORT_FLAGS_NONE - ); - arkts.importDeclarationInsert(newImport, this.program!); - }); - this.structInterfaceImport.forEach((element: arkts.ETSImportDeclaration) => { - arkts.importDeclarationInsert(element, this.program!); - }); - } - - enter(node: arkts.AstNode): void { - if (this.isExternal && arkts.isFunctionDeclaration(node)) { - const component = this.nameCollector.findComponentFunction(node); - if (!!component) this.nameCollector.collectInfoFromComponentFunction(component); - } - } - - visitor(node: arkts.AstNode): arkts.AstNode { - this.enter(node); - const newNode = this.visitEachChild(node); - if (arkts.isCallExpression(newNode) && this.isCustomConponentDecl(newNode)) { - return this.transformComponentCall(newNode); - } else if (arkts.isCallExpression(newNode) && this.isComponentFunctionCall(newNode)) { - return this.transformComponentFunctionCall(newNode); - } - if (arkts.isETSImportDeclaration(node)) { - this.addDependencesImport(node); - } else if (arkts.isEtsScript(node)) { - if (!this.isMemoImportOnce) this.prepareMemoImports(); - this.updateScriptWithImport(); - } - return newNode; - } -} diff --git a/arkui-plugins/ui-plugins/property-translators/base.ts b/arkui-plugins/ui-plugins/property-translators/base.ts index 4be00d1c8..e79a7e763 100644 --- a/arkui-plugins/ui-plugins/property-translators/base.ts +++ b/arkui-plugins/ui-plugins/property-translators/base.ts @@ -14,55 +14,30 @@ */ import * as arkts from '@koalaui/libarkts'; -import { createGetter, createSetter, getStageManagementIdent } from './utils'; -import { createOptionalClassProperty } from '../utils'; +import { collectStateManagementTypeImport, createGetter, createSetter } from './utils'; +import { CustomComponentInfo, ClassInfo } from '../utils'; +import { StateManagementTypes } from '../../common/predefines'; +import { ClassScopeInfo } from '../struct-translators/utils'; +import { getClassPropertyType } from '../utils'; -export abstract class PropertyTranslator { - constructor( - protected property: arkts.ClassProperty, - protected structName: string - ) {} - - abstract translateMember(): arkts.AstNode[]; - - translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field = createOptionalClassProperty( - newName, - this.property, - getStageManagementIdent(this.property), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE - ); +export interface PropertyTranslatorOptions { + property: arkts.ClassProperty; + structInfo: CustomComponentInfo; +} - const member = arkts.factory.createTSNonNullExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(newName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ) - ); - const thisValue: arkts.MemberExpression = arkts.factory.createMemberExpression( - member, - arkts.factory.createIdentifier('value'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ); +export abstract class PropertyTranslator { + protected property: arkts.ClassProperty; + protected structInfo: CustomComponentInfo; + protected propertyType: arkts.TypeNode | undefined; - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisValue - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisValue - ); - return [field, getter, setter]; + constructor(options: PropertyTranslatorOptions) { + this.property = options.property; + this.structInfo = options.structInfo; + this.propertyType = getClassPropertyType(options.property); } + abstract translateMember(): arkts.AstNode[]; + translateGetter( originalName: string, typeAnnotation: arkts.TypeNode | undefined, @@ -77,10 +52,65 @@ export abstract class PropertyTranslator { left: arkts.MemberExpression ): arkts.MethodDefinition { const right: arkts.CallExpression = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('observableProxy'), + arkts.factory.createIdentifier(StateManagementTypes.OBSERVABLE_PROXY), undefined, [arkts.factory.createIdentifier('value')] ); + collectStateManagementTypeImport(StateManagementTypes.OBSERVABLE_PROXY); return createSetter(originalName, typeAnnotation, left, right); } } + +export abstract class MethodTranslator { + protected method: arkts.MethodDefinition; + protected classInfo: ClassInfo; + protected returnType: arkts.TypeNode | undefined; + + constructor(method: arkts.MethodDefinition, classInfo: ClassInfo) { + this.method = method; + this.classInfo = classInfo; + this.returnType = this.method.scriptFunction.returnTypeAnnotation?.clone(); + } + + abstract translateMember(): arkts.AstNode[]; +} + +export abstract class ObservedPropertyTranslator { + protected property: arkts.ClassProperty; + protected classScopeInfo: ClassScopeInfo; + protected propertyType: arkts.TypeNode | undefined; + + constructor(property: arkts.ClassProperty, classScopeInfo: ClassScopeInfo) { + this.property = property; + this.classScopeInfo = classScopeInfo; + this.propertyType = getClassPropertyType(this.property); + } + + abstract translateMember(): arkts.AstNode[]; + abstract createField(originalName: string, newName: string): arkts.ClassProperty[]; +} + +export type InterfacePropertyTypes = arkts.MethodDefinition | arkts.ClassProperty; + +export interface InterfacePropertyTranslatorOptions { + property: T; +} + +export abstract class InterfacePropertyTranslator + implements InterfacePropertyTranslatorOptions +{ + property: T; + + modified: boolean; + + constructor(options: InterfacePropertyTranslatorOptions) { + this.property = options.property; + this.modified = false; + } + + abstract translateProperty(): T; + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + return false; + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/builderParam.ts b/arkui-plugins/ui-plugins/property-translators/builderParam.ts index fbf600f06..40b3a54ea 100644 --- a/arkui-plugins/ui-plugins/property-translators/builderParam.ts +++ b/arkui-plugins/ui-plugins/property-translators/builderParam.ts @@ -15,51 +15,53 @@ import * as arkts from '@koalaui/libarkts'; -import { - createGetter, - createSetter, - generateThisBackingValue, - generateThisBacking, - getValueInAnnotation, - DecoratorNames, -} from './utils'; -import { PropertyTranslator } from './base'; -import { GetterSetter, InitializerConstructor } from './types'; import { backingField, expectName } from '../../common/arkts-utils'; -import { createOptionalClassProperty } from '../utils'; +import { DecoratorNames } from '../../common/predefines'; +import { createGetter, createSetter, generateThisBacking, hasDecorator, removeDecorator } from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; import { factory } from './factory'; +import { addMemoAnnotation, findCanAddMemoFromTypeAnnotation } from '../../collectors/memo-collectors/utils'; +import { PropertyCache } from './cache/propertyCache'; export class BuilderParamTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const mutableThis: arkts.Expression = generateThisBacking(newName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(mutableThis, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty(newName, this.property, '', - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, true); - const thisGetValue: arkts.Expression = generateThisBacking(newName, false, true); + const propertyType = this.propertyType; + if (!!propertyType && (arkts.isETSFunctionType(propertyType) || arkts.isETSUnionType(propertyType))) { + addMemoAnnotation(propertyType); + } + const field: arkts.ClassProperty = factory.createOptionalClassProperty( + newName, + this.property, + undefined, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + true + ); + arkts.NodeCache.getInstance().collect(field); const thisSetValue: arkts.Expression = generateThisBacking(newName, false, false); const getter: arkts.MethodDefinition = this.translateGetter( originalName, - this.property.typeAnnotation, - thisGetValue - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSetValue + propertyType, + arkts.hasModifierFlag(this.property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL) + ? generateThisBacking(newName, false, false) + : generateThisBacking(newName, false, true) ); + arkts.NodeCache.getInstance().collect(getter); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, propertyType, thisSetValue); + arkts.NodeCache.getInstance().collect(setter); return [field, getter, setter]; } @@ -82,6 +84,10 @@ export class BuilderParamTranslator extends PropertyTranslator implements Initia } generateInitializeStruct(mutableThis: arkts.Expression, originalName: string): arkts.AstNode { + const value = this.property.value; + if (!!value && arkts.isArrowFunctionExpression(value)) { + arkts.NodeCache.getInstance().collect(value); + } return arkts.factory.createAssignmentExpression( mutableThis, arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, @@ -94,9 +100,77 @@ export class BuilderParamTranslator extends PropertyTranslator implements Initia arkts.factory.createIdentifier('content'), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING ), - this.property.value ?? arkts.factory.createUndefinedLiteral(), + value ?? arkts.factory.createUndefinedLiteral(), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING ) ); } } + +export class BuilderParamInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateBuilderParamMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateBuilderParamPropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.BUILDER_PARAM)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.BUILDER_PARAM)) { + return true; + } + return false; + } + + /** + * Add `@memo` to getter's return type and setter's param type (expecting a function type or a function type within a union type). + * + * @param method expecting getter with `@BuilderParam` and a setter with `@BuilderParam` in the overloads. + */ + private updateBuilderParamMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + if (method.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET) { + const type: arkts.TypeNode | undefined = method.scriptFunction.returnTypeAnnotation; + if (!!type && (arkts.isETSFunctionType(type) || arkts.isETSUnionType(type))) { + addMemoAnnotation(type); + } + const newOverLoads = method.overloads.map((overload) => { + if (arkts.isMethodDefinition(overload)) { + return this.updateBuilderParamMethodInInterface(overload); + } + return overload; + }); + method.setOverloads(newOverLoads); + removeDecorator(method, DecoratorNames.BUILDER_PARAM); + arkts.NodeCache.getInstance().collect(method, { isGetter: true }); + } else if (method.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET) { + const param = method.scriptFunction.params.at(0)! as arkts.ETSParameterExpression; + const type = param.type; + if (!!type && (arkts.isETSFunctionType(type) || arkts.isETSUnionType(type))) { + addMemoAnnotation(type); + } + removeDecorator(method, DecoratorNames.BUILDER_PARAM); + arkts.NodeCache.getInstance().collect(method, { isSetter: true }); + } + return method; + } + + /** + * Add `@memo` to the type of the property (expecting a function type or a function type within a union type). + * + * @param property expecting property with `@BuilderParam`. + */ + private updateBuilderParamPropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + const type: arkts.TypeNode | undefined = property.typeAnnotation; + if (findCanAddMemoFromTypeAnnotation(type)) { + addMemoAnnotation(type); + } + removeDecorator(property, DecoratorNames.BUILDER_PARAM); + return property; + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/cache/monitorCache.ts b/arkui-plugins/ui-plugins/property-translators/cache/monitorCache.ts new file mode 100644 index 000000000..898d2aa21 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/cache/monitorCache.ts @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { factory } from '../factory'; + +export interface MonitorInfo { + monitorItem: string[] | undefined; + originalName: string; + newName: string; +} + +export class MonitorCache { + private _cache: Map>; + private static instance: MonitorCache | null = null; + + private constructor() { + this._cache = new Map>(); + } + + static getInstance(): MonitorCache { + if (!this.instance) { + this.instance = new MonitorCache(); + } + return this.instance; + } + + reset(): void { + this._cache.clear(); + } + + getCachedMonitors(className: string): arkts.AstNode[] { + if (!this._cache.has(className)) { + return []; + } + return Object.entries(this._cache.get(className)!).map((item: [string, MonitorInfo]) => { + const { monitorItem, originalName, newName } = item[1]; + return factory.generateinitAssignment(monitorItem, originalName, newName); + }); + } + + collectMonitors(className: string, monitorPath: string, info: MonitorInfo): void { + let classCache: Record = {}; + if (this._cache.has(className)) { + classCache = this._cache.get(className)!; + } + classCache[monitorPath] = info; + this._cache.set(className, classCache); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/cache/propertyCache.ts b/arkui-plugins/ui-plugins/property-translators/cache/propertyCache.ts new file mode 100644 index 000000000..ed7677586 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/cache/propertyCache.ts @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as arkts from '@koalaui/libarkts'; + +export interface PropertyCachedBody { + initializeBody?: arkts.AstNode[]; + updateBody?: arkts.AstNode[]; + toRecordBody?: arkts.Property[]; + constructorBody?: arkts.AstNode[]; + monitorBody?: arkts.AstNode[]; +} + +export class PropertyCache { + private _cache: Map; + private static instance: PropertyCache; + + private constructor() { + this._cache = new Map(); + } + + static getInstance(): PropertyCache { + if (!this.instance) { + this.instance = new PropertyCache(); + } + return this.instance; + } + + reset(): void { + this._cache.clear(); + } + + getInitializeBody(name: string): arkts.AstNode[] { + return this._cache.get(name)?.initializeBody ?? []; + } + + getUpdateBody(name: string): arkts.AstNode[] { + return this._cache.get(name)?.updateBody ?? []; + } + + getToRecordBody(name: string): arkts.Property[] { + return this._cache.get(name)?.toRecordBody ?? []; + } + + collectInitializeStruct(name: string, initializeStruct: arkts.AstNode[]): void { + const initializeBody = this._cache.get(name)?.initializeBody ?? []; + const newInitializeBody = [...initializeBody, ...initializeStruct]; + this._cache.set(name, { ...this._cache.get(name), initializeBody: newInitializeBody }); + } + + collectUpdateStruct(name: string, updateStruct: arkts.AstNode[]): void { + const updateBody = this._cache.get(name)?.updateBody ?? []; + const newUpdateBody = [...updateBody, ...updateStruct]; + this._cache.set(name, { ...this._cache.get(name), updateBody: newUpdateBody }); + } + + collectToRecord(name: string, toRecord: arkts.Property[]): void { + const toRecordBody = this._cache.get(name)?.toRecordBody ?? []; + const newToRecordBody = [...toRecordBody, ...toRecord]; + this._cache.set(name, { ...this._cache.get(name), toRecordBody: newToRecordBody }); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/computed.ts b/arkui-plugins/ui-plugins/property-translators/computed.ts new file mode 100644 index 000000000..8e5cc573a --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/computed.ts @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +import { expectName } from '../../common/arkts-utils'; +import { GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { ClassInfo, computedField } from '../utils'; +import { generateThisBacking, generateGetOrSetCall, getGetterReturnType } from './utils'; +import { MethodTranslator } from './base'; +import { InitializerConstructor } from './types'; +import { factory as UIFactory } from '../ui-factory'; +import { factory } from './factory'; + +export class ComputedTranslator extends MethodTranslator implements InitializerConstructor { + private isStatic: boolean; + + constructor(method: arkts.MethodDefinition, classInfo: ClassInfo) { + super(method, classInfo); + this.isStatic = this.method.isStatic; + } + + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.method.name); + const newName: string = computedField(originalName); + if (!this.returnType) { + this.returnType = getGetterReturnType(this.method); + } + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string): void {} + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const modifiers = this.isStatic ? this.method.modifiers : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE; + const field: arkts.ClassProperty = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(newName), + factory.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_COMPUTED, + this.returnType, + [ + arkts.factory.createArrowFunction( + UIFactory.createScriptFunction({ + body: this.method.scriptFunction.body?.clone(), + modifiers: modifiers, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + }) + ), + arkts.factory.createStringLiteral(originalName), + ], + false + ), + undefined, + modifiers, + false + ); + + const originGetter: arkts.MethodDefinition = UIFactory.updateMethodDefinition(this.method, { + function: { + returnTypeAnnotation: this.returnType, + body: arkts.factory.createBlock([ + arkts.factory.createReturnStatement(this.generateComputedGet(newName)), + ]), + }, + }); + + return [field, originGetter]; + } + + generateComputedGet(newName: string): arkts.CallExpression { + const thisValue: arkts.Expression = this.isStatic + ? UIFactory.generateMemberExpression(arkts.factory.createIdentifier(this.classInfo.className), newName) + : generateThisBacking(newName, false, true); + return generateGetOrSetCall(thisValue, GetSetTypes.GET); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/consume.ts b/arkui-plugins/ui-plugins/property-translators/consume.ts index bbb32deb1..52f38c6fa 100644 --- a/arkui-plugins/ui-plugins/property-translators/consume.ts +++ b/arkui-plugins/ui-plugins/property-translators/consume.ts @@ -15,6 +15,8 @@ import * as arkts from '@koalaui/libarkts'; +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; import { generateToRecord, createGetter, @@ -22,55 +24,44 @@ import { generateThisBacking, generateGetOrSetCall, getValueInAnnotation, - DecoratorNames, + hasDecorator, } from './utils'; -import { PropertyTranslator } from './base'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { backingField, expectName } from '../../common/arkts-utils'; -import { createOptionalClassProperty } from '../utils'; import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; export class ConsumeTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(originalName, newName); - currentStructInfo.initializeBody.push(initializeStruct); - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - 'ConsumeDecoratedVariable', + StateManagementTypes.CONSUME_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( - generateGetOrSetCall(thisValue, 'set') - ); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -92,12 +83,60 @@ export class ConsumeTranslator extends PropertyTranslator implements Initializer } generateInitializeStruct(originalName: string, newName: string): arkts.AstNode { - const alias = getValueInAnnotation(this.property, DecoratorNames.CONSUME); + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + arkts.factory.create1StringLiteral( + getValueInAnnotation(this.property, DecoratorNames.CONSUME) ?? originalName + ), + ]; + factory.judgeIfAddWatchFunc(args, this.property); const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - factory.generateInitConsumeCall(originalName, this.property, alias ?? originalName) + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_CONSUME, this.propertyType, args, true) ); return arkts.factory.createExpressionStatement(assign); } } + +export class ConsumeInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.CONSUME)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.CONSUME)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `ConsumeDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Consume` and a setter with `@Consume` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.CONSUME); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `ConsumeDecoratedVariable | undefined`. + * + * @param property expecting property with `@Consume`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.CONSUME); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/consumer.ts b/arkui-plugins/ui-plugins/property-translators/consumer.ts new file mode 100644 index 000000000..ccfb63461 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/consumer.ts @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + getValueInAnnotation, + hasDecorator, +} from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class ConsumerTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(originalName, newName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field: arkts.ClassProperty = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.CONSUMER_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } + + generateInitializeStruct(originalName: string, newName: string): arkts.AstNode { + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + arkts.factory.create1StringLiteral( + getValueInAnnotation(this.property, DecoratorNames.CONSUMER) ?? originalName + ), + this.property.value!, + ]; + const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_CONSUMER, this.propertyType, args, true) + ); + return arkts.factory.createExpressionStatement(assign); + } +} + +export class ConsumerInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.CONSUMER)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.CONSUMER)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `IConsumerDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Consumer` and a setter with `@Consumer` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.CONSUMER); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `IConsumerDecoratedVariable | undefined`. + * + * @param property expecting property with `@Consumer`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.CONSUMER); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/factory.ts b/arkui-plugins/ui-plugins/property-translators/factory.ts index c1f610a4b..b9bcba9a7 100644 --- a/arkui-plugins/ui-plugins/property-translators/factory.ts +++ b/arkui-plugins/ui-plugins/property-translators/factory.ts @@ -15,8 +15,26 @@ import * as arkts from '@koalaui/libarkts'; import { GenSymGenerator } from '../../common/gensym-generator'; +import { + DecoratorNames, + DECORATOR_TYPE_MAP, + StateManagementTypes, + ObservedNames, + MonitorNames, + TypeNames, +} from '../../common/predefines'; import { factory as UIFactory } from '../ui-factory'; -import { judgeIfAddWatchFunc } from './utils'; +import { + collectStateManagementTypeImport, + generateThisBacking, + getValueInAnnotation, + hasDecorator, + OptionalMemberInfo, + removeDecorator, +} from './utils'; +import { CustomComponentNames, getClassPropertyType } from '../utils'; +import { addMemoAnnotation, findCanAddMemoFromTypeAnnotation } from '../../collectors/memo-collectors/utils'; +import { annotation, isNumeric } from '../../common/arkts-utils'; export class factory { /** @@ -24,16 +42,17 @@ export class factory { * * @param object item before ?.. * @param key item after ?.. + * @param info optional member information */ static createBlockStatementForOptionalExpression( object: arkts.AstNode, key: string, - isCall: boolean = false + info?: OptionalMemberInfo ): arkts.Expression { let id = GenSymGenerator.getInstance().id(key); const statements: arkts.Statement[] = [ factory.generateLetVariableDecl(arkts.factory.createIdentifier(id), object), - factory.generateTernaryExpression(id, key, isCall), + factory.generateTernaryExpression(id, key, info), ]; return arkts.factory.createBlockExpression(statements); } @@ -67,7 +86,7 @@ export class factory { static generateTernaryExpression( testLeft: string, key: string, - isCall: boolean = false + info?: OptionalMemberInfo ): arkts.ExpressionStatement { const test = arkts.factory.createBinaryExpression( arkts.factory.createIdentifier(testLeft), @@ -75,22 +94,30 @@ export class factory { arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_EQUAL ); const consequent: arkts.Expression = arkts.factory.createUndefinedLiteral(); - const alternate: arkts.MemberExpression = arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(testLeft), - arkts.factory.createIdentifier(key), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ); return arkts.factory.createExpressionStatement( arkts.factory.createConditionalExpression( test, consequent, - isCall ? arkts.factory.createCallExpression(alternate, undefined, undefined) : alternate + this.generateConditionalAlternate(testLeft, key, info) ) ); } + static generateConditionalAlternate(testLeft: string, key: string, info?: OptionalMemberInfo): arkts.Expression { + const leftIdent: arkts.Identifier = arkts.factory.createIdentifier(testLeft); + const alternate: arkts.MemberExpression = UIFactory.generateMemberExpression( + leftIdent, + info?.isNumeric ? '$_get' : key + ); + return info?.isCall + ? arkts.factory.createCallExpression(alternate, undefined, undefined) + : info?.isNumeric + ? arkts.factory.createCallExpression(alternate, undefined, [ + arkts.factory.createNumericLiteral(Number(key)), + ]) + : alternate; + } + /** * generate an substitution for two optional expression ?., e.g. a?.b?.c. * @@ -168,7 +195,7 @@ export class factory { false, [ arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression(factory.generateThisCall(callbackName), undefined, [ + arkts.factory.createCallExpression(generateThisBacking(callbackName), undefined, [ arkts.factory.createIdentifier('_'), ]) ), @@ -176,20 +203,6 @@ export class factory { ); } - /* - * create this. with optional or nonNullable. - */ - static generateThisCall(name: string, optional: boolean = false, nonNull: boolean = false): arkts.Expression { - const member: arkts.Expression = arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(`${name}`), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - optional - ); - return nonNull ? arkts.factory.createTSNonNullExpression(member) : member; - } - /* * create `initializers!.!.()`. */ @@ -233,64 +246,658 @@ export class factory { } /* - * create `this.addProvidedVar(, , initializers?. ?? , , watchFunc)`. + * create `StateMgmtFactory.(this, ...);`. */ - static generateAddProvideVarCall( + static generateStateMgmtFactoryCall( + makeType: StateManagementTypes, + typeArguments: arkts.TypeNode | undefined, + args: arkts.AstNode[], + argsContainsThis: boolean + ): arkts.CallExpression { + collectStateManagementTypeImport(StateManagementTypes.STATE_MANAGEMENT_FACTORY); + return arkts.factory.createCallExpression( + UIFactory.generateMemberExpression( + arkts.factory.createIdentifier(StateManagementTypes.STATE_MANAGEMENT_FACTORY), + makeType + ), + typeArguments ? [typeArguments] : undefined, + [...(argsContainsThis ? [arkts.factory.createThisExpression()] : []), ...args] + ); + } + + /* + * create if statement in __updateStruct method. + */ + static createIfInUpdateStruct( originalName: string, + member: arkts.Expression, + args: arkts.AstNode[] + ): arkts.IfStatement { + const binaryItem = arkts.factory.createBinaryExpression( + factory.createBlockStatementForOptionalExpression( + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), + originalName + ), + arkts.factory.createUndefinedLiteral(), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NOT_STRICT_EQUAL + ); + return arkts.factory.createIfStatement( + binaryItem, + arkts.factory.createBlock([ + arkts.factory.createExpressionStatement(arkts.factory.createCallExpression(member, undefined, args)), + ]) + ); + } + + /* + * create `initializers!. as `. + */ + static generateDefiniteInitializers(type: arkts.TypeNode | undefined, originalName: string): arkts.Expression { + return arkts.factory.createTSAsExpression( + factory.createNonNullOrOptionalMemberExpression( + CustomComponentNames.COMPONENT_INITIALIZERS_NAME, + originalName, + false, + true + ), + type ? type.clone() : undefined, + false + ); + } + + static judgeIfAddWatchFunc(args: arkts.Expression[], property: arkts.ClassProperty): void { + if (hasDecorator(property, DecoratorNames.WATCH)) { + const watchStr: string | undefined = getValueInAnnotation(property, DecoratorNames.WATCH); + if (watchStr) { + args.push(factory.createWatchCallback(watchStr)); + } + } + } + + static createOptionalClassProperty( + name: string, property: arkts.ClassProperty, - alias: string, - allowOverride: boolean = false - ): arkts.CallExpression { - const args: arkts.Expression[] = [ - arkts.factory.create1StringLiteral(originalName), - arkts.factory.create1StringLiteral(alias), - arkts.factory.createBinaryExpression( - factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), - originalName + stageManagementType: StateManagementTypes | undefined, + modifiers: arkts.Es2pandaModifierFlags, + needMemo: boolean = false + ): arkts.ClassProperty { + const originType = getClassPropertyType(property); + const newType: arkts.TypeNode | undefined = !stageManagementType + ? property.typeAnnotation ?? UIFactory.createTypeReferenceFromString(TypeNames.ANY) + : originType; + if (needMemo && findCanAddMemoFromTypeAnnotation(newType)) { + addMemoAnnotation(newType); + } + const newProperty = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(name), + undefined, + !!stageManagementType ? factory.createStageManagementType(stageManagementType, originType) : newType, + modifiers, + false + ); + return arkts.classPropertySetOptional(newProperty, true); + } + + static createStageManagementType( + stageManagementType: StateManagementTypes, + type: arkts.TypeNode | undefined + ): arkts.ETSTypeReference { + collectStateManagementTypeImport(stageManagementType); + return arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(stageManagementType), + arkts.factory.createTSTypeParameterInstantiation([type ?? arkts.factory.createETSUndefinedType()]) + ) + ); + } + + /* + * create watch related members in Observed/Track classes + */ + static createWatchMembers(): arkts.AstNode[] { + const subscribedWatches: arkts.ClassProperty = arkts.factory.createClassProperty( + arkts.factory.createIdentifier('subscribedWatches'), + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_SUBSCRIBED_WATCHES, undefined, [], false), + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(StateManagementTypes.SUBSCRIBED_WATCHES) + ) + ), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + subscribedWatches.setAnnotations([annotation(DecoratorNames.JSONSTRINGIFYIGNORE)]); + collectStateManagementTypeImport(StateManagementTypes.SUBSCRIBED_WATCHES); + + const addWatchSubscriber = factory.createWatchMethod( + 'addWatchSubscriber', + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID, + 'watchId', + StateManagementTypes.WATCH_ID_TYPE, + false + ); + const removeWatchSubscriber = factory.createWatchMethod( + 'removeWatchSubscriber', + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_BOOLEAN, + 'watchId', + StateManagementTypes.WATCH_ID_TYPE, + true + ); + collectStateManagementTypeImport(StateManagementTypes.WATCH_ID_TYPE); + + const executeOnSubscribingWatches = factory.createWatchMethod( + 'executeOnSubscribingWatches', + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID, + 'propertyName', + 'string', + false + ); + + return [subscribedWatches, addWatchSubscriber, removeWatchSubscriber, executeOnSubscribingWatches]; + } + + /* + * helper for createWatchMembers to create watch methods + */ + static createWatchMethod( + methodName: string, + returnType: arkts.Es2pandaPrimitiveType, + paramName: string, + paramType: string, + isReturnStatement: boolean + ): arkts.MethodDefinition { + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + arkts.factory.createIdentifier(methodName), + arkts.factory.createScriptFunction( + arkts.factory.createBlock([ + isReturnStatement + ? arkts.factory.createReturnStatement( + arkts.factory.createCallExpression( + factory.thisSubscribedWatchesMember(methodName), + undefined, + [arkts.factory.createIdentifier(paramName)] + ) + ) + : arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + factory.thisSubscribedWatchesMember(methodName), + undefined, + [arkts.factory.createIdentifier(paramName)] + ) + ), + ]), + arkts.factory.createFunctionSignature( + undefined, + [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + paramName, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(paramType)) + ) + ), + undefined + ), + ], + arkts.factory.createPrimitiveType(returnType), + false ), - property.value ?? arkts.factory.createUndefinedLiteral(), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC ), - arkts.factory.createBooleanLiteral(allowOverride), - ]; - judgeIfAddWatchFunc(args, property); - return arkts.factory.createCallExpression( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + false + ); + } + + /* + * helper for createWatchMethod, generates this.subscribedWatches.xxx + */ + static thisSubscribedWatchesMember(member: string): arkts.MemberExpression { + return arkts.factory.createMemberExpression( arkts.factory.createMemberExpression( arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('addProvidedVar'), + arkts.factory.createIdentifier('subscribedWatches'), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, false ), - property.typeAnnotation ? [property.typeAnnotation.clone()] : undefined, - args + arkts.factory.createIdentifier(member), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false ); } /* - * create `this.initConsume(, , watchFunc)`. + * create ____V1RenderId related members in Observed/Track classes */ - static generateInitConsumeCall( - originalName: string, + static createV1RenderIdMembers(isObservedV2: boolean): arkts.AstNode[] { + const v1RenderId: arkts.ClassProperty = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(ObservedNames.V1_RERENDER_ID), + arkts.factory.createNumericLiteral(0), + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(StateManagementTypes.RENDER_ID_TYPE) + ) + ), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + v1RenderId.setAnnotations([annotation(DecoratorNames.JSONSTRINGIFYIGNORE)]); + collectStateManagementTypeImport(StateManagementTypes.RENDER_ID_TYPE); + const setV1RenderId: arkts.MethodDefinition = factory.setV1RenderId(isObservedV2); + return isObservedV2 ? [setV1RenderId] : [v1RenderId, setV1RenderId]; + } + + /* + * helper for createV1RenderIdMembers to generate setV1RenderId method + */ + static setV1RenderId(isObservedV2: boolean): arkts.MethodDefinition { + const assignRenderId: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + generateThisBacking(ObservedNames.V1_RERENDER_ID), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + arkts.factory.createIdentifier(ObservedNames.RERENDER_ID) + ) + ); + return UIFactory.createMethodDefinition({ + key: arkts.factory.createIdentifier(ObservedNames.SET_V1_RERENDER_ID), + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + function: { + body: arkts.factory.createBlock(isObservedV2 ? [] : [assignRenderId]), + params: [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + ObservedNames.RERENDER_ID, + UIFactory.createTypeReferenceFromString(StateManagementTypes.RENDER_ID_TYPE) + ), + undefined + ), + ], + returnTypeAnnotation: arkts.factory.createPrimitiveType( + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID + ), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + }, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + }); + } + + /* + * create conditionalAddRef method in Observed/Track classes + */ + static conditionalAddRef(isObservedV2: boolean): arkts.MethodDefinition { + const metaAddRef: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + UIFactory.generateMemberExpression( + arkts.factory.createIdentifier(ObservedNames.META), + ObservedNames.ADD_REF + ), + undefined, + undefined + ) + ); + collectStateManagementTypeImport(StateManagementTypes.MUTABLE_STATE_META); + return UIFactory.createMethodDefinition({ + key: arkts.factory.createIdentifier(ObservedNames.CONDITIONAL_ADD_REF), + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + function: { + body: arkts.factory.createBlock(isObservedV2 ? [metaAddRef] : [factory.shouldAddRef(metaAddRef)]), + params: [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + ObservedNames.META, + UIFactory.createTypeReferenceFromString(StateManagementTypes.MUTABLE_STATE_META) + ), + undefined + ), + ], + returnTypeAnnotation: arkts.factory.createPrimitiveType( + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID + ), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PROTECTED, + }, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PROTECTED, + }); + } + + /* + * helper for conditionalAddRef to generate shouldAddRef method + */ + static shouldAddRef(metaAddRef: arkts.ExpressionStatement): arkts.IfStatement { + const test: arkts.CallExpression = arkts.factory.createCallExpression( + UIFactory.generateMemberExpression( + arkts.factory.createIdentifier(StateManagementTypes.OBSERVE), + ObservedNames.SHOULD_ADD_REF + ), + undefined, + [generateThisBacking(ObservedNames.V1_RERENDER_ID)] + ); + collectStateManagementTypeImport(StateManagementTypes.OBSERVE); + const consequent: arkts.BlockStatement = arkts.factory.createBlock([metaAddRef]); + return arkts.factory.createIfStatement(test, consequent); + } + + /* + * helper to create meta field in classes with only @Observe and no @Track + */ + static createMetaInObservedClass(): arkts.ClassProperty { + collectStateManagementTypeImport(StateManagementTypes.MUTABLE_STATE_META); + const meta = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(StateManagementTypes.META), + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_MUTABLESTATE_META, undefined, [], false), + UIFactory.createTypeReferenceFromString(StateManagementTypes.MUTABLE_STATE_META), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + meta.setAnnotations([annotation(DecoratorNames.JSONSTRINGIFYIGNORE)]); + return meta; + } + + /** + * add `@memo` to the `@Builder` methods in class. + */ + static addMemoToBuilderClassMethod(method: arkts.MethodDefinition): arkts.MethodDefinition { + if (hasDecorator(method, DecoratorNames.BUILDER)) { + removeDecorator(method, DecoratorNames.BUILDER); + addMemoAnnotation(method.scriptFunction); + } + return method; + } + + static createStorageLinkStateValue( property: arkts.ClassProperty, - alias: string - ): arkts.CallExpression { - const args: arkts.Expression[] = [ - arkts.factory.create1StringLiteral(originalName), - arkts.factory.create1StringLiteral(alias), - ]; - judgeIfAddWatchFunc(args, property); - return arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('initConsume'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false + localStorageporpValueStr: string + ): arkts.MemberExpression { + return arkts.factory.createMemberExpression( + arkts.factory.createCallExpression( + arkts.factory.createIdentifier(StateManagementTypes.STORAGE_LINK_STATE), + property.typeAnnotation ? [property.typeAnnotation] : [], + [ + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier('_entry_local_storage_'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + arkts.factory.createStringLiteral(localStorageporpValueStr), + property.value ?? arkts.factory.createUndefinedLiteral(), + ] ), - property.typeAnnotation ? [property.typeAnnotation.clone()] : undefined, - args + arkts.factory.createIdentifier('value'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ); + } + + /** + * wrap interface non-undefined property type `T` to ``. + */ + static wrapInterfacePropertyType(type: arkts.TypeNode, wrapTypeName: StateManagementTypes): arkts.TypeNode { + if (arkts.isETSUnionType(type)) { + return arkts.factory.updateUnionType(type, [ + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(wrapTypeName), + arkts.factory.createTSTypeParameterInstantiation([type.types[0]]) + ) + ), + type.types[1], + ]); + } + return type; + } + + /** + * wrap interface property parameter that has non-undefined type `T` to ``. + */ + static wrapInterfacePropertyParamExpr( + param: arkts.Expression, + wrapTypeName: StateManagementTypes + ): arkts.Expression { + if (!arkts.isEtsParameterExpression(param)) { + return param; + } + if (!param.type || !arkts.isETSUnionType(param.type)) { + return param; + } + return arkts.factory.updateParameterDeclaration( + param, + arkts.factory.createIdentifier( + param.identifier.name, + factory.wrapInterfacePropertyType(param.type, wrapTypeName) + ), + param.initializer + ); + } + + static wrapStateManagementTypeToType( + type: arkts.TypeNode | undefined, + decoratorName: DecoratorNames + ): arkts.TypeNode | undefined { + let newType: arkts.TypeNode | undefined; + let wrapTypeName: StateManagementTypes | undefined; + if (!!type && !!(wrapTypeName = DECORATOR_TYPE_MAP.get(decoratorName))) { + newType = factory.wrapInterfacePropertyType(type, wrapTypeName); + collectStateManagementTypeImport(wrapTypeName); + } + return newType; + } + + static wrapStateManagementTypeToParam( + param: arkts.Expression | undefined, + decoratorName: DecoratorNames + ): arkts.Expression | undefined { + let newParam: arkts.Expression | undefined; + let wrapTypeName: StateManagementTypes | undefined; + if (!!param && !!(wrapTypeName = DECORATOR_TYPE_MAP.get(decoratorName))) { + newParam = factory.wrapInterfacePropertyParamExpr(param, wrapTypeName); + collectStateManagementTypeImport(wrapTypeName); + } + return newParam; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to ` | undefined`, where `` is getting from `DecoratorName`; + * + * @param method expecting getter with decorator annotation and a setter with decorator annotation in the overloads. + */ + static wrapStateManagementTypeToMethodInInterface( + method: arkts.MethodDefinition, + decorator: DecoratorNames + ): arkts.MethodDefinition { + if (method.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET) { + const newType: arkts.TypeNode | undefined = factory.wrapStateManagementTypeToType( + method.scriptFunction.returnTypeAnnotation, + decorator + ); + const newOverLoads = method.overloads.map((overload) => { + if (arkts.isMethodDefinition(overload)) { + return factory.wrapStateManagementTypeToMethodInInterface(overload, decorator); + } + return overload; + }); + method.setOverloads(newOverLoads); + removeDecorator(method, decorator); + if (!!newType) { + method.scriptFunction.setReturnTypeAnnotation(newType); + } + } else if (method.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET) { + const newParam: arkts.Expression | undefined = factory.wrapStateManagementTypeToParam( + method.scriptFunction.params.at(0), + decorator + ); + removeDecorator(method, decorator); + if (!!newParam) { + return UIFactory.updateMethodDefinition(method, { function: { params: [newParam] } }); + } + } + return method; + } + + /** + * create external assignment node, e.g. `initializers?. ?? .value` or `initializers!.!`. + * + * @param property class property node. + * @param propertyType class property type. + * @param originalName property name. + */ + static generateInitializeValue( + property: arkts.ClassProperty, + propertyType: arkts.TypeNode | undefined, + originalName: string + ): arkts.Expression { + const outInitialize: arkts.Expression = factory.createBlockStatementForOptionalExpression( + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), + originalName + ); + const binaryItem: arkts.Expression = arkts.factory.createBinaryExpression( + outInitialize, + property.value ?? arkts.factory.createUndefinedLiteral(), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING + ); + const finalBinary: arkts.Expression = property.typeAnnotation + ? binaryItem + : arkts.factory.createTSAsExpression(binaryItem, propertyType, false); + return property.value ? finalBinary : factory.generateDefiniteInitializers(propertyType, originalName); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to ` | undefined`, where `` is getting from `DecoratorName`; + * + * @param property expecting property with decorator annotation. + */ + static wrapStateManagementTypeToPropertyInInterface( + property: arkts.ClassProperty, + decorator: DecoratorNames + ): arkts.ClassProperty { + const newType: arkts.TypeNode | undefined = factory.wrapStateManagementTypeToType( + property.typeAnnotation, + decorator + ); + removeDecorator(property, decorator); + if (!!newType) { + property.setTypeAnnotation(newType); + } + return property; + } + + static generateinitAssignment( + monitorItem: string[] | undefined, + originalName: string, + newName: string + ): arkts.ExpressionStatement { + const thisValue: arkts.Expression = generateThisBacking(newName, false, false); + const right: arkts.CallExpression = factory.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_MONITOR, + undefined, + [this.generatePathArg(monitorItem), this.generateLambdaArg(originalName)], + false + ); + return arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + thisValue, + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + right + ) + ); + } + + static generatePathArg(monitorItem: string[] | undefined): arkts.ArrayExpression { + if (!monitorItem || monitorItem.length <= 0) { + return arkts.factory.createArrayExpression([]); + } + const params = monitorItem.map((itemName: string) => { + return factory.createMonitorPathsInfoParameter(itemName); + }); + return arkts.factory.createArrayExpression(params); + } + + static generateLambdaArg(originalName: string): arkts.ArrowFunctionExpression { + return arkts.factory.createArrowFunction( + UIFactory.createScriptFunction({ + params: [UIFactory.createParameterDeclaration(MonitorNames.M_PARAM, MonitorNames.I_MONITOR)], + body: arkts.factory.createBlock([ + arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression(generateThisBacking(originalName), undefined, [ + arkts.factory.createIdentifier(MonitorNames.M_PARAM), + ]) + ), + ]), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + }) + ); + } + + static generateMonitorVariable(itemNameSplit: string[]): arkts.Expression { + const objectFirst: arkts.Expression = generateThisBacking(itemNameSplit[0]); + if (itemNameSplit.length === 1) { + return objectFirst; + } + itemNameSplit.shift(); + return this.recursiveCreateOptionalMember(objectFirst, itemNameSplit); + } + + /** + * recursively create member expression with node and property name in . + * + * @param typeAnnotation expecting property's original type annotation. + */ + static recursiveCreateOptionalMember(object: arkts.Expression, resNameArr: string[]): arkts.Expression { + if (resNameArr.length <= 0) { + return object; + } + const optionalInfo: OptionalMemberInfo = { isNumeric: false }; + if (isNumeric(resNameArr[0])) { + optionalInfo.isNumeric = true; + } + const newMember: arkts.Expression = this.createBlockStatementForOptionalExpression( + object, + resNameArr[0], + optionalInfo + ); + resNameArr.shift(); + return this.recursiveCreateOptionalMember(newMember, resNameArr); + } + + /** + * create IMonitorPathsInfo type parameter `{ path: "", lambda: () => { return this. } }`. + * + * @param monitorItem monitored property name. + */ + static createMonitorPathsInfoParameter(monitorItem: string): arkts.ObjectExpression { + const itemNameSplit: string[] = monitorItem.split('.'); + let monitorVariable: arkts.Expression = arkts.factory.createUndefinedLiteral(); + if (itemNameSplit.length > 0) { + monitorVariable = this.generateMonitorVariable(itemNameSplit); + } + return arkts.factory.createObjectExpression( + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [ + arkts.factory.createProperty( + arkts.factory.createIdentifier(MonitorNames.PATH), + arkts.factory.create1StringLiteral(monitorItem) + ), + arkts.factory.createProperty( + arkts.factory.createIdentifier(MonitorNames.VALUE_CALL_CACK), + arkts.factory.createArrowFunction( + UIFactory.createScriptFunction({ + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + body: arkts.factory.createBlock([arkts.factory.createReturnStatement(monitorVariable)]), + returnTypeAnnotation: UIFactory.createTypeReferenceFromString(TypeNames.ANY), + }) + ) + ), + ], + false ); } } diff --git a/arkui-plugins/ui-plugins/property-translators/index.ts b/arkui-plugins/ui-plugins/property-translators/index.ts index 013eaa286..914006824 100644 --- a/arkui-plugins/ui-plugins/property-translators/index.ts +++ b/arkui-plugins/ui-plugins/property-translators/index.ts @@ -15,71 +15,272 @@ import * as arkts from '@koalaui/libarkts'; -import { PropertyTranslator } from './base'; -import { DecoratorNames, hasDecorator } from './utils'; -import { StateTranslator } from './state'; -import { PropTranslator } from './prop'; -import { StorageLinkTranslator } from './storagelink'; -import { LocalStorageLinkTranslator } from './localstoragelink'; -import { LinkTranslator } from './link'; -import { ObjectLinkTranslator } from './objectlink'; -import { LocalStoragePropTranslator } from './localstorageprop'; -import { regularPropertyTranslator } from './regularProperty'; -import { staticPropertyTranslator } from './staticProperty'; -import { isStatic } from '../utils'; -import { StoragePropTranslator } from './storageProp'; -import { ConsumeTranslator } from './consume'; -import { ProvideTranslator } from './provide'; -import { BuilderParamTranslator } from './builderParam'; +import { DecoratorNames } from '../../common/predefines'; +import { InterfacePropertyTranslator, MethodTranslator, PropertyTranslator } from './base'; +import { hasDecorator } from './utils'; +import { StateInterfaceTranslator, StateTranslator } from './state'; +import { PropInterfaceTranslator, PropTranslator } from './prop'; +import { StorageLinkInterfaceTranslator, StorageLinkTranslator } from './storagelink'; +import { LocalStorageLinkInterfaceTranslator, LocalStorageLinkTranslator } from './localstoragelink'; +import { LinkInterfaceTranslator, LinkTranslator } from './link'; +import { ObjectLinkInterfaceTranslator, ObjectLinkTranslator } from './objectlink'; +import { LocalStoragePropInterfaceTranslator, LocalStoragePropTranslator } from './localstorageprop'; +import { RegularInterfaceTranslator, RegularPropertyTranslator } from './regularProperty'; +import { StaticPropertyTranslator } from './staticProperty'; +import { CustomComponentInfo } from '../utils'; +import { StoragePropInterfaceTranslator, StoragePropTranslator } from './storageProp'; +import { ConsumeInterfaceTranslator, ConsumeTranslator } from './consume'; +import { ProvideInterfaceTranslator, ProvideTranslator } from './provide'; +import { BuilderParamInterfaceTranslator, BuilderParamTranslator } from './builderParam'; +import { PropRefInterfaceTranslator, PropRefTranslator } from './propRef'; import { ObservedTrackTranslator } from './observedTrack'; -import { ClassScopeInfo } from 'ui-plugins/checked-transformer'; +import { ClassScopeInfo } from '../struct-translators/utils'; +import { LocalInterfaceTranslator, LocalTranslator } from './local'; +import { StoragePropRefInterfaceTranslator, StoragePropRefTranslator } from './storagePropRef'; +import { LocalStoragePropRefInterfaceTranslator, LocalStoragePropRefTranslator } from './localStoragePropRef'; +import { ObservedV2TraceTranslator } from './observedV2Trace'; +import { ParamInterfaceTranslator, ParamTranslator } from './param'; +import { OnceInterfaceTranslator, OnceTranslator } from './once'; +import { ProviderInterfaceTranslator, ProviderTranslator } from './provider'; +import { ConsumerInterfaceTranslator, ConsumerTranslator } from './consumer'; +import { ComputedTranslator } from './computed'; +import { MonitorTranslator } from './monitor'; -export { PropertyTranslator }; +export { PropertyTranslator, InterfacePropertyTranslator }; +export type { ClassScopeInfo }; -export function classifyProperty(member: arkts.AstNode, structName: string): PropertyTranslator | undefined { - if (!arkts.isClassProperty(member)) return undefined; - if (isStatic(member)) return new staticPropertyTranslator(member, structName); +export function classifyStructMembers( + member: arkts.AstNode, + structInfo: CustomComponentInfo +): PropertyTranslator | MethodTranslator | undefined { + if (arkts.isClassProperty(member)) { + return classifyProperty(member, structInfo); + } else if (arkts.isMethodDefinition(member)) { + return classifyMethod(member, true, structInfo.name); + } + return undefined; +} + +export function classifyProperty( + property: arkts.AstNode, + structInfo: CustomComponentInfo +): PropertyTranslator | undefined { + if (!arkts.isClassProperty(property)) return undefined; + if (StaticPropertyTranslator.canBeStaticTranslate(property)) { + return new StaticPropertyTranslator({ property, structInfo }); + } + let propertyTranslator: PropertyTranslator | undefined = undefined; + + propertyTranslator = classifyV1Property(property, structInfo); + if (!!propertyTranslator) { + return propertyTranslator; + } + + propertyTranslator = classifyV2Property(property, structInfo); + if (!!propertyTranslator) { + return propertyTranslator; + } - if (hasDecorator(member, DecoratorNames.STATE)) { - return new StateTranslator(member, structName); + return new RegularPropertyTranslator({ property, structInfo }); +} + +export function classifyV1Property( + property: arkts.ClassProperty, + structInfo: CustomComponentInfo +): PropertyTranslator | undefined { + if (hasDecorator(property, DecoratorNames.STATE)) { + return new StateTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.STORAGE_LINK)) { + return new StorageLinkTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.LOCAL_STORAGE_LINK)) { + return new LocalStorageLinkTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.LINK)) { + return new LinkTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.OBJECT_LINK)) { + return new ObjectLinkTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.LOCAL_STORAGE_PROP)) { + return new LocalStoragePropTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.LOCAL_STORAGE_PROP_REF)) { + return new LocalStoragePropRefTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.STORAGE_LINK)) { - return new StorageLinkTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.STORAGE_PROP)) { + return new StoragePropTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.LOCAL_STORAGE_LINK)) { - return new LocalStorageLinkTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.STORAGE_PROP_REF)) { + return new StoragePropRefTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.LINK)) { - return new LinkTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.PROP)) { + return new PropTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.OBJECT_LINK)) { - return new ObjectLinkTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.PROP_REF)) { + return new PropRefTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.LOCAL_STORAGE_PROP)) { - return new LocalStoragePropTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.PROVIDE)) { + return new ProvideTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.STORAGE_PROP)) { - return new StoragePropTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.CONSUME)) { + return new ConsumeTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.PROP)) { - return new PropTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.BUILDER_PARAM)) { + return new BuilderParamTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.PROVIDE)) { - return new ProvideTranslator(member, structName); + + return undefined; +} + +export function classifyV2Property( + property: arkts.ClassProperty, + structInfo: CustomComponentInfo +): PropertyTranslator | undefined { + if (hasDecorator(property, DecoratorNames.LOCAL)) { + return new LocalTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.ONCE)) { + return new OnceTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.CONSUME)) { - return new ConsumeTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.PARAM)) { + return new ParamTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.PROVIDER)) { + return new ProviderTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.CONSUMER)) { + return new ConsumerTranslator({ property, structInfo }); + } + + return undefined; +} + +export function classifyPropertyInInterface(property: arkts.AstNode): InterfacePropertyTranslator | undefined { + let interfacePropertyTranslater: InterfacePropertyTranslator | undefined = undefined; + + interfacePropertyTranslater = classifyV1PropertyInInterface(property); + if (!!interfacePropertyTranslater) { + return interfacePropertyTranslater; } - if (hasDecorator(member, DecoratorNames.BUILDER_PARAM)) { - return new BuilderParamTranslator(member, structName); + + interfacePropertyTranslater = classifyV2PropertyInInterface(property); + if (!!interfacePropertyTranslater) { + return interfacePropertyTranslater; } - return new regularPropertyTranslator(member, structName); + if (RegularInterfaceTranslator.canBeTranslated(property)) { + return new RegularInterfaceTranslator({ property }); + } + return undefined; } -export function classifyObservedTrack(member: arkts.AstNode, classScopeInfo: ClassScopeInfo): ObservedTrackTranslator | undefined { - if (!arkts.isClassProperty(member)) { +export function classifyV1PropertyInInterface(property: arkts.AstNode): InterfacePropertyTranslator | undefined { + if (StateInterfaceTranslator.canBeTranslated(property)) { + return new StateInterfaceTranslator({ property }); + } + if (LinkInterfaceTranslator.canBeTranslated(property)) { + return new LinkInterfaceTranslator({ property }); + } + if (PropInterfaceTranslator.canBeTranslated(property)) { + return new PropInterfaceTranslator({ property }); + } + if (PropRefInterfaceTranslator.canBeTranslated(property)) { + return new PropRefInterfaceTranslator({ property }); + } + if (ProvideInterfaceTranslator.canBeTranslated(property)) { + return new ProvideInterfaceTranslator({ property }); + } + if (ConsumeInterfaceTranslator.canBeTranslated(property)) { + return new ConsumeInterfaceTranslator({ property }); + } + if (StoragePropInterfaceTranslator.canBeTranslated(property)) { + return new StoragePropInterfaceTranslator({ property }); + } + if (StorageLinkInterfaceTranslator.canBeTranslated(property)) { + return new StorageLinkInterfaceTranslator({ property }); + } + if (StoragePropRefInterfaceTranslator.canBeTranslated(property)) { + return new StoragePropRefInterfaceTranslator({ property }); + } + if (LocalStoragePropRefInterfaceTranslator.canBeTranslated(property)) { + return new LocalStoragePropRefInterfaceTranslator({ property }); + } + if (BuilderParamInterfaceTranslator.canBeTranslated(property)) { + return new BuilderParamInterfaceTranslator({ property }); + } + if (LocalStoragePropInterfaceTranslator.canBeTranslated(property)) { + return new LocalStoragePropInterfaceTranslator({ property }); + } + if (LocalStorageLinkInterfaceTranslator.canBeTranslated(property)) { + return new LocalStorageLinkInterfaceTranslator({ property }); + } + if (ObjectLinkInterfaceTranslator.canBeTranslated(property)) { + return new ObjectLinkInterfaceTranslator({ property }); + } + return undefined; +} + +export function classifyV2PropertyInInterface(property: arkts.AstNode): InterfacePropertyTranslator | undefined { + if (LocalInterfaceTranslator.canBeTranslated(property)) { + return new LocalInterfaceTranslator({ property }); + } + if (OnceInterfaceTranslator.canBeTranslated(property)) { + return new OnceInterfaceTranslator({ property }); + } + if (ParamInterfaceTranslator.canBeTranslated(property)) { + return new ParamInterfaceTranslator({ property }); + } + if (ProviderInterfaceTranslator.canBeTranslated(property)) { + return new ProviderInterfaceTranslator({ property }); + } + if (ConsumerInterfaceTranslator.canBeTranslated(property)) { + return new ConsumerInterfaceTranslator({ property }); + } + return undefined; +} + +export type ObservedTranslator = ObservedV2TraceTranslator | ObservedTrackTranslator; + +export function classifyObservedClassProperty( + member: arkts.ClassProperty, + classScopeInfo: ClassScopeInfo +): ObservedTranslator | undefined { + if (classScopeInfo.isObservedV2) { + return new ObservedV2TraceTranslator(member, classScopeInfo); + } + if (classScopeInfo.isObserved || classScopeInfo.classHasTrack) { + return new ObservedTrackTranslator(member, classScopeInfo); + } + return undefined; +} + +export function classifyMethod( + member: arkts.AstNode, + isFromStruct: boolean, + className: string +): MethodTranslator | undefined { + if (!arkts.isMethodDefinition(member)) { return undefined; } - return new ObservedTrackTranslator(member, classScopeInfo); -} \ No newline at end of file + if (hasDecorator(member, DecoratorNames.COMPUTED)) { + return new ComputedTranslator(member, { isFromStruct, className }); + } + if (hasDecorator(member, DecoratorNames.MONITOR)) { + return new MonitorTranslator(member, { isFromStruct, className }); + } + return undefined; +} + +export function classifyInObservedClass( + member: arkts.AstNode, + classScopeInfo: ClassScopeInfo +): ObservedTranslator | MethodTranslator | undefined { + if (arkts.isClassProperty(member)) { + return classifyObservedClassProperty(member, classScopeInfo); + } else if (arkts.isMethodDefinition(member)) { + return classifyMethod(member, false, classScopeInfo.className); + } + return undefined; +} diff --git a/arkui-plugins/ui-plugins/property-translators/link.ts b/arkui-plugins/ui-plugins/property-translators/link.ts index eafd90c95..35c50150f 100644 --- a/arkui-plugins/ui-plugins/property-translators/link.ts +++ b/arkui-plugins/ui-plugins/property-translators/link.ts @@ -15,59 +15,67 @@ import * as arkts from '@koalaui/libarkts'; +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; import { generateToRecord, createGetter, createSetter2, generateThisBacking, generateGetOrSetCall, - judgeIfAddWatchFunc, + collectStateManagementTypeImport, + hasDecorator, } from './utils'; -import { PropertyTranslator } from './base'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { backingField, expectName } from '../../common/arkts-utils'; import { factory } from './factory'; -import { createOptionalClassProperty } from '../utils'; +import { PropertyCache } from './cache/propertyCache'; export class LinkTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } generateInitializeStruct(newName: string, originalName: string) { const test = factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), newName ); const args: arkts.Expression[] = [ arkts.factory.create1StringLiteral(originalName), arkts.factory.createTSNonNullExpression( - factory.createNonNullOrOptionalMemberExpression('initializers', newName, false, true) + factory.createNonNullOrOptionalMemberExpression( + CustomComponentNames.COMPONENT_INITIALIZERS_NAME, + newName, + false, + true + ) ), ]; - judgeIfAddWatchFunc(args, this.property); + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.LINK_DECORATED); + collectStateManagementTypeImport(StateManagementTypes.LINK_SOURCE_TYPE); const consequent = arkts.BlockStatement.createBlockStatement([ arkts.factory.createExpressionStatement( arkts.factory.createAssignmentExpression( generateThisBacking(newName, false, false), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - factory.createNewDecoratedInstantiate('LinkDecoratedVariable', this.property.typeAnnotation, args) + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_LINK, this.propertyType, args, true) ) ), ]); @@ -76,27 +84,19 @@ export class LinkTranslator extends PropertyTranslator implements InitializerCon } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - 'LinkDecoratedVariable', + StateManagementTypes.LINK_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( - generateGetOrSetCall(thisValue, 'set') - ); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -117,3 +117,45 @@ export class LinkTranslator extends PropertyTranslator implements InitializerCon return createSetter2(originalName, typeAnnotation, statement); } } + +export class LinkInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.LINK)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.LINK)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `DecoratedV1VariableBase | undefined`. + * + * @param method expecting getter with `@Link` and a setter with `@Link` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.LINK); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `DecoratedV1VariableBase | undefined`. + * + * @param property expecting property with `@Link`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.LINK); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/local.ts b/arkui-plugins/ui-plugins/property-translators/local.ts new file mode 100644 index 000000000..84a76401e --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/local.ts @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + hasDecorator, + collectStateManagementTypeImport, +} from './utils'; +import { + InterfacePropertyTranslator, + InterfacePropertyTypes, + PropertyTranslator, + PropertyTranslatorOptions, +} from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { factory as UIFactory } from '../ui-factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class LocalTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + private isStatic: boolean; + + constructor(options: PropertyTranslatorOptions) { + super(options); + this.isStatic = this.property.isStatic; + } + + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + if (!this.isStatic) { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + } + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field: arkts.ClassProperty = this.createPropertyField(newName, originalName); + const thisValue: arkts.Expression = this.isStatic + ? UIFactory.generateMemberExpression(arkts.factory.createIdentifier(this.structInfo.name), newName) + : generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + + return [field, getter, setter]; + } + + createPropertyField(newName: string, originalName: string): arkts.ClassProperty { + return this.isStatic + ? arkts.factory.createClassProperty( + arkts.factory.createIdentifier(newName), + this.generateInitializeValue(originalName), + factory.createStageManagementType(StateManagementTypes.LOCAL_DECORATED, this.propertyType), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, + false + ) + : factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.LOCAL_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue, false, this.isStatic); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement, this.isStatic); + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + collectStateManagementTypeImport(StateManagementTypes.LOCAL_DECORATED); + const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + this.generateInitializeValue(originalName) + ); + return arkts.factory.createExpressionStatement(assign); + } + + generateInitializeValue(originalName: string): arkts.Expression { + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + this.property.value ?? arkts.factory.createUndefinedLiteral(), + ]; + collectStateManagementTypeImport(StateManagementTypes.LOCAL_DECORATED); + return factory.generateStateMgmtFactoryCall( + this.isStatic ? StateManagementTypes.MAKE_STATIC_LOCAL : StateManagementTypes.MAKE_LOCAL, + this.propertyType, + args, + this.isStatic ? false : true + ); + } +} + +export class LocalInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.LOCAL)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.LOCAL)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `ILocalDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Local` and a setter with `@Local` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.LOCAL); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `ILocalDecoratedVariable | undefined`. + * + * @param property expecting property with `@Local`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.LOCAL); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/localStoragePropRef.ts b/arkui-plugins/ui-plugins/property-translators/localStoragePropRef.ts new file mode 100644 index 000000000..1f1909c45 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/localStoragePropRef.ts @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { + generateToRecord, + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + collectStateManagementTypeImport, + hasDecorator, + getValueInAnnotation, +} from './utils'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class LocalStoragePropRefTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { + const toRecord = generateToRecord(newName, originalName); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); + } + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + const localStoragePropRefValueStr: string | undefined = getValueInAnnotation( + this.property, + DecoratorNames.LOCAL_STORAGE_PROP_REF + ); + if (!localStoragePropRefValueStr) { + throw new Error('LocalStoragePropRef required only one value!!'); + } + + const args: arkts.Expression[] = [ + arkts.factory.createStringLiteral(localStoragePropRefValueStr), + arkts.factory.create1StringLiteral(originalName), + this.property.value ?? arkts.factory.createUndefinedLiteral(), + ]; + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.LOCAL_STORAGE_PROP_REF_DECORATED); + return arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_LOCAL_STORAGE_PROP_REF, + this.propertyType, + args, + true + ) + ); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.LOCAL_STORAGE_PROP_REF_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } +} + +export class LocalStoragePropRefInterfaceTranslator< + T extends InterfacePropertyTypes +> extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.LOCAL_STORAGE_PROP_REF)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.LOCAL_STORAGE_PROP_REF)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `ILocalStoragePropRefDecoratedVariable | undefined`. + * + * @param method expecting getter with `@LocalStoragePropRef` and a setter with `@LocalStoragePropRef` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.LOCAL_STORAGE_PROP_REF); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `ILocalStoragePropRefDecoratedVariable | undefined`. + * + * @param property expecting property with `@LocalStoragePropRef`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.LOCAL_STORAGE_PROP_REF); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/localstoragelink.ts b/arkui-plugins/ui-plugins/property-translators/localstoragelink.ts index a050e9852..da1c0787e 100755 --- a/arkui-plugins/ui-plugins/property-translators/localstoragelink.ts +++ b/arkui-plugins/ui-plugins/property-translators/localstoragelink.ts @@ -16,93 +16,142 @@ import * as arkts from '@koalaui/libarkts'; import { backingField, expectName } from '../../common/arkts-utils'; -import { PropertyTranslator } from './base'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { DecoratorNames, generateToRecord } from './utils'; - -function getLocalStorageLinkValueStr(node: arkts.AstNode): string | undefined { - if (!arkts.isClassProperty(node) || !node.value) return undefined; - return arkts.isStringLiteral(node.value) ? node.value.str : undefined; -} - -function getLocalStorageLinkAnnotationValue(anno: arkts.AnnotationUsage): string | undefined { - const isStorageLinkAnnotation: boolean = - !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.LOCAL_STORAGE_LINK; - - if (isStorageLinkAnnotation && anno.properties.length === 1) { - return getLocalStorageLinkValueStr(anno.properties.at(0)!); - } - return undefined; -} - -function getLocalStorageLinkValueInAnnotation(node: arkts.ClassProperty): string | undefined { - const annotations: readonly arkts.AnnotationUsage[] = node.annotations; - - for (let i = 0; i < annotations.length; i++) { - const anno: arkts.AnnotationUsage = annotations[i]; - const str: string | undefined = getLocalStorageLinkAnnotationValue(anno); - if (!!str) { - return str; - } - } - - return undefined; -} +import { + generateToRecord, + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + collectStateManagementTypeImport, + hasDecorator, + getValueInAnnotation, +} from './utils'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; export class LocalStorageLinkTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { - const localStorageLinkValueStr: string | undefined = getLocalStorageLinkValueInAnnotation(this.property); + const localStorageLinkValueStr: string | undefined = getValueInAnnotation( + this.property, + DecoratorNames.LOCAL_STORAGE_LINK + ); if (!localStorageLinkValueStr) { - throw new Error('LocalStorageLink required only one value!!'); // TODO: replace this with proper error message. + throw new Error('LocalStorageLink required only one value!!'); } - const call = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('StorageLinkState'), - this.property.typeAnnotation ? [this.property.typeAnnotation] : [], - [ - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('_entry_local_storage_'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.factory.createStringLiteral(localStorageLinkValueStr), - this.property.value ?? arkts.factory.createUndefinedLiteral(), - ] - ); - + const args: arkts.Expression[] = [ + arkts.factory.createStringLiteral(localStorageLinkValueStr), + arkts.factory.create1StringLiteral(originalName), + this.property.value ?? arkts.factory.createUndefinedLiteral(), + ]; + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.LOCAL_STORAGE_LINK_DECORATED); return arkts.factory.createAssignmentExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(newName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), + generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - call + factory.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_LOCAL_STORAGE_LINK, + this.propertyType, + args, + true + ) + ); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.LOCAL_STORAGE_LINK_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } +} + +export class LocalStorageLinkInterfaceTranslator< + T extends InterfacePropertyTypes +> extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.LOCAL_STORAGE_LINK)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.LOCAL_STORAGE_LINK)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `MutableState | undefined`. + * + * @param method expecting getter with `@LocalStorageLink` and a setter with `@LocalStorageLink` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.LOCAL_STORAGE_LINK); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `MutableState | undefined`. + * + * @param property expecting property with `@LocalStorageLink`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.LOCAL_STORAGE_LINK); } } diff --git a/arkui-plugins/ui-plugins/property-translators/localstorageprop.ts b/arkui-plugins/ui-plugins/property-translators/localstorageprop.ts index d20d982cd..7b5840e62 100644 --- a/arkui-plugins/ui-plugins/property-translators/localstorageprop.ts +++ b/arkui-plugins/ui-plugins/property-translators/localstorageprop.ts @@ -15,10 +15,13 @@ import * as arkts from '@koalaui/libarkts'; -import { DecoratorNames, generateToRecord } from './utils'; -import { PropertyTranslator } from './base'; -import { GetterSetter, InitializerConstructor } from './types'; import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, StateManagementTypes } from '../../common/predefines'; +import { collectStateManagementTypeImport, generateToRecord, hasDecorator } from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; function getLocalStorageporpValueStr(node: arkts.AstNode): string | undefined { if (!arkts.isClassProperty(node) || !node.value) return undefined; @@ -54,29 +57,55 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); const updateStruct: arkts.AstNode = this.generateUpdateStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - currentStructInfo.updateBody.push(updateStruct); - - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectUpdateStruct(this.structInfo.name, [updateStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.SYNCED_PROPERTY, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); + const member = arkts.factory.createTSNonNullExpression( + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(newName), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ) + ); + const thisValue: arkts.MemberExpression = arkts.factory.createMemberExpression( + member, + arkts.factory.createIdentifier('value'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ); + + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisValue); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisValue); + return [field, getter, setter]; } generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { const localStorageporpValueStr: string | undefined = getLocalStorageporpValueInAnnotation(this.property); if (!localStorageporpValueStr) { - throw new Error('LocalStorageProp required only one value!!'); // TODO: replace this with proper error message. + throw new Error('LocalStorageProp required only one value!!'); } const insideMember = arkts.factory.createMemberExpression( arkts.factory.createThisExpression(), @@ -86,17 +115,18 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In false ); const binaryItem = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('StorageLinkState'), - this.property.typeAnnotation ? [this.property.typeAnnotation] : [], + arkts.factory.createIdentifier(StateManagementTypes.STORAGE_LINK_STATE), + this.propertyType ? [this.propertyType] : [], [ insideMember, arkts.factory.createStringLiteral(localStorageporpValueStr), this.property.value ?? arkts.factory.createUndefinedLiteral(), ] ); + collectStateManagementTypeImport(StateManagementTypes.STORAGE_LINK_STATE); const call = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('propState'), - this.property.typeAnnotation ? [this.property.typeAnnotation] : [], + arkts.factory.createIdentifier(StateManagementTypes.PROP_STATE), + this.propertyType ? [this.propertyType] : [], [ arkts.factory.createMemberExpression( binaryItem, @@ -107,6 +137,7 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In ), ] ); + collectStateManagementTypeImport(StateManagementTypes.PROP_STATE); return arkts.factory.createAssignmentExpression( arkts.factory.createMemberExpression( arkts.factory.createThisExpression(), @@ -123,31 +154,10 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In generateUpdateStruct(newName: string, originalName: string): arkts.AstNode { const localStorageporpValueStr: string | undefined = getLocalStorageporpValueInAnnotation(this.property); if (!localStorageporpValueStr) { - throw new Error('StorageLink required only one value!!'); // TODO: replace this with proper error message. + throw new Error('StorageLink required only one value!!'); } - - const StorageLinkStateValue = arkts.factory.createMemberExpression( - arkts.factory.createCallExpression( - arkts.factory.createIdentifier('StorageLinkState'), - this.property.typeAnnotation ? [this.property.typeAnnotation] : [], - [ - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('_entry_local_storage_'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.factory.createStringLiteral(localStorageporpValueStr), - this.property.value ?? arkts.factory.createUndefinedLiteral(), - ] - ), - arkts.factory.createIdentifier('value'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ); - + const StorageLinkStateValue = factory.createStorageLinkStateValue(this.property, localStorageporpValueStr); + collectStateManagementTypeImport(StateManagementTypes.STORAGE_LINK_STATE); const test = arkts.factory.createMemberExpression( arkts.factory.createThisExpression(), arkts.factory.createIdentifier(newName), @@ -155,7 +165,6 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In false, false ); - const consequent = arkts.BlockStatement.createBlockStatement([ arkts.factory.createExpressionStatement( arkts.factory.createCallExpression( @@ -171,7 +180,50 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In ) ), ]); - return arkts.factory.createExpressionStatement(arkts.factory.createIfStatement(test, consequent)); } } + +export class LocalStoragePropInterfaceTranslator< + T extends InterfacePropertyTypes +> extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.LOCAL_STORAGE_PROP)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.LOCAL_STORAGE_PROP)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `SyncedProperty | undefined`. + * + * @param method expecting getter with `@LocalStorageProp` and a setter with `@LocalStorageProp` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.LOCAL_STORAGE_PROP); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `SyncedProperty | undefined`. + * + * @param property expecting property with `@LocalStorageProp`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.LOCAL_STORAGE_PROP); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/monitor.ts b/arkui-plugins/ui-plugins/property-translators/monitor.ts new file mode 100644 index 000000000..2070b4fd5 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/monitor.ts @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +import { expectName } from '../../common/arkts-utils'; +import { StateManagementTypes } from '../../common/predefines'; +import { monitorField } from '../utils'; +import { collectStateManagementTypeImport, getValueInMonitorAnnotation } from './utils'; +import { MethodTranslator } from './base'; +import { InitializerConstructor } from './types'; +import { factory as UIFactory } from '../ui-factory'; +import { MonitorCache, MonitorInfo } from './cache/monitorCache'; + +export class MonitorTranslator extends MethodTranslator implements InitializerConstructor { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.method.name); + const newName: string = monitorField(originalName); + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const monitorItem: string[] | undefined = getValueInMonitorAnnotation( + this.method.scriptFunction.annotations + ); + const monitorPathsStr: string = !!monitorItem ? monitorItem.join(',') : ''; + const monitorInfo: MonitorInfo = { + monitorItem: monitorItem, + originalName: originalName, + newName: newName, + }; + MonitorCache.getInstance().collectMonitors(this.classInfo.className, monitorPathsStr, monitorInfo); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + collectStateManagementTypeImport(StateManagementTypes.MONITOR_DECORATED); + const field: arkts.ClassProperty = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(newName), + undefined, + arkts.factory.createUnionType([ + UIFactory.createTypeReferenceFromString(StateManagementTypes.MONITOR_DECORATED), + arkts.factory.createETSUndefinedType(), + ]), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + + return [field]; + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/objectlink.ts b/arkui-plugins/ui-plugins/property-translators/objectlink.ts index 8da1283a2..0e5a4b488 100644 --- a/arkui-plugins/ui-plugins/property-translators/objectlink.ts +++ b/arkui-plugins/ui-plugins/property-translators/objectlink.ts @@ -15,121 +15,82 @@ import * as arkts from '@koalaui/libarkts'; -import { - createGetter, - DecoratorNames, - generateGetOrSetCall, - generateThisBacking, - generateToRecord, - hasDecorator, - judgeIfAddWatchFunc, -} from './utils'; -import { PropertyTranslator } from './base'; -import { GetterSetter, InitializerConstructor } from './types'; import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; +import { createGetter, generateGetOrSetCall, generateThisBacking, generateToRecord, hasDecorator } from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; import { factory } from './factory'; -import { createOptionalClassProperty } from '../utils'; +import { PropertyCache } from './cache/propertyCache'; export class ObjectLinkTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - if (!this.ifObservedDecoratedClass()) { - throw new Error('@ObjectLink decorated property only accepts @Observed decorated class instance'); // TODO: replace this with proper error message. - } - - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); const updateStruct: arkts.AstNode = this.generateUpdateStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - currentStructInfo.updateBody.push(updateStruct); - - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectUpdateStruct(this.structInfo.name, [updateStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { - const initializers = arkts.factory.createTSNonNullExpression( + const initializers = arkts.factory.createTSAsExpression( factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), originalName - ) - ); - - const args: arkts.Expression[] = [arkts.factory.create1StringLiteral(originalName), initializers]; - judgeIfAddWatchFunc(args, this.property); - - const newClass = arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('ObjectLinkDecoratedVariable'), - arkts.factory.createTSTypeParameterInstantiation( - this.property.typeAnnotation ? [this.property.typeAnnotation] : [] - ) - ) ), - args + this.propertyType, + false ); + const args: arkts.Expression[] = [arkts.factory.create1StringLiteral(originalName), initializers]; + factory.judgeIfAddWatchFunc(args, this.property); return arkts.factory.createAssignmentExpression( generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - newClass + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_OBJECT_LINK, this.propertyType, args, true) ); } generateUpdateStruct(newName: string, originalName: string): arkts.AstNode { - const binaryItem = arkts.factory.createBinaryExpression( - factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), - originalName - ), - arkts.factory.createUndefinedLiteral(), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NOT_STRICT_EQUAL - ); const member: arkts.MemberExpression = arkts.factory.createMemberExpression( generateThisBacking(newName, false, true), - arkts.factory.createIdentifier('update'), + arkts.factory.createIdentifier(StateManagementTypes.UPDATE), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, false ); const nonNullItem = arkts.factory.createTSNonNullExpression( - factory.createNonNullOrOptionalMemberExpression('initializers', originalName, false, true) - ); - return arkts.factory.createIfStatement( - binaryItem, - arkts.factory.createBlock([ - arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression(member, undefined, [nonNullItem]) - ), - ]) + factory.createNonNullOrOptionalMemberExpression( + CustomComponentNames.COMPONENT_INITIALIZERS_NAME, + originalName, + false, + true + ) ); + return factory.createIfInUpdateStruct(originalName, member, [nonNullItem]); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - 'ObjectLinkDecoratedVariable', + StateManagementTypes.OBJECT_LINK_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); return [field, getter]; } @@ -140,14 +101,46 @@ export class ObjectLinkTranslator extends PropertyTranslator implements Initiali ): arkts.MethodDefinition { return createGetter(originalName, typeAnnotation, returnValue); } +} + +export class ObjectLinkInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } - ifObservedDecoratedClass(): boolean { - if (this.property.typeAnnotation && arkts.isETSTypeReference(this.property.typeAnnotation)) { - const decl = arkts.getDecl(this.property.typeAnnotation.part?.name!); - if (arkts.isClassDefinition(decl!) && hasDecorator(decl, DecoratorNames.OBSERVED)) { - return true; - } + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.OBJECT_LINK)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.OBJECT_LINK)) { + return true; } return false; } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `ObjectLinkDecoratedVariable | undefined`. + * + * @param method expecting getter with `@ObjectLink` and a setter with `@ObjectLink` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.OBJECT_LINK); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `ObjectLinkDecoratedVariable | undefined`. + * + * @param property expecting property with `@ObjectLink`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.OBJECT_LINK); + } } diff --git a/arkui-plugins/ui-plugins/property-translators/observedTrack.ts b/arkui-plugins/ui-plugins/property-translators/observedTrack.ts index 036132553..84cc39257 100644 --- a/arkui-plugins/ui-plugins/property-translators/observedTrack.ts +++ b/arkui-plugins/ui-plugins/property-translators/observedTrack.ts @@ -14,141 +14,144 @@ */ import * as arkts from '@koalaui/libarkts'; -import { backingField, expectName } from '../../common/arkts-utils'; -import { DecoratorNames, hasDecorator } from './utils'; -import { ClassScopeInfo } from 'ui-plugins/checked-transformer'; - -export class ObservedTrackTranslator { - constructor(protected property: arkts.ClassProperty, protected classScopeInfo: ClassScopeInfo) {} - - private hasImplement: boolean = expectName(this.property.key).startsWith(''); - private isTracked: boolean = hasDecorator(this.property, DecoratorNames.TRACK); +import { annotation, backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, StateManagementTypes, ObservedNames } from '../../common/predefines'; +import { ObservedPropertyTranslator } from './base'; +import { + collectStateManagementTypeImport, + generateThisBacking, + hasDecorator, + hasDecoratorName, + removeDecorator, + removeImplementProperty, +} from './utils'; +import { ClassScopeInfo } from '../struct-translators/utils'; +import { factory } from './factory'; +import { factory as uiFactory } from '../ui-factory'; + +export class ObservedTrackTranslator extends ObservedPropertyTranslator { + private hasImplement: boolean; + private isTracked: boolean; + + constructor(property: arkts.ClassProperty, classScopeInfo: ClassScopeInfo) { + super(property, classScopeInfo); + this.hasImplement = expectName(this.property.key).startsWith(ObservedNames.PROPERTY_PREFIX); + this.isTracked = hasDecorator(this.property, DecoratorNames.TRACK); + } translateMember(): arkts.AstNode[] { if (!this.isTracked && (this.classScopeInfo.classHasTrack || !this.classScopeInfo.isObserved)) { return [this.property]; } const originalName: string = this.hasImplement - ? this.removeImplementProperty(expectName(this.property.key)) + ? removeImplementProperty(expectName(this.property.key)) : expectName(this.property.key); const newName: string = backingField(originalName); - let properyIsClass = false; - - if (this.property.typeAnnotation && arkts.isETSTypeReference(this.property.typeAnnotation)) { - const decl = arkts.getDecl(this.property.typeAnnotation.part?.name!); - if (arkts.isClassDefinition(decl!)) { - properyIsClass = true; - } - } - const field = this.createField(originalName, newName, properyIsClass); - - this.transformGetterSetter(originalName, newName, properyIsClass); - + const field = this.createField(originalName, newName); + this.transformGetterSetter(originalName, newName); return [...field]; } - createField(originalName: string, newName: string, properyIsClass: boolean): arkts.ClassProperty[] { - const backingField = properyIsClass - ? this.propertyIsClassField(newName) - : arkts.factory.createClassProperty( - arkts.factory.createIdentifier(newName), - this.property.value, - this.property.typeAnnotation, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, - false - ); + createField(originalName: string, newName: string): arkts.ClassProperty[] { + const backingField = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(newName), + this.property.value, + this.propertyType, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + if (!this.property.value) { + backingField.modifiers |= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL; + } + const annotations: arkts.AnnotationUsage[] = [...this.property.annotations]; + if ( + !hasDecoratorName(this.property, DecoratorNames.JSONSTRINGIFYIGNORE) && + !hasDecoratorName(this.property, DecoratorNames.JSONRENAME) + ) { + annotations.push( + annotation(DecoratorNames.JSONRENAME).addProperty( + arkts.factory.createClassProperty( + arkts.factory.createIdentifier(ObservedNames.NEW_NAME), + arkts.factory.createStringLiteral(originalName), + undefined, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + false + ) + ) + ); + } + backingField.setAnnotations(annotations); + removeDecorator(backingField, DecoratorNames.TRACK); if (!this.isTracked) { return [backingField]; } const metaField = this.metaField(originalName); + metaField.setAnnotations([annotation(DecoratorNames.JSONSTRINGIFYIGNORE)]); return [backingField, metaField]; } - createGetter(originalName: string, newName: string, properyIsClass: boolean): arkts.MethodDefinition { - const ifRefDepth: arkts.IfStatement = this.getterIfRefDepth(originalName); - const returnMember: arkts.ReturnStatement = this.getterReturnMember(properyIsClass, newName); - const setObservationDepth = this.getterSetObservationDepth(newName); - - const body = arkts.factory.createBlock([ - ifRefDepth, - ...(properyIsClass ? [setObservationDepth] : []), - returnMember, - ]); - - const scriptFunction = arkts.factory.createScriptFunction( - body, - arkts.FunctionSignature.createFunctionSignature(undefined, [], this.property.typeAnnotation, false), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_GETTER, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC - ); - - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET, - arkts.factory.createIdentifier(originalName), - scriptFunction, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, - false + createGetter(originalName: string, newName: string): arkts.MethodDefinition { + const conditionalAddRef = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression(generateThisBacking(ObservedNames.CONDITIONAL_ADD_REF), undefined, [ + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + this.metaIdentifier(originalName), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + ]) ); + const backingMember: arkts.Expression = generateThisBacking(newName); + const returnMember: arkts.ReturnStatement = arkts.factory.createReturnStatement( + this.property.value + ? backingMember + : arkts.factory.createTSAsExpression(backingMember, this.propertyType, false) + ); + const body = arkts.factory.createBlock([conditionalAddRef, returnMember]); + return uiFactory.createMethodDefinition({ + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET, + key: arkts.factory.createIdentifier(originalName), + function: { + body: body, + returnTypeAnnotation: this.propertyType, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_GETTER, + }, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + }); } - createSetter(originalName: string, newName: string, properyIsClass: boolean): arkts.MethodDefinition { - const ifEqualsNewValue: arkts.IfStatement = this.setterIfEqualsNewValue(properyIsClass, originalName, newName); + createSetter(originalName: string, newName: string): arkts.MethodDefinition { + const ifEqualsNewValue: arkts.IfStatement = this.setterIfEqualsNewValue(originalName, newName); const body = arkts.factory.createBlock([ifEqualsNewValue]); const param = arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier('newValue', this.property.typeAnnotation), + arkts.factory.createIdentifier(ObservedNames.NEW_VALUE, this.propertyType), undefined ); - const scriptFunction = arkts.factory.createScriptFunction( - body, - arkts.FunctionSignature.createFunctionSignature(undefined, [param], undefined, false), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_SETTER, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC - ); - - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET, - arkts.factory.createIdentifier(originalName), - scriptFunction, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, - false - ); - } - - genThisBacking(newName: string): arkts.MemberExpression { - return arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(newName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ); - } - - genThisBackingValue(newName: string): arkts.MemberExpression { - return arkts.factory.createMemberExpression( - this.genThisBacking(newName), - arkts.factory.createIdentifier('value'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ); + return uiFactory.createMethodDefinition({ + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET, + key: arkts.factory.createIdentifier(originalName), + function: { + body: body, + params: [param], + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_SETTER, + }, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + }); } metaIdentifier(originalName: string): arkts.Identifier { return this.isTracked - ? arkts.factory.createIdentifier(`__meta_${originalName}`) - : arkts.factory.createIdentifier('__meta'); - } - - removeImplementProperty(originalName: string): string { - const prefix = ''; - return originalName.substring(prefix.length); + ? arkts.factory.createIdentifier(`${StateManagementTypes.META}_${originalName}`) + : arkts.factory.createIdentifier(StateManagementTypes.META); } - transformGetterSetter(originalName: string, newName: string, properyIsClass: boolean): void { - const newGetter = this.createGetter(originalName, newName, properyIsClass); - const newSetter = this.createSetter(originalName, newName, properyIsClass); + transformGetterSetter(originalName: string, newName: string): void { + const newGetter = this.createGetter(originalName, newName); + const newSetter = this.createSetter(originalName, newName); if (this.hasImplement) { { const idx: number = this.classScopeInfo.getters.findIndex( @@ -181,124 +184,25 @@ export class ObservedTrackTranslator { } } - propertyIsClassField(newName: string): arkts.ClassProperty { - return arkts.factory.createClassProperty( - arkts.factory.createIdentifier(newName), - this.property.value - ? arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('BackingValue'), - arkts.factory.createTSTypeParameterInstantiation( - this.property.typeAnnotation ? [this.property.typeAnnotation] : [] - ) - ) - ), - [this.property.value] - ) - : undefined, - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('BackingValue'), - arkts.factory.createTSTypeParameterInstantiation( - this.property.typeAnnotation ? [this.property.typeAnnotation] : [] - ) - ) - ), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, - false - ); - } - metaField(originalName: string): arkts.ClassProperty { + collectStateManagementTypeImport(StateManagementTypes.MUTABLE_STATE_META); return arkts.factory.createClassProperty( - arkts.factory.createIdentifier(`__meta_${originalName}`), - arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('MutableStateMeta')) - ), - [arkts.factory.createStringLiteral('@Track')] - ), - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('MutableStateMeta')) - ), + arkts.factory.createIdentifier(`${StateManagementTypes.META}_${originalName}`), + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_MUTABLESTATE_META, undefined, [], false), + uiFactory.createTypeReferenceFromString(StateManagementTypes.MUTABLE_STATE_META), arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, false ); } - getterIfRefDepth(originalName: string): arkts.IfStatement { - return arkts.factory.createIfStatement( - arkts.factory.createBinaryExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('_permissibleAddRefDepth'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.factory.createNumericLiteral(0), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_GREATER_THAN - ), - arkts.factory.createBlock([ - arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - this.metaIdentifier(originalName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.factory.createIdentifier('addRef'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - undefined, - false, - false - ) - ), - ]) - ); - } - - getterSetObservationDepth(newName: string): arkts.ExpressionStatement { - return arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression(arkts.factory.createIdentifier('setObservationDepth'), undefined, [ - this.genThisBackingValue(newName), - arkts.factory.createBinaryExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('_permissibleAddRefDepth'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.factory.createNumericLiteral(1), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_MINUS - ), - ]) - ); - } - - getterReturnMember(properyIsClass: boolean, newName: string): arkts.ReturnStatement { - return arkts.factory.createReturnStatement( - properyIsClass ? this.genThisBackingValue(newName) : this.genThisBacking(newName) - ); - } - - setterIfEqualsNewValue(properyIsClass: boolean, originalName: string, newName: string): arkts.IfStatement { - const backingValue = properyIsClass ? this.genThisBackingValue(newName) : this.genThisBacking(newName); + setterIfEqualsNewValue(originalName: string, newName: string): arkts.IfStatement { + const backingValue = generateThisBacking(newName); const setNewValue = arkts.factory.createExpressionStatement( arkts.factory.createAssignmentExpression( backingValue, arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - arkts.factory.createIdentifier('newValue') + arkts.factory.createIdentifier(ObservedNames.NEW_VALUE) ) ); @@ -312,7 +216,7 @@ export class ObservedTrackTranslator { false, false ), - arkts.factory.createIdentifier('fireChange'), + arkts.factory.createIdentifier(ObservedNames.FIRE_CHANGE), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, false @@ -323,23 +227,15 @@ export class ObservedTrackTranslator { ); const subscribingWatches = arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('executeOnSubscribingWatches'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [arkts.factory.createStringLiteral(originalName)] - ) + arkts.factory.createCallExpression(generateThisBacking(ObservedNames.EXECUATE_WATCHES), undefined, [ + arkts.factory.createStringLiteral(originalName), + ]) ); return arkts.factory.createIfStatement( arkts.factory.createBinaryExpression( backingValue, - arkts.factory.createIdentifier('newValue'), + arkts.factory.createIdentifier(ObservedNames.NEW_VALUE), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NOT_STRICT_EQUAL ), arkts.factory.createBlock([setNewValue, fireChange, subscribingWatches]) diff --git a/arkui-plugins/ui-plugins/property-translators/observedV2Trace.ts b/arkui-plugins/ui-plugins/property-translators/observedV2Trace.ts new file mode 100644 index 000000000..673184f74 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/observedV2Trace.ts @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { annotation, backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, ObservedNames, StateManagementTypes } from '../../common/predefines'; +import { ObservedPropertyTranslator } from './base'; +import { + collectStateManagementTypeImport, + generateThisBacking, + hasDecorator, + hasDecoratorName, + removeDecorator, + removeImplementProperty, +} from './utils'; +import { ClassScopeInfo } from '../struct-translators/utils'; +import { factory } from './factory'; +import { factory as uiFactory } from '../ui-factory'; +import { ImportCollector } from '../../common/import-collector'; + +export class ObservedV2TraceTranslator extends ObservedPropertyTranslator { + private hasImplement: boolean; + private isTraced: boolean; + private isStatic: boolean; + + constructor(property: arkts.ClassProperty, classScopeInfo: ClassScopeInfo) { + super(property, classScopeInfo); + this.hasImplement = expectName(this.property.key).startsWith(ObservedNames.PROPERTY_PREFIX); + this.isTraced = hasDecorator(this.property, DecoratorNames.TRACE); + this.isStatic = arkts.hasModifierFlag(this.property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC); + } + + translateMember(): arkts.AstNode[] { + if (!this.isTraced) { + return [this.property]; + } + const originalName: string = this.hasImplement + ? removeImplementProperty(expectName(this.property.key)) + : expectName(this.property.key); + const newName: string = backingField(originalName); + const field = this.createField(originalName, newName); + this.transformGetterSetter(originalName, newName); + return [...field]; + } + + createField(originalName: string, newName: string): arkts.ClassProperty[] { + const backingField = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(newName), + this.property.value, + this.propertyType, + this.isStatic + ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC + : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + if (!this.property.value) { + backingField.modifiers |= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL; + } + const annotations: arkts.AnnotationUsage[] = [...this.property.annotations]; + if ( + !hasDecoratorName(this.property, DecoratorNames.JSONSTRINGIFYIGNORE) && + !hasDecoratorName(this.property, DecoratorNames.JSONRENAME) + ) { + annotations.push( + annotation(DecoratorNames.JSONRENAME).addProperty( + arkts.factory.createClassProperty( + arkts.factory.createIdentifier(ObservedNames.NEW_NAME), + arkts.factory.createStringLiteral(originalName), + undefined, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + false + ) + ) + ); + } + backingField.setAnnotations(annotations); + removeDecorator(backingField, DecoratorNames.TRACE); + const metaField = this.metaField(originalName); + metaField.setAnnotations([annotation(DecoratorNames.JSONSTRINGIFYIGNORE)]); + return [backingField, metaField]; + } + + createGetter( + originalName: string, + newName: string, + methodModifier: arkts.Es2pandaModifierFlags + ): arkts.MethodDefinition { + const classIdent: arkts.Identifier = arkts.factory.createIdentifier(this.classScopeInfo.className); + ImportCollector.getInstance().collectImport(StateManagementTypes.UI_UTILS); + const observedMember: arkts.Expression = this.isStatic + ? uiFactory.generateMemberExpression(classIdent.clone(), newName) + : generateThisBacking(newName); + const returnMember: arkts.ReturnStatement = arkts.factory.createReturnStatement( + arkts.factory.createCallExpression( + uiFactory.generateMemberExpression( + arkts.factory.createIdentifier(StateManagementTypes.UI_UTILS), + StateManagementTypes.MAKE_OBSERVED + ), + undefined, + [ + this.property.value + ? observedMember + : arkts.factory.createTSAsExpression(observedMember, this.propertyType, false), + ] + ) + ); + const body = arkts.factory.createBlock([this.createAddRef(originalName, classIdent), returnMember]); + return uiFactory.createMethodDefinition({ + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET, + key: arkts.factory.createIdentifier(originalName), + function: { + body: body, + returnTypeAnnotation: this.propertyType, + modifiers: methodModifier, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_GETTER, + }, + modifiers: methodModifier, + }); + } + + createSetter( + originalName: string, + newName: string, + methodModifier: arkts.Es2pandaModifierFlags + ): arkts.MethodDefinition { + const ifEqualsNewValue: arkts.IfStatement = this.setterIfEqualsNewValue(originalName, newName); + const body = arkts.factory.createBlock([ifEqualsNewValue]); + const param = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(ObservedNames.NEW_VALUE, this.propertyType), + undefined + ); + + return uiFactory.createMethodDefinition({ + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET, + key: arkts.factory.createIdentifier(originalName), + function: { + body: body, + params: [param], + modifiers: methodModifier, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_SETTER, + }, + modifiers: methodModifier, + }); + } + + transformGetterSetter(originalName: string, newName: string): void { + const methodModifier = this.isStatic + ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC + : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; + const newGetter = this.createGetter(originalName, newName, methodModifier); + const newSetter = this.createSetter(originalName, newName, methodModifier); + if (this.hasImplement) { + { + const idx: number = this.classScopeInfo.getters.findIndex( + (getter) => getter.name.name === originalName + ); + const originGetter: arkts.MethodDefinition = this.classScopeInfo.getters[idx]; + const originSetter: arkts.MethodDefinition = originGetter.overloads[0]; + const updateGetter: arkts.MethodDefinition = arkts.factory.updateMethodDefinition( + originGetter, + originGetter.kind, + newGetter.name, + newGetter.scriptFunction.addFlag(arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD), + originGetter.modifiers, + false + ); + arkts.factory.updateMethodDefinition( + originSetter, + originSetter.kind, + newSetter.name, + newSetter.scriptFunction + .addFlag(arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_OVERLOAD) + .addFlag(arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD), + originSetter.modifiers, + false + ); + this.classScopeInfo.getters[idx] = updateGetter; + } + } else { + this.classScopeInfo.getters.push(...[newGetter, newSetter]); + } + } + + metaField(originalName: string): arkts.ClassProperty { + collectStateManagementTypeImport(StateManagementTypes.MUTABLE_STATE_META); + return arkts.factory.createClassProperty( + arkts.factory.createIdentifier(`${StateManagementTypes.META}_${originalName}`), + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_MUTABLESTATE_META, undefined, [], false), + uiFactory.createTypeReferenceFromString(StateManagementTypes.MUTABLE_STATE_META), + this.isStatic + ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC + : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + } + + createAddRef(originalName: string, classIdent: arkts.Identifier): arkts.ExpressionStatement { + const metaName: string = `${StateManagementTypes.META}_${originalName}`; + const conditionalAddRef = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression(generateThisBacking(ObservedNames.CONDITIONAL_ADD_REF), undefined, [ + generateThisBacking(metaName), + ]) + ); + const metaAddRef = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + uiFactory.generateMemberExpression( + uiFactory.generateMemberExpression(classIdent.clone(), metaName), + ObservedNames.ADD_REF + ), + undefined, + undefined + ) + ); + return this.isStatic ? metaAddRef : conditionalAddRef; + } + + setterIfEqualsNewValue(originalName: string, newName: string): arkts.IfStatement { + const classIdent: arkts.Identifier = arkts.factory.createIdentifier(this.classScopeInfo.className); + const metaName: string = `${StateManagementTypes.META}_${originalName}`; + const backingValue: arkts.Expression = generateThisBacking(newName); + const metaValue: arkts.Expression = generateThisBacking(metaName); + const staticMetaValue: arkts.Expression = uiFactory.generateMemberExpression(classIdent.clone(), metaName); + const staticBackingValue: arkts.Expression = uiFactory.generateMemberExpression(classIdent.clone(), newName); + const setNewValue = arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + this.isStatic ? staticBackingValue : backingValue, + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + arkts.factory.createIdentifier(ObservedNames.NEW_VALUE) + ) + ); + + const fireChange = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + this.isStatic ? staticMetaValue.clone() : metaValue.clone(), + arkts.factory.createIdentifier(ObservedNames.FIRE_CHANGE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + ); + + const subscribingWatches = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression(generateThisBacking(ObservedNames.EXECUATE_WATCHES), undefined, [ + arkts.factory.createStringLiteral(originalName), + ]) + ); + + const consequentArr = this.isStatic ? [setNewValue, fireChange] : [setNewValue, fireChange, subscribingWatches]; + return arkts.factory.createIfStatement( + arkts.factory.createBinaryExpression( + this.isStatic ? staticBackingValue.clone() : backingValue.clone(), + arkts.factory.createIdentifier(ObservedNames.NEW_VALUE), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NOT_STRICT_EQUAL + ), + arkts.factory.createBlock(consequentArr) + ); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/once.ts b/arkui-plugins/ui-plugins/property-translators/once.ts new file mode 100644 index 000000000..df0468990 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/once.ts @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; +import { + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + hasDecorator, + collectStateManagementTypeImport, +} from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class OnceTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field: arkts.ClassProperty = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.ONCE_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + const outInitialize: arkts.Expression = factory.createBlockStatementForOptionalExpression( + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), + originalName + ); + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + factory.generateInitializeValue(this.property, this.propertyType, originalName), + ]; + collectStateManagementTypeImport(StateManagementTypes.ONCE_DECORATED); + const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_PARAM_ONCE, this.propertyType, args, true) + ); + return arkts.factory.createExpressionStatement(assign); + } +} + +export class OnceInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.ONCE)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.ONCE)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `IParamOnceDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Once` and a setter with `@Once` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.ONCE); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `IParamOnceDecoratedVariable | undefined`. + * + * @param property expecting property with `@Once`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.ONCE); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/param.ts b/arkui-plugins/ui-plugins/property-translators/param.ts new file mode 100644 index 000000000..95caf467a --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/param.ts @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; +import { + createGetter, + generateThisBacking, + generateGetOrSetCall, + hasDecorator, + collectStateManagementTypeImport, +} from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class ParamTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + const updateStruct: arkts.AstNode = this.generateUpdateStruct(generateThisBacking(newName), originalName); + PropertyCache.getInstance().collectUpdateStruct(this.structInfo.name, [updateStruct]); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field: arkts.ClassProperty = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.PARAM_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + + return [field, getter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + factory.generateInitializeValue(this.property, this.propertyType, originalName), + ]; + collectStateManagementTypeImport(StateManagementTypes.PARAM_DECORATED); + const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_PARAM, this.propertyType, args, true) + ); + return arkts.factory.createExpressionStatement(assign); + } + + generateUpdateStruct(mutableThis: arkts.Expression, originalName: string): arkts.AstNode { + const member: arkts.MemberExpression = arkts.factory.createMemberExpression( + arkts.factory.createTSNonNullExpression(mutableThis), + arkts.factory.createIdentifier(StateManagementTypes.UPDATE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ); + return factory.createIfInUpdateStruct(originalName, member, [ + arkts.factory.createTSAsExpression( + factory.createNonNullOrOptionalMemberExpression( + CustomComponentNames.COMPONENT_INITIALIZERS_NAME, + originalName, + false, + true + ), + this.propertyType, + false + ), + ]); + } +} + +export class ParamInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.PARAM)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.PARAM)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `IParamDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Param` and a setter with `@Param` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.PARAM); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `IParamDecoratedVariable | undefined`. + * + * @param property expecting property with `@Param`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.PARAM); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/prop.ts b/arkui-plugins/ui-plugins/property-translators/prop.ts index b8bb3d991..f4e6fbcb1 100644 --- a/arkui-plugins/ui-plugins/property-translators/prop.ts +++ b/arkui-plugins/ui-plugins/property-translators/prop.ts @@ -15,65 +15,58 @@ import * as arkts from '@koalaui/libarkts'; +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; import { - generateToRecord, + collectStateManagementTypeImport, createGetter, createSetter2, generateGetOrSetCall, generateThisBacking, - judgeIfAddWatchFunc, + generateToRecord, + hasDecorator, } from './utils'; -import { PropertyTranslator } from './base'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { backingField, expectName } from '../../common/arkts-utils'; -import { createOptionalClassProperty } from '../utils'; import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; export class PropTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const mutableThis: arkts.Expression = generateThisBacking(newName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); const updateStruct: arkts.AstNode = this.generateUpdateStruct(mutableThis, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - currentStructInfo.updateBody.push(updateStruct); - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectUpdateStruct(this.structInfo.name, [updateStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - 'PropDecoratedVariable', + StateManagementTypes.PROP_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( - generateGetOrSetCall(thisValue, 'set') - ); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -95,73 +88,81 @@ export class PropTranslator extends PropertyTranslator implements InitializerCon } generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { - const binaryItem = arkts.factory.createBinaryExpression( - factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), - originalName - ), - this.property.value ?? arkts.factory.createUndefinedLiteral(), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING - ); const args: arkts.Expression[] = [ arkts.factory.create1StringLiteral(originalName), - this.property.value - ? binaryItem - : arkts.factory.createTSAsExpression( - factory.createNonNullOrOptionalMemberExpression('initializers', originalName, false, true), - this.property.typeAnnotation ? this.property.typeAnnotation.clone() : undefined, - false - ), + factory.generateInitializeValue(this.property, this.propertyType, originalName), ]; - judgeIfAddWatchFunc(args, this.property); - const right = arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('PropDecoratedVariable'), - arkts.factory.createTSTypeParameterInstantiation( - this.property.typeAnnotation ? [this.property.typeAnnotation] : [] - ) - ) - ), - args - ); + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.PROP_DECORATED); const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - right + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_PROP, this.propertyType, args, true) ); return arkts.factory.createExpressionStatement(assign); } generateUpdateStruct(mutableThis: arkts.Expression, originalName: string): arkts.AstNode { - const binaryItem = arkts.factory.createBinaryExpression( - factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), - originalName - ), - arkts.factory.createUndefinedLiteral(), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NOT_STRICT_EQUAL - ); const member: arkts.MemberExpression = arkts.factory.createMemberExpression( arkts.factory.createTSNonNullExpression(mutableThis), - arkts.factory.createIdentifier('update'), + arkts.factory.createIdentifier(StateManagementTypes.UPDATE), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, false ); - return arkts.factory.createIfStatement( - binaryItem, - arkts.factory.createBlock([ - arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression(member, undefined, [ - arkts.factory.createTSAsExpression( - factory.createNonNullOrOptionalMemberExpression('initializers', originalName, false, true), - this.property.typeAnnotation ? this.property.typeAnnotation.clone() : undefined, - false - ), - ]) + return factory.createIfInUpdateStruct(originalName, member, [ + arkts.factory.createTSAsExpression( + factory.createNonNullOrOptionalMemberExpression( + CustomComponentNames.COMPONENT_INITIALIZERS_NAME, + originalName, + false, + true ), - ]) - ); + this.propertyType, + false + ), + ]); + } +} + +export class PropInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.PROP)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.PROP)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `PropDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Prop` and a setter with `@Prop` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.PROP); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `PropDecoratedVariable | undefined`. + * + * @param property expecting property with `@Prop`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.PROP); } } diff --git a/arkui-plugins/ui-plugins/property-translators/propRef.ts b/arkui-plugins/ui-plugins/property-translators/propRef.ts new file mode 100644 index 000000000..750b1a5d9 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/propRef.ts @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; +import { + generateToRecord, + createGetter, + createSetter2, + generateGetOrSetCall, + generateThisBacking, + collectStateManagementTypeImport, + hasDecorator, +} from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class PropRefTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const mutableThis: arkts.Expression = generateThisBacking(newName); + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + const updateStruct: arkts.AstNode = this.generateUpdateStruct(mutableThis, originalName); + PropertyCache.getInstance().collectUpdateStruct(this.structInfo.name, [updateStruct]); + if (!!this.structInfo.annotations?.reusable) { + const toRecord = generateToRecord(newName, originalName); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); + } + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field: arkts.ClassProperty = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.PROP_REF_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + factory.generateInitializeValue(this.property, this.propertyType, originalName), + ]; + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.PROP_REF_DECORATED); + const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_PROP_REF, this.propertyType, args, true) + ); + return arkts.factory.createExpressionStatement(assign); + } + + generateUpdateStruct(mutableThis: arkts.Expression, originalName: string): arkts.AstNode { + const member: arkts.MemberExpression = arkts.factory.createMemberExpression( + arkts.factory.createTSNonNullExpression(mutableThis), + arkts.factory.createIdentifier(StateManagementTypes.UPDATE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ); + return factory.createIfInUpdateStruct(originalName, member, [ + arkts.factory.createTSAsExpression( + factory.createNonNullOrOptionalMemberExpression( + CustomComponentNames.COMPONENT_INITIALIZERS_NAME, + originalName, + false, + true + ), + this.propertyType, + false + ), + ]); + } +} + +export class PropRefInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.PROP_REF)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.PROP_REF)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `IPropRefDecoratedVariable | undefined`. + * + * @param method expecting getter with `@PropRef` and a setter with `@PropRef` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.PROP_REF); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `IPropRefDecoratedVariable | undefined`. + * + * @param property expecting property with `@PropRef`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.PROP_REF); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/provide.ts b/arkui-plugins/ui-plugins/property-translators/provide.ts index 144d1239a..84a84a4f7 100644 --- a/arkui-plugins/ui-plugins/property-translators/provide.ts +++ b/arkui-plugins/ui-plugins/property-translators/provide.ts @@ -15,62 +15,55 @@ import * as arkts from '@koalaui/libarkts'; +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; import { - generateToRecord, createGetter, + generateToRecord, createSetter2, generateThisBacking, generateGetOrSetCall, getValueInProvideAnnotation, ProvideOptions, + hasDecorator, } from './utils'; -import { PropertyTranslator } from './base'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { backingField, expectName } from '../../common/arkts-utils'; -import { createOptionalClassProperty } from '../utils'; import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; export class ProvideTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(originalName, newName); - currentStructInfo.initializeBody.push(initializeStruct); - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - 'ProvideDecoratedVariable', + StateManagementTypes.PROVIDE_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( - generateGetOrSetCall(thisValue, 'set') - ); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -95,11 +88,60 @@ export class ProvideTranslator extends PropertyTranslator implements Initializer const options: undefined | ProvideOptions = getValueInProvideAnnotation(this.property); const alias: string = options?.alias ?? originalName; const allowOverride: boolean = options?.allowOverride ?? false; + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + arkts.factory.create1StringLiteral(alias), + factory.generateInitializeValue(this.property, this.propertyType, originalName), + arkts.factory.createBooleanLiteral(allowOverride), + ]; + factory.judgeIfAddWatchFunc(args, this.property); const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - factory.generateAddProvideVarCall(originalName, this.property, alias, allowOverride) + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_PROVIDE, this.propertyType, args, true) ); return arkts.factory.createExpressionStatement(assign); } } + +export class ProvideInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.PROVIDE)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.PROVIDE)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `ProvideDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Provide` and a setter with `@Provide` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.PROVIDE); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `ProvideDecoratedVariable | undefined`. + * + * @param property expecting property with `@Provide`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.PROVIDE); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/provider.ts b/arkui-plugins/ui-plugins/property-translators/provider.ts new file mode 100644 index 000000000..1b70ff77d --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/provider.ts @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + hasDecorator, + getValueInAnnotation, +} from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class ProviderTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(originalName, newName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field: arkts.ClassProperty = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.PROVIDER_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } + + generateInitializeStruct(originalName: string, newName: string): arkts.AstNode { + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + arkts.factory.create1StringLiteral( + getValueInAnnotation(this.property, DecoratorNames.PROVIDER) ?? originalName + ), + this.property.value!, + ]; + const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_PROVIDER, this.propertyType, args, true) + ); + return arkts.factory.createExpressionStatement(assign); + } +} + +export class ProviderInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.PROVIDER)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.PROVIDER)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `IProviderDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Provider` and a setter with `@Provider` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.PROVIDER); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `IProviderDecoratedVariable | undefined`. + * + * @param property expecting property with `@Provider`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.PROVIDER); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/regularProperty.ts b/arkui-plugins/ui-plugins/property-translators/regularProperty.ts index d89b471e7..9cae2857d 100644 --- a/arkui-plugins/ui-plugins/property-translators/regularProperty.ts +++ b/arkui-plugins/ui-plugins/property-translators/regularProperty.ts @@ -15,37 +15,44 @@ import * as arkts from '@koalaui/libarkts'; -import { createGetter, generateToRecord, generateThisBacking, createSetter2 } from './utils'; -import { PropertyTranslator } from './base'; +import { createGetter, generateToRecord, generateThisBacking, createSetter2, isCustomDialogController } from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { createOptionalClassProperty } from '../utils'; import { backingField, expectName } from '../../common/arkts-utils'; import { factory } from './factory'; +import { CustomComponentNames } from '../utils'; +import { PropertyCache } from './cache/propertyCache'; -export class regularPropertyTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { +export class RegularPropertyTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); - const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - if (currentStructInfo.isReusable) { + const value = this.property.value ?? arkts.factory.createUndefinedLiteral(); + let initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName, value); + if ( + !!this.propertyType && + !!this.structInfo.annotations.customdialog && + isCustomDialogController(this.propertyType) + ) { + initializeStruct = this.generateControllerInit(originalName, initializeStruct); + } + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - '', + undefined, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, false); @@ -53,19 +60,15 @@ export class regularPropertyTranslator extends PropertyTranslator implements Ini arkts.factory.createAssignmentExpression( thisValue, arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - arkts.factory.createIdentifier('value'), + arkts.factory.createIdentifier('value') ) ); const getter: arkts.MethodDefinition = this.translateGetter( originalName, - this.property.typeAnnotation, - arkts.factory.createTSAsExpression(thisValue, this.property.typeAnnotation, false) - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + this.propertyType, + arkts.factory.createTSAsExpression(thisValue, this.propertyType, false) ); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -86,13 +89,13 @@ export class regularPropertyTranslator extends PropertyTranslator implements Ini return createSetter2(originalName, typeAnnotation, statement); } - generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + generateInitializeStruct(newName: string, originalName: string, value: arkts.Expression): arkts.AstNode { const binaryItem = arkts.factory.createBinaryExpression( factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), originalName ), - this.property.value ?? arkts.factory.createUndefinedLiteral(), + value ?? arkts.factory.createUndefinedLiteral(), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING ); const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( @@ -102,4 +105,24 @@ export class regularPropertyTranslator extends PropertyTranslator implements Ini ); return arkts.factory.createExpressionStatement(assign); } + + generateControllerInit(originalName: string, initializeStruct: arkts.AstNode): arkts.AstNode { + return arkts.factory.createIfStatement( + factory.createBlockStatementForOptionalExpression( + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), + originalName + ), + arkts.factory.createBlock([initializeStruct]) + ); + } +} + +export class RegularInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + return true; + } } diff --git a/arkui-plugins/ui-plugins/property-translators/return-transformer.ts b/arkui-plugins/ui-plugins/property-translators/return-transformer.ts new file mode 100644 index 000000000..bdc1a536e --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/return-transformer.ts @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { AbstractVisitor } from '../../common/abstract-visitor'; + +export class ReturnTransformer extends AbstractVisitor { + private _types: arkts.TypeNode[] = []; + + reset(): void { + super.reset(); + this._types = []; + } + + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { + const node = this.visitEachChild(beforeChildren); + if (arkts.isReturnStatement(node) && node.argument) { + const type = arkts.createTypeNodeFromTsType(node.argument); + if (!!type && arkts.isTypeNode(type)) { + this._types.push(type); + } + return node; + } + return node; + } + + get types(): arkts.TypeNode[] { + return this._types; + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/state.ts b/arkui-plugins/ui-plugins/property-translators/state.ts index 667fabac7..20df22709 100644 --- a/arkui-plugins/ui-plugins/property-translators/state.ts +++ b/arkui-plugins/ui-plugins/property-translators/state.ts @@ -15,61 +15,54 @@ import * as arkts from '@koalaui/libarkts'; +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; import { generateToRecord, createGetter, createSetter2, generateThisBacking, generateGetOrSetCall, - judgeIfAddWatchFunc, + hasDecorator, + collectStateManagementTypeImport, } from './utils'; -import { PropertyTranslator } from './base'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { backingField, expectName } from '../../common/arkts-utils'; -import { createOptionalClassProperty } from '../utils'; import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; export class StateTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - 'StateDecoratedVariable', + StateManagementTypes.STATE_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( - generateGetOrSetCall(thisValue, 'set') - ); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -91,32 +84,59 @@ export class StateTranslator extends PropertyTranslator implements InitializerCo } generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { - const binaryItem = arkts.factory.createBinaryExpression( - factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), - originalName - ), - this.property.value ?? arkts.factory.createUndefinedLiteral(), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING - ); - const args: arkts.Expression[] = [arkts.factory.create1StringLiteral(originalName), binaryItem]; - judgeIfAddWatchFunc(args, this.property); - const right = arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('StateDecoratedVariable'), - arkts.factory.createTSTypeParameterInstantiation( - this.property.typeAnnotation ? [this.property.typeAnnotation] : [] - ) - ) - ), - args - ); + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + factory.generateInitializeValue(this.property, this.propertyType, originalName), + ]; + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.STATE_DECORATED); const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - right + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_STATE, this.propertyType, args, true) ); return arkts.factory.createExpressionStatement(assign); } } + +export class StateInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.STATE)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.STATE)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `StateDecoratedVariable | undefined`. + * + * @param method expecting getter with `@State` and a setter with `@State` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.STATE); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `StateDecoratedVariable | undefined`. + * + * @param property expecting property with `@State`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.STATE); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/staticProperty.ts b/arkui-plugins/ui-plugins/property-translators/staticProperty.ts index 12e9535c5..c36c9781a 100644 --- a/arkui-plugins/ui-plugins/property-translators/staticProperty.ts +++ b/arkui-plugins/ui-plugins/property-translators/staticProperty.ts @@ -15,12 +15,14 @@ import * as arkts from '@koalaui/libarkts'; -import { createGetter, createSetter } from './utils'; +import { createGetter, createSetter, hasDecorator } from './utils'; import { PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; import { backingField, expectName } from '../../common/arkts-utils'; +import { isStatic } from '../../ui-plugins/utils'; +import { DecoratorNames } from '../../common/predefines'; -export class staticPropertyTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { +export class StaticPropertyTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); @@ -49,4 +51,8 @@ export class staticPropertyTranslator extends PropertyTranslator implements Init const right: arkts.Identifier = arkts.factory.createIdentifier('value'); return createSetter(originalName, typeAnnotation, left, right); } + + static canBeStaticTranslate(node: arkts.ClassProperty): boolean { + return isStatic(node) && !hasDecorator(node, DecoratorNames.LOCAL); + } } diff --git a/arkui-plugins/ui-plugins/property-translators/storageProp.ts b/arkui-plugins/ui-plugins/property-translators/storageProp.ts index 2693f749d..e4b0978d2 100644 --- a/arkui-plugins/ui-plugins/property-translators/storageProp.ts +++ b/arkui-plugins/ui-plugins/property-translators/storageProp.ts @@ -16,74 +16,47 @@ import * as arkts from '@koalaui/libarkts'; import { backingField, expectName } from '../../common/arkts-utils'; -import { PropertyTranslator } from './base'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; import { - DecoratorNames, generateToRecord, createGetter, createSetter2, generateThisBacking, generateGetOrSetCall, - judgeIfAddWatchFunc, + collectStateManagementTypeImport, + hasDecorator, + getValueInAnnotation, } from './utils'; -import { createOptionalClassProperty } from '../utils'; - -function getStoragePropValueStr(node: arkts.AstNode): string | undefined { - if (!arkts.isClassProperty(node) || !node.value) return undefined; - - return arkts.isStringLiteral(node.value) ? node.value.str : undefined; -} - -function getStoragePropAnnotationValue(anno: arkts.AnnotationUsage): string | undefined { - const isStoragePropAnnotation: boolean = - !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.STORAGE_PROP; - - if (isStoragePropAnnotation && anno.properties.length === 1) { - return getStoragePropValueStr(anno.properties.at(0)!); - } - return undefined; -} - -function getStoragePropValueInAnnotation(node: arkts.ClassProperty): string | undefined { - const annotations: readonly arkts.AnnotationUsage[] = node.annotations; - - for (let i = 0; i < annotations.length; i++) { - const anno: arkts.AnnotationUsage = annotations[i]; - const str: string | undefined = getStoragePropAnnotationValue(anno); - if (!!str) { - return str; - } - } - return undefined; -} +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; export class StoragePropTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { - const storagePropValueStr: string | undefined = getStoragePropValueInAnnotation(this.property); + const storagePropValueStr: string | undefined = getValueInAnnotation( + this.property, + DecoratorNames.STORAGE_PROP + ); if (!storagePropValueStr) { - throw new Error('StorageProp required only one value!!'); // TODO: replace this with proper error message. + throw new Error('StorageProp required only one value!!'); } const args: arkts.Expression[] = [ @@ -91,55 +64,35 @@ export class StoragePropTranslator extends PropertyTranslator implements Initial arkts.factory.create1StringLiteral(originalName), this.property.value ?? arkts.factory.createUndefinedLiteral(), ]; - judgeIfAddWatchFunc(args, this.property); - - const newClass = arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('StoragePropDecoratedVariable'), - arkts.factory.createTSTypeParameterInstantiation( - this.property.typeAnnotation ? [this.property.typeAnnotation] : [] - ) - ) - ), - args - ); + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.STORAGE_PROP_REF_DECORATED); return arkts.factory.createAssignmentExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(newName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), + generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - newClass + factory.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_STORAGE_PROP_REF, + this.propertyType, + args, + true + ) ); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field = createOptionalClassProperty( + const field = factory.createOptionalClassProperty( newName, this.property, - 'StoragePropDecoratedVariable', + StateManagementTypes.STORAGE_PROP_REF_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( - generateGetOrSetCall(thisValue, 'set') - ); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -159,3 +112,45 @@ export class StoragePropTranslator extends PropertyTranslator implements Initial return createSetter2(originalName, typeAnnotation, statement); } } + +export class StoragePropInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.STORAGE_PROP)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.STORAGE_PROP)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `StoragePropDecoratedVariable | undefined`. + * + * @param method expecting getter with `@StorageProp` and a setter with `@StorageProp` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.STORAGE_PROP); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `StoragePropDecoratedVariable | undefined`. + * + * @param property expecting property with `@StorageProp`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.STORAGE_PROP); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/storagePropRef.ts b/arkui-plugins/ui-plugins/property-translators/storagePropRef.ts new file mode 100644 index 000000000..a27aa4be0 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/storagePropRef.ts @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { + generateToRecord, + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + collectStateManagementTypeImport, + hasDecorator, + getValueInAnnotation, +} from './utils'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class StoragePropRefTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { + const toRecord = generateToRecord(newName, originalName); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); + } + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + const storagePropValueStr: string | undefined = getValueInAnnotation( + this.property, + DecoratorNames.STORAGE_PROP_REF + ); + if (!storagePropValueStr) { + throw new Error('StoragePropRef required only one value!!'); + } + + const args: arkts.Expression[] = [ + arkts.factory.createStringLiteral(storagePropValueStr), + arkts.factory.create1StringLiteral(originalName), + this.property.value ?? arkts.factory.createUndefinedLiteral(), + ]; + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.STORAGE_PROP_REF_DECORATED); + + return arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_STORAGE_PROP_REF, + this.propertyType, + args, + true + ) + ); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.STORAGE_PROP_REF_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } +} + +export class StoragePropRefInterfaceTranslator< + T extends InterfacePropertyTypes +> extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.STORAGE_PROP_REF)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.STORAGE_PROP_REF)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `IStoragePropRefDecoratedVariable | undefined`. + * + * @param method expecting getter with `@StoragePropRef` and a setter with `@StoragePropRef` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.STORAGE_PROP_REF); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `IStoragePropRefDecoratedVariable | undefined`. + * + * @param property expecting property with `@StoragePropRef`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.STORAGE_PROP_REF); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/storagelink.ts b/arkui-plugins/ui-plugins/property-translators/storagelink.ts index efd83e9d6..e90016c3c 100644 --- a/arkui-plugins/ui-plugins/property-translators/storagelink.ts +++ b/arkui-plugins/ui-plugins/property-translators/storagelink.ts @@ -16,74 +16,47 @@ import * as arkts from '@koalaui/libarkts'; import { backingField, expectName } from '../../common/arkts-utils'; -import { PropertyTranslator } from './base'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; import { - DecoratorNames, generateToRecord, createGetter, createSetter2, generateThisBacking, generateGetOrSetCall, - judgeIfAddWatchFunc, + collectStateManagementTypeImport, + hasDecorator, + getValueInAnnotation, } from './utils'; -import { createOptionalClassProperty } from '../utils'; - -function getStorageLinkValueStr(node: arkts.AstNode): string | undefined { - if (!arkts.isClassProperty(node) || !node.value) return undefined; - - return arkts.isStringLiteral(node.value) ? node.value.str : undefined; -} - -function getStorageLinkAnnotationValue(anno: arkts.AnnotationUsage): string | undefined { - const isStorageLinkAnnotation: boolean = - !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.STORAGE_LINK; - - if (isStorageLinkAnnotation && anno.properties.length === 1) { - return getStorageLinkValueStr(anno.properties.at(0)!); - } - return undefined; -} - -function getStorageLinkValueInAnnotation(node: arkts.ClassProperty): string | undefined { - const annotations: readonly arkts.AnnotationUsage[] = node.annotations; - - for (let i = 0; i < annotations.length; i++) { - const anno: arkts.AnnotationUsage = annotations[i]; - const str: string | undefined = getStorageLinkAnnotationValue(anno); - if (!!str) { - return str; - } - } - return undefined; -} +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; export class StorageLinkTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { - const storageLinkValueStr: string | undefined = getStorageLinkValueInAnnotation(this.property); + const storageLinkValueStr: string | undefined = getValueInAnnotation( + this.property, + DecoratorNames.STORAGE_LINK + ); if (!storageLinkValueStr) { - throw new Error('StorageLink required only one value!!'); // TODO: replace this with proper error message. + throw new Error('StorageLink required only one value!!'); } const args: arkts.Expression[] = [ @@ -91,55 +64,29 @@ export class StorageLinkTranslator extends PropertyTranslator implements Initial arkts.factory.create1StringLiteral(originalName), this.property.value ?? arkts.factory.createUndefinedLiteral(), ]; - judgeIfAddWatchFunc(args, this.property); - - const newClass = arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('StorageLinkDecoratedVariable'), - arkts.factory.createTSTypeParameterInstantiation( - this.property.typeAnnotation ? [this.property.typeAnnotation] : [] - ) - ) - ), - args - ); - + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.STORAGE_LINK_DECORATED); return arkts.factory.createAssignmentExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(newName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), + generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - newClass + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_STORAGE_LINK, this.propertyType, args, true) ); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field = createOptionalClassProperty( + const field = factory.createOptionalClassProperty( newName, this.property, - 'StorageLinkDecoratedVariable', + StateManagementTypes.STORAGE_LINK_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( - generateGetOrSetCall(thisValue, 'set') - ); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -159,3 +106,45 @@ export class StorageLinkTranslator extends PropertyTranslator implements Initial return createSetter2(originalName, typeAnnotation, statement); } } + +export class StorageLinkInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.STORAGE_LINK)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.STORAGE_LINK)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `StorageLinkDecoratedVariable | undefined`. + * + * @param method expecting getter with `@StorageLink` and a setter with `@StorageLink` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.STORAGE_LINK); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `StorageLinkDecoratedVariable | undefined`. + * + * @param property expecting property with `@StorageLink`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.STORAGE_LINK); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/types.ts b/arkui-plugins/ui-plugins/property-translators/types.ts index b791a7da1..0b903d0e3 100644 --- a/arkui-plugins/ui-plugins/property-translators/types.ts +++ b/arkui-plugins/ui-plugins/property-translators/types.ts @@ -31,4 +31,4 @@ export interface GetterSetter { export interface InitializerConstructor { cacheTranslatedInitializer(newName: string, originalName: string): void; translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[]; -} +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/property-translators/utils.ts b/arkui-plugins/ui-plugins/property-translators/utils.ts index cda1293fc..8e4415efe 100644 --- a/arkui-plugins/ui-plugins/property-translators/utils.ts +++ b/arkui-plugins/ui-plugins/property-translators/utils.ts @@ -14,45 +14,99 @@ */ import * as arkts from '@koalaui/libarkts'; -import { annotation } from '../../common/arkts-utils'; -import { factory } from './factory'; - -export enum DecoratorNames { - STATE = 'State', - STORAGE_LINK = 'StorageLink', - STORAGE_PROP = 'StorageProp', - LINK = 'Link', - PROP = 'Prop', - PROVIDE = 'Provide', - CONSUME = 'Consume', - OBJECT_LINK = 'ObjectLink', - OBSERVED = 'Observed', - WATCH = 'Watch', - BUILDER_PARAM = 'BuilderParam', - BUILDER = 'Builder', - CUSTOM_DIALOG = 'CustomDialog', - LOCAL_STORAGE_PROP = 'LocalStorageProp', - LOCAL_STORAGE_LINK = 'LocalStorageLink', - REUSABLE = 'Reusable', - TRACK = 'Track', +import { ImportCollector } from '../../common/import-collector'; +import { isDecoratorAnnotation } from '../../common/arkts-utils'; +import { + DecoratorIntrinsicNames, + DecoratorNames, + StateManagementTypes, + GetSetTypes, + ObservedNames, +} from '../../common/predefines'; +import { + addMemoAnnotation, + findCanAddMemoFromParameter, + findCanAddMemoFromTypeAnnotation, +} from '../../collectors/memo-collectors/utils'; +import { CustomDialogNames } from '../utils'; +import { ReturnTransformer } from './return-transformer'; + +export interface DecoratorInfo { + annotation: arkts.AnnotationUsage; + name: DecoratorNames; } -export function collectPropertyDecorators(property: arkts.ClassProperty): string[] { - const properties: string[] = []; - property.annotations.forEach((anno) => { - if (!!anno.expr && arkts.isIdentifier(anno.expr)) { - properties.push(anno.expr.name); - } - }); - return properties; +export interface OptionalMemberInfo { + isCall?: boolean; + isNumeric?: boolean; } -export function isDecoratorAnnotation(anno: arkts.AnnotationUsage, decoratorName: DecoratorNames): boolean { +export function isDecoratorIntrinsicAnnotation( + anno: arkts.AnnotationUsage, + decoratorName: DecoratorIntrinsicNames +): boolean { return !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === decoratorName; } -export function hasDecorator( +export function removeDecorator( property: arkts.ClassProperty | arkts.ClassDefinition | arkts.MethodDefinition, + decoratorName: DecoratorNames, + ignoreDecl?: boolean +): void { + if (arkts.isMethodDefinition(property)) { + property.scriptFunction.setAnnotations( + property.scriptFunction.annotations.filter( + (anno) => !isDecoratorAnnotation(anno, decoratorName, ignoreDecl) + ) + ); + } else { + property.setAnnotations( + property.annotations.filter((anno) => !isDecoratorAnnotation(anno, decoratorName, ignoreDecl)) + ); + } +} + +export function getGetterReturnType(method: arkts.MethodDefinition): arkts.TypeNode | undefined { + const body = method.scriptFunction.body; + if (!body || !arkts.isBlockStatement(body) || body.statements.length <= 0) { + return undefined; + } + let returnType: arkts.TypeNode | undefined = undefined; + const returnTransformer = new ReturnTransformer(); + returnTransformer.visitor(body); + const typeArray = returnTransformer.types; + if (typeArray.length <= 0) { + returnType = undefined; + } else if (typeArray.length === 1) { + returnType = typeArray.at(0); + } else { + returnType = arkts.factory.createUnionType(typeArray); + } + returnTransformer.reset(); + return returnType?.clone(); +} + +/** + * checking whether astNode's annotations contain given corresponding decorator name, + * regardless where the annotation's declaration is from arkui declaration files. + */ +export function hasDecoratorName( + property: arkts.ClassProperty | arkts.ClassDefinition | arkts.MethodDefinition, + decoratorName: DecoratorNames +): boolean { + if (arkts.isMethodDefinition(property)) { + return property.scriptFunction.annotations.some((anno) => isDecoratorAnnotation(anno, decoratorName, true)); + } + return property.annotations.some((anno) => isDecoratorAnnotation(anno, decoratorName, true)); +} + +export function hasDecorator( + property: + | arkts.ClassProperty + | arkts.ClassDefinition + | arkts.MethodDefinition + | arkts.ETSParameterExpression + | arkts.ETSFunctionType, decoratorName: DecoratorNames ): boolean { if (arkts.isMethodDefinition(property)) { @@ -68,34 +122,54 @@ export function hasDecorator( */ export function needDefiniteOrOptionalModifier(st: arkts.ClassProperty): boolean { return ( - hasDecorator(st, DecoratorNames.LINK) || - hasDecorator(st, DecoratorNames.CONSUME) || - hasDecorator(st, DecoratorNames.OBJECT_LINK) || - (hasDecorator(st, DecoratorNames.PROP) && !st.value) + hasDecoratorName(st, DecoratorNames.LINK) || + hasDecoratorName(st, DecoratorNames.CONSUME) || + hasDecoratorName(st, DecoratorNames.OBJECT_LINK) || + (hasDecoratorName(st, DecoratorNames.PROP) && !st.value) || + (hasDecoratorName(st, DecoratorNames.PROP_REF) && !st.value) || + (hasDecoratorName(st, DecoratorNames.PARAM) && !st.value) || + (hasDecoratorName(st, DecoratorNames.EVENT) && !st.value) || + (hasDecoratorName(st, DecoratorNames.REQUIRE) && !st.value) ); } -export function getStateManagementType(node: arkts.ClassProperty): string { - if (hasDecorator(node, DecoratorNames.STATE)) { - return 'StateDecoratedVariable'; - } else if (hasDecorator(node, DecoratorNames.LINK)) { - return 'DecoratedV1VariableBase'; - } else if (hasDecorator(node, DecoratorNames.PROP)) { - return 'PropDecoratedVariable'; - } else if (hasDecorator(node, DecoratorNames.STORAGE_LINK)) { - return 'StorageLinkDecoratedVariable'; - } else if (hasDecorator(node, DecoratorNames.STORAGE_PROP)) { - return 'StoragePropDecoratedVariable'; - } else if (hasDecorator(node, DecoratorNames.PROVIDE)) { - return 'ProvideDecoratedVariable'; - } else if (hasDecorator(node, DecoratorNames.CONSUME)) { - return 'ConsumeDecoratedVariable'; - } else if (hasDecorator(node, DecoratorNames.OBJECT_LINK)) { - return 'ObjectLinkDecoratedVariable'; - } else if (hasDecorator(node, DecoratorNames.LOCAL_STORAGE_PROP)) { - return 'SyncedProperty'; +export function findDecoratorByName( + property: arkts.ClassProperty | arkts.ClassDefinition | arkts.MethodDefinition, + decoratorName: DecoratorNames +): arkts.AnnotationUsage | undefined { + if (arkts.isMethodDefinition(property)) { + return property.scriptFunction.annotations.find((anno) => isDecoratorAnnotation(anno, decoratorName, true)); + } + return property.annotations.find((anno) => isDecoratorAnnotation(anno, decoratorName, true)); +} + +export function findDecorator( + property: arkts.ClassProperty | arkts.ClassDefinition | arkts.MethodDefinition, + decoratorName: DecoratorNames +): arkts.AnnotationUsage | undefined { + if (arkts.isMethodDefinition(property)) { + return property.scriptFunction.annotations.find((anno) => isDecoratorAnnotation(anno, decoratorName)); + } + return property.annotations.find((anno) => isDecoratorAnnotation(anno, decoratorName)); +} + +export function findDecoratorInfos( + property: arkts.ClassProperty | arkts.ClassDefinition | arkts.MethodDefinition +): DecoratorInfo[] { + const decoratorNames = Object.values(DecoratorNames); + const infos: DecoratorInfo[] = []; + for (let i = 0; i < decoratorNames.length; i++) { + const name = decoratorNames[i]; + const annotation: arkts.AnnotationUsage | undefined = findDecoratorByName(property, name); + if (!!annotation) { + infos.push({ annotation, name }); + } } - return 'MutableState'; + return infos; +} + +export function collectStateManagementTypeImport(type: StateManagementTypes): void { + ImportCollector.getInstance().collectImport(type); } export function createGetter( @@ -103,23 +177,27 @@ export function createGetter( type: arkts.TypeNode | undefined, returns: arkts.Expression, needMemo: boolean = false, + isStatic: boolean = false ): arkts.MethodDefinition { const returnType: arkts.TypeNode | undefined = type?.clone(); - if (needMemo) { - returnType?.setAnnotations([annotation('memo')]); + if (needMemo && findCanAddMemoFromTypeAnnotation(returnType)) { + addMemoAnnotation(returnType); } const body = arkts.factory.createBlock([arkts.factory.createReturnStatement(returns)]); + const modifiers = isStatic + ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC + : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; const scriptFunction = arkts.factory.createScriptFunction( body, arkts.FunctionSignature.createFunctionSignature(undefined, [], returnType, false), arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_GETTER, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC + modifiers ); return arkts.factory.createMethodDefinition( arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET, arkts.factory.createIdentifier(name), scriptFunction, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + modifiers, false ); } @@ -144,8 +222,8 @@ export function createSetter( arkts.factory.createIdentifier('value', type?.clone()), undefined ); - if (needMemo) { - param.annotations = [annotation('memo')]; + if (needMemo && findCanAddMemoFromParameter(param)) { + addMemoAnnotation(param); } const scriptFunction = arkts.factory.createScriptFunction( body, @@ -166,25 +244,29 @@ export function createSetter( export function createSetter2( name: string, type: arkts.TypeNode | undefined, - statement: arkts.AstNode + statement: arkts.AstNode, + isStatic: boolean = false ): arkts.MethodDefinition { const body = arkts.factory.createBlock([statement]); const param: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier('value', type?.clone()), undefined ); + const modifiers = isStatic + ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC + : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; const scriptFunction = arkts.factory.createScriptFunction( body, arkts.FunctionSignature.createFunctionSignature(undefined, [param], undefined, false), arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_SETTER, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC + modifiers ); return arkts.factory.createMethodDefinition( arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET, arkts.factory.createIdentifier(name), scriptFunction, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + modifiers, false ); } @@ -297,16 +379,7 @@ function getDifferentAnnoTypeValue(value: arkts.Expression): string | boolean { return value.dumpSrc(); } -export function judgeIfAddWatchFunc(args: arkts.Expression[], property: arkts.ClassProperty): void { - if (hasDecorator(property, DecoratorNames.WATCH)) { - const watchStr: string | undefined = getValueInAnnotation(property, DecoratorNames.WATCH); - if (watchStr) { - args.push(factory.createWatchCallback(watchStr)); - } - } -} - -export function generateGetOrSetCall(beforCall: arkts.AstNode, type: string) { +export function generateGetOrSetCall(beforCall: arkts.AstNode, type: GetSetTypes) { return arkts.factory.createCallExpression( arkts.factory.createMemberExpression( beforCall, @@ -343,25 +416,65 @@ export function generateToRecord(newName: string, originalName: string): arkts.P ); } -export function getStageManagementIdent(property: arkts.ClassProperty): string { - const useMutableState: boolean = - hasDecorator(property, DecoratorNames.STATE) || - hasDecorator(property, DecoratorNames.STORAGE_LINK) || - hasDecorator(property, DecoratorNames.PROVIDE) || - hasDecorator(property, DecoratorNames.CONSUME) || - hasDecorator(property, DecoratorNames.LINK) || - hasDecorator(property, DecoratorNames.LOCAL_STORAGE_LINK) || - hasDecorator(property, DecoratorNames.LINK); - const useSyncedProperty: boolean = - hasDecorator(property, DecoratorNames.PROP) || - hasDecorator(property, DecoratorNames.STORAGE_PROP) || - hasDecorator(property, DecoratorNames.LOCAL_STORAGE_PROP) || - hasDecorator(property, DecoratorNames.OBJECT_LINK); - if (useMutableState) { - return 'MutableState'; - } else if (useSyncedProperty) { - return 'SyncedProperty'; - } else { - return ''; +export function isCustomDialogController(type: arkts.TypeNode): boolean { + if (arkts.isETSUnionType(type)) { + return !!type.types.find((item: arkts.TypeNode) => { + return isCustomDialogController(item); + }); } + return ( + !!arkts.isETSTypeReference(type) && + !!type.part && + !!type.part.name && + arkts.isIdentifier(type.part.name) && + type.part.name.name === CustomDialogNames.CUSTOM_DIALOG_CONTROLLER + ); +} + +export function removeImplementProperty(originalName: string): string { + const prefix = ObservedNames.PROPERTY_PREFIX; + return originalName.substring(prefix.length); +} + +export function getValueInMonitorAnnotation(annotations: readonly arkts.AnnotationUsage[]): string[] | undefined { + const monitorAnno: arkts.AnnotationUsage | undefined = annotations.find((anno: arkts.AnnotationUsage) => { + return ( + anno.expr && + arkts.isIdentifier(anno.expr) && + anno.expr.name === DecoratorNames.MONITOR && + anno.properties.length === 1 + ); + }); + if (!monitorAnno) { + return undefined; + } + return getArrayFromAnnoProperty(monitorAnno.properties.at(0)!); +} + +export function getArrayFromAnnoProperty(property: arkts.AstNode): string[] | undefined { + if (!arkts.isClassProperty(property) || !property.value || !arkts.isArrayExpression(property.value)) { + return undefined; + } + const resArr: string[] = []; + property.value.elements.forEach((item: arkts.Expression) => { + if (arkts.isStringLiteral(item)) { + resArr.push(item.str); + } else if (arkts.isMemberExpression(item)) { + const res: string | undefined = getMonitorStrFromMemberExpr(item); + !!res && resArr.push(res); + } + }); + return resArr; +} + +function getMonitorStrFromMemberExpr(node: arkts.MemberExpression): string | undefined { + const decl: arkts.AstNode | undefined = arkts.getDecl(node.property); + if (!decl || !arkts.isClassProperty(decl) || !decl.value || !arkts.isETSNewClassInstanceExpression(decl.value)) { + return undefined; + } + const args: readonly arkts.Expression[] = decl.value.getArguments; + if (args.length >= 2 && arkts.isStringLiteral(args[1])) { + return args[1].str; + } + return undefined; } diff --git a/arkui-plugins/ui-plugins/struct-translators/factory.ts b/arkui-plugins/ui-plugins/struct-translators/factory.ts index 6fd41c6b6..dedd4331e 100644 --- a/arkui-plugins/ui-plugins/struct-translators/factory.ts +++ b/arkui-plugins/ui-plugins/struct-translators/factory.ts @@ -14,99 +14,106 @@ */ import * as arkts from '@koalaui/libarkts'; -import { BuilderLambdaNames, CustomComponentNames, Dollars } from '../utils'; -import { factory as uiFactory } from '../ui-factory'; -import { annotation } from '../../common/arkts-utils'; +import { + BuilderLambdaNames, + CustomComponentNames, + CustomDialogNames, + getCustomComponentOptionsName, + getGettersFromClassDecl, + getTypeNameFromTypeParameter, + getTypeParamsFromClassDecl, + isCustomComponentInterface, + isCustomDialogControllerOptions, + isKnownMethodDefinition, + isStatic, +} from '../utils'; +import { factory as UIFactory } from '../ui-factory'; +import { factory as PropertyFactory } from '../property-translators/factory'; +import { factory as BuilderLambdaFactory } from '../builder-lambda-translators/factory'; +import { BuilderFactory } from '../builder-lambda-translators/builder-factory'; +import { backingField, collect, filterDefined } from '../../common/arkts-utils'; +import { + classifyInObservedClass, + classifyPropertyInInterface, + classifyStructMembers, + ClassScopeInfo, + InterfacePropertyTranslator, + PropertyTranslator, +} from '../property-translators'; +import { + CustomComponentScopeInfo, + isEtsGlobalClass, + ResourceInfo, + checkRawfileResource, + generateResourceModuleName, + generateResourceBundleName, + isDynamicName, + preCheckResourceData, + ResourceParameter, + getResourceParams, + isResourceNode, + isForEachCall, + getCustomDialogController, + isInvalidDialogControllerOptions, + findBuilderIndexInControllerOptions, + ObservedAnnoInfo, + getNoTransformationMembersInClass, + isComputedMethod, +} from './utils'; +import { collectStateManagementTypeImport, generateThisBacking, hasDecorator } from '../property-translators/utils'; +import { ProjectConfig } from '../../common/plugin-context'; +import { ImportCollector } from '../../common/import-collector'; +import { + AnimationNames, + ARKUI_COMPONENT_COMMON_SOURCE_NAME, + DecoratorNames, + Dollars, + ModuleType, + StateManagementTypes, + RESOURCE_TYPE, + ARKUI_BUILDER_SOURCE_NAME, + TypeNames, +} from '../../common/predefines'; +import { ObservedTranslator } from '../property-translators/index'; +import { + addMemoAnnotation, + collectMemoableInfoInFunctionReturnType, + collectScriptFunctionReturnTypeFromInfo, +} from '../../collectors/memo-collectors/utils'; +import { generateArkUICompatible, isArkUICompatible } from '../interop/interop'; +import { GenSymGenerator } from '../../common/gensym-generator'; +import { MethodTranslator } from 'ui-plugins/property-translators/base'; +import { MonitorCache } from '../property-translators/cache/monitorCache'; +import { PropertyCache } from '../property-translators/cache/propertyCache'; export class factory { - /* - * create `constructor() {}`. - */ - static createConstructorMethod(member: arkts.MethodDefinition): arkts.MethodDefinition { - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, - member.name, - member.scriptFunction, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONSTRUCTOR, - false - ); - } - - /* - * create _build method. - */ - static transformBuildMethodWithOriginBuild( - method: arkts.MethodDefinition, - typeName: string, - optionsName: string, - isDecl?: boolean - ): arkts.MethodDefinition { - const updateKey: arkts.Identifier = arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_BUILD); - - const scriptFunction: arkts.ScriptFunction = method.scriptFunction; - const updateScriptFunction = arkts.factory - .createScriptFunction( - scriptFunction.body, - arkts.FunctionSignature.createFunctionSignature( - scriptFunction.typeParams, - [ - uiFactory.createStyleParameter(typeName), - uiFactory.createContentParameter(), - uiFactory.createInitializersOptionsParameter(optionsName), - ], - arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), - false - ), - scriptFunction.flags, - scriptFunction.modifiers - ) - .setAnnotations([annotation('memo')]); - - const modifiers: arkts.Es2pandaModifierFlags = isDecl - ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_ABSTRACT - : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, - updateKey, - updateScriptFunction, - modifiers, - false - ); - } - - /* - * generate _r() or _rawfile(). + /** + * update class `constructor` to private. + * @deprecated */ - static generateTransformedResource( - resourceNode: arkts.CallExpression, - newArgs: arkts.AstNode[] - ): arkts.CallExpression { - const transformedKey: string = - resourceNode.expression.dumpSrc() === Dollars.DOLLAR_RESOURCE ? '_r' : '_rawfile'; - return arkts.factory.updateCallExpression( - resourceNode, - arkts.factory.createIdentifier(transformedKey), - resourceNode.typeArguments, - newArgs - ); + static setStructConstructorToPrivate(member: arkts.MethodDefinition): arkts.MethodDefinition { + member.modifiers &= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; + member.modifiers &= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PROTECTED; + member.modifiers |= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE; + return member; } - /* + /** * create __initializeStruct method. */ - static createInitializeStruct( - structInfo: arkts.StructInfo, - optionsTypeName: string, - isDecl?: boolean - ): arkts.MethodDefinition { + static createInitializeStruct(optionsTypeName: string, scope: CustomComponentScopeInfo): arkts.MethodDefinition { const updateKey: arkts.Identifier = arkts.factory.createIdentifier( CustomComponentNames.COMPONENT_INITIALIZE_STRUCT ); let body: arkts.BlockStatement | undefined; - let modifiers: arkts.Es2pandaModifierFlags = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_ABSTRACT; - if (!isDecl) { - body = arkts.factory.createBlock(structInfo.initializeBody); + let modifiers: arkts.Es2pandaModifierFlags = + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE; + if (!scope.isDecl) { + body = arkts.factory.createBlock([ + ...PropertyCache.getInstance().getInitializeBody(scope.name), + ...MonitorCache.getInstance().getCachedMonitors(scope.name), + ]); modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; } const scriptFunction: arkts.ScriptFunction = arkts.factory @@ -114,7 +121,7 @@ export class factory { body, arkts.FunctionSignature.createFunctionSignature( undefined, - [uiFactory.createInitializersOptionsParameter(optionsTypeName), uiFactory.createContentParameter()], + [UIFactory.createInitializersOptionsParameter(optionsTypeName), UIFactory.createContentParameter()], arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), false ), @@ -132,22 +139,67 @@ export class factory { ); } - /* + static transformControllerInterfaceType(node: arkts.TSInterfaceDeclaration): arkts.TSInterfaceDeclaration { + if (!node.body || node.body.body.length <= 0 || !arkts.isMethodDefinition(node.body.body[0])) { + return node; + } + const updatedBody = arkts.factory.updateInterfaceBody(node.body, [ + this.updateBuilderType(node.body.body[0]), + ...node.body.body.slice(1), + ]); + return arkts.factory.updateInterfaceDeclaration( + node, + node.extends, + node.id, + node.typeParams, + updatedBody, + node.isStatic, + node.isFromExternal + ); + } + + static updateBuilderType(builderNode: arkts.MethodDefinition): arkts.MethodDefinition { + const newType: arkts.TypeNode | undefined = UIFactory.createTypeReferenceFromString( + CustomDialogNames.CUSTOM_BUILDER + ); + if (builderNode.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET) { + const newOverLoads = builderNode.overloads.map((overload) => { + if (arkts.isMethodDefinition(overload)) { + return factory.updateBuilderType(overload); + } + return overload; + }); + builderNode.setOverloads(newOverLoads); + if (!!newType) { + builderNode.scriptFunction.setReturnTypeAnnotation(newType); + } + } else if (builderNode.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET) { + const param = builderNode.scriptFunction.params[0] as arkts.ETSParameterExpression; + const newParam: arkts.Expression | undefined = arkts.factory.updateParameterDeclaration( + param, + arkts.factory.createIdentifier(param.identifier.name, newType), + param.initializer + ); + if (!!newParam) { + return UIFactory.updateMethodDefinition(builderNode, { function: { params: [newParam] } }); + } + } + return builderNode; + } + + /** * create __updateStruct method. */ - static createUpdateStruct( - structInfo: arkts.StructInfo, - optionsTypeName: string, - isDecl?: boolean - ): arkts.MethodDefinition { + static createUpdateStruct(optionsTypeName: string, scope: CustomComponentScopeInfo): arkts.MethodDefinition { const updateKey: arkts.Identifier = arkts.factory.createIdentifier( CustomComponentNames.COMPONENT_UPDATE_STRUCT ); let body: arkts.BlockStatement | undefined; - let modifiers: arkts.Es2pandaModifierFlags = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_ABSTRACT; - if (!isDecl) { - body = arkts.factory.createBlock(structInfo.updateBody); + let modifiers: arkts.Es2pandaModifierFlags = + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE; + if (!scope.isDecl) { + body = arkts.factory.createBlock(PropertyCache.getInstance().getUpdateBody(scope.name)); modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; } @@ -156,7 +208,7 @@ export class factory { body, arkts.FunctionSignature.createFunctionSignature( undefined, - [uiFactory.createInitializersOptionsParameter(optionsTypeName)], + [UIFactory.createInitializersOptionsParameter(optionsTypeName)], arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), false ), @@ -174,15 +226,15 @@ export class factory { ); } - /* + /** * create __toRecord method when the component is decorated with @Reusable. */ - static toRecord(optionsTypeName: string, toRecordBody: arkts.Property[]): arkts.MethodDefinition { + static createToRecord(optionsTypeName: string, scope: CustomComponentScopeInfo): arkts.MethodDefinition { const paramsCasted = factory.generateParamsCasted(optionsTypeName); const returnRecord = arkts.factory.createReturnStatement( arkts.ObjectExpression.createObjectExpression( arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, - toRecordBody, + PropertyCache.getInstance().getToRecordBody(scope.name), false ) ); @@ -209,7 +261,7 @@ export class factory { ); } - /* + /** * generate `const paramsCasted = (params as )`. */ static generateParamsCasted(optionsTypeName: string): arkts.VariableDeclaration { @@ -230,7 +282,7 @@ export class factory { ); } - /* + /** * generate Record type. */ static generateTypeRecord(): arkts.ETSTypeReference { @@ -245,7 +297,7 @@ export class factory { ); } - /* + /** * create type reference with type name, e.g. number. */ static generateTypeReferenceWithTypeName(typeName: string): arkts.ETSTypeReference { @@ -254,7 +306,7 @@ export class factory { ); } - /* + /** * create type reference with type name, e.g. number. */ static updateCustomComponentClass( @@ -275,15 +327,20 @@ export class factory { ); } - /* - * add headers for animation in CommonMethod + /** + * add headers for animation & @AnimatableExtend in CommonMethod */ - static modifyExternalComponentCommon(node: arkts.TSInterfaceDeclaration): arkts.AstNode { - const animationStart = factory.createAnimationMethod(BuilderLambdaNames.ANIMATION_START); - const animationStop = factory.createAnimationMethod(BuilderLambdaNames.ANIMATION_STOP); + static modifyExternalComponentCommon(node: arkts.TSInterfaceDeclaration): arkts.TSInterfaceDeclaration { + if (!node.body) { + return node; + } + const animationStart = factory.createAnimationMethod(AnimationNames.ANIMATION_START); + const animationStop = factory.createAnimationMethod(AnimationNames.ANIMATION_STOP); + const createOrSetAniProperty = factory.createOrSetAniProperty(); const updatedBody = arkts.factory.updateInterfaceBody(node.body!, [ animationStart, animationStop, + createOrSetAniProperty, ...node.body!.body, ]); return arkts.factory.updateInterfaceDeclaration( @@ -297,7 +354,70 @@ export class factory { ); } - /* + /** + * helper to create value parameter for AnimatableExtend methods + */ + static createAniExtendValueParam(): arkts.ETSParameterExpression { + const numberType = UIFactory.createTypeReferenceFromString('number'); + const AnimatableArithmeticType = arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(AnimationNames.ANIMATABLE_ARITHMETIC), + arkts.factory.createTSTypeParameterInstantiation([UIFactory.createTypeReferenceFromString('T')]) + ) + ); + return arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'value', + arkts.factory.createUnionType([numberType, AnimatableArithmeticType]) + ), + undefined + ); + } + + /** + * generate __createOrSetAnimatableProperty(...) for AnimatableExtend + */ + static createOrSetAniProperty(): arkts.MethodDefinition { + const funcNameParam: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier('functionName', UIFactory.createTypeReferenceFromString('string')), + undefined + ); + const cbParam = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'callback', + arkts.factory.createFunctionType( + arkts.factory.createFunctionSignature( + undefined, + [factory.createAniExtendValueParam()], + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), + false + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW + ) + ), + undefined + ); + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + arkts.factory.createIdentifier(AnimationNames.CREATE_OR_SET_ANIMATABLEPROPERTY), + UIFactory.createScriptFunction({ + typeParams: arkts.factory.createTypeParameterDeclaration( + [arkts.factory.createTypeParameter(arkts.factory.createIdentifier('T'))], + 0 + ), + params: [funcNameParam, factory.createAniExtendValueParam(), cbParam], + returnTypeAnnotation: arkts.factory.createPrimitiveType( + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID + ), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + }), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + false + ); + } + + /** * generate animationStart(...) and animationStop(...) */ static createAnimationMethod(key: string): arkts.MethodDefinition { @@ -305,19 +425,17 @@ export class factory { arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier( 'value', - arkts.factory.createUnionType( - [ - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('AnimateParam')) - ), - arkts.factory.createETSUndefinedType() - ] - ) + arkts.factory.createUnionType([ + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('AnimateParam')) + ), + arkts.factory.createETSUndefinedType(), + ]) ), undefined ), ]; - const aniFuncExpr = arkts.factory.createScriptFunction( + const aniFunc = arkts.factory.createScriptFunction( undefined, arkts.factory.createFunctionSignature(undefined, aniparams, arkts.TSThisType.createTSThisType(), false), arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, @@ -326,35 +444,627 @@ export class factory { return arkts.factory.createMethodDefinition( arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, arkts.factory.createIdentifier(key), - aniFuncExpr, + aniFunc, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, false ); } + /** + * transform property members in custom-component class. + */ + static tranformPropertyMembers( + propertyTranslators: (PropertyTranslator | MethodTranslator)[], + optionsTypeName: string, + scope: CustomComponentScopeInfo + ): arkts.AstNode[] { + const propertyMembers = propertyTranslators.map((translator) => translator.translateMember()); + const collections = []; + if (!scope.hasInitializeStruct) { + collections.push(this.createInitializeStruct(optionsTypeName, scope)); + } + if (!scope.hasUpdateStruct) { + collections.push(this.createUpdateStruct(optionsTypeName, scope)); + } + if (!!scope.annotations?.reusable) { + collections.push(this.createToRecord(optionsTypeName, scope)); + } + return collect(...collections, ...propertyMembers); + } + + /** + * transform non-property members in custom-component class. + */ + static transformNonPropertyMembersInClass(member: arkts.AstNode, isDecl?: boolean): arkts.AstNode { + if (arkts.isMethodDefinition(member)) { + PropertyFactory.addMemoToBuilderClassMethod(member); + if (isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_BUILD_ORI)) { + addMemoAnnotation(member.scriptFunction); + return BuilderFactory.rewriteBuilderMethod(member); + } + return member; + } + return member; + } + + /** + * transform members in custom-component class. + */ + static tranformClassMembers(node: arkts.ClassDeclaration, scope: CustomComponentScopeInfo): arkts.ClassDeclaration { + if (!node.definition) { + return node; + } + let classOptionsName: string | undefined; + if (scope.isDecl) { + const [_, classOptions] = getTypeParamsFromClassDecl(node); + classOptionsName = getTypeNameFromTypeParameter(classOptions); + } + const definition: arkts.ClassDefinition = node.definition; + const className: string | undefined = node.definition.ident?.name; + if (!className) { + throw new Error('Non Empty className expected for Component'); + } + const body: readonly arkts.AstNode[] = definition.body; + const propertyTranslators: (PropertyTranslator | MethodTranslator)[] = filterDefined( + body.map((member) => classifyStructMembers(member, scope)) + ); + const translatedMembers: arkts.AstNode[] = this.tranformPropertyMembers( + propertyTranslators, + classOptionsName ?? getCustomComponentOptionsName(className), + scope + ); + const updateMembers: arkts.AstNode[] = body + .filter((member) => !arkts.isClassProperty(member) && !isComputedMethod(member)) + .map((member: arkts.AstNode) => factory.transformNonPropertyMembersInClass(member, scope.isDecl)); + const updateClassDef: arkts.ClassDefinition = this.updateCustomComponentClass( + definition, + factory.addClassStaticBlock([...translatedMembers, ...updateMembers], body) + ); + if ( + !!scope.annotations.customdialog || + (scope.isDecl && scope.name === CustomComponentNames.BASE_CUSTOM_DIALOG_NAME) + ) { + updateClassDef.addProperties(factory.addControllerSetMethod(scope.isDecl, body)); + } + return arkts.factory.updateClassDeclaration(node, updateClassDef); + } + + /** + * Determine whether a class static block needs to be added. + */ + static addClassStaticBlock(members: arkts.AstNode[], body: readonly arkts.AstNode[]): arkts.AstNode[] { + const staticBlock = body.find((item: arkts.AstNode) => arkts.isClassStaticBlock(item)); + if (staticBlock) { + return members; + } + const classHasStaticComputed = body.find((item: arkts.AstNode) => isComputedMethod(item) && isStatic(item)); + if (classHasStaticComputed) { + members.push(UIFactory.createClassStaticBlock()); + } + return members; + } + + /** + * add `__setDialogController__` method in `@CustomDialog` component. + */ + static addControllerSetMethod(isDecl: boolean, body: readonly arkts.AstNode[]): arkts.MethodDefinition[] { + if (isDecl) { + return [this.createCustomDialogMethod(isDecl)]; + } + const dialogControllerProperty: arkts.ClassProperty | undefined = body.find( + (item: arkts.AstNode) => arkts.isClassProperty(item) && getCustomDialogController(item).length > 0 + ) as arkts.ClassProperty | undefined; + if (!!dialogControllerProperty) { + return [this.createCustomDialogMethod(isDecl, getCustomDialogController(dialogControllerProperty))]; + } + return []; + } + + /** + * transform `$r` and `$rawfile` function calls. + */ + static transformResource( + resourceNode: arkts.CallExpression, + projectConfig: ProjectConfig | undefined, + resourceInfo: ResourceInfo + ): arkts.CallExpression { + if (!arkts.isIdentifier(resourceNode.expression) || !projectConfig) { + return resourceNode; + } + const resourceKind: Dollars = resourceNode.expression.name as Dollars; + if (arkts.isStringLiteral(resourceNode.arguments[0])) { + return factory.processStringLiteralResourceNode( + resourceNode, + resourceInfo, + projectConfig, + resourceKind, + resourceNode.arguments[0] + ); + } else if (resourceNode.arguments && resourceNode.arguments.length) { + return factory.generateTransformedResourceCall( + resourceNode, + getResourceParams( + -1, + resourceKind === Dollars.DOLLAR_RAWFILE ? RESOURCE_TYPE.rawfile : -1, + Array.from(resourceNode.arguments) + ), + '', + false, + projectConfig, + resourceKind + ); + } + return resourceNode; + } + /* - * generate XComponent(..., packageInfo: string, ...) + * Process string Literal type arguments for resource node. */ - static modifyXcomponent(node: arkts.ScriptFunction): arkts.ScriptFunction { - const info = arkts.factory.createParameterDeclaration( + static processStringLiteralResourceNode( + resourceNode: arkts.CallExpression, + resourceInfo: ResourceInfo, + projectConfig: ProjectConfig, + resourceKind: Dollars, + literalArg: arkts.StringLiteral + ): arkts.CallExpression { + const resourceData: string[] = literalArg.str.trim().split('.'); + const fromOtherModule: boolean = !!resourceData.length && /^\[.*\]$/g.test(resourceData[0]); + if (resourceKind === Dollars.DOLLAR_RAWFILE) { + checkRawfileResource(resourceNode, literalArg, fromOtherModule, resourceInfo.rawfile); + let resourceId: number = projectConfig.moduleType === ModuleType.HAR ? -1 : 0; + let resourceModuleName: string = ''; + if (resourceData && resourceData[0] && fromOtherModule) { + resourceId = -1; + resourceModuleName = resourceData[0]; + } + return factory.generateTransformedResourceCall( + resourceNode, + getResourceParams(resourceId, RESOURCE_TYPE.rawfile, [literalArg]), + resourceModuleName, + fromOtherModule, + projectConfig, + Dollars.DOLLAR_RAWFILE + ); + } else { + return factory.processStringLiteralDollarResourceNode( + resourceNode, + resourceInfo, + projectConfig, + resourceData, + fromOtherModule + ); + } + } + + static createCustomDialogMethod(isDecl: boolean, controller?: string): arkts.MethodDefinition { + let block: arkts.BlockStatement | undefined = undefined; + if (!!controller) { + block = arkts.factory.createBlock(this.createSetControllerElements(controller)); + } + const param: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier( - 'packageInfo', - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('string') - ) - ) + CustomDialogNames.CONTROLLER, + UIFactory.createTypeReferenceFromString(CustomDialogNames.CUSTOM_DIALOG_CONTROLLER) ), undefined ); + const modifiers = isDecl + ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE + : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; + return UIFactory.createMethodDefinition({ + key: arkts.factory.createIdentifier(CustomDialogNames.SET_DIALOG_CONTROLLER_METHOD), + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + function: { + body: block, + params: [param], + returnTypeAnnotation: arkts.factory.createPrimitiveType( + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID + ), + hasReceiver: false, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + modifiers: modifiers, + }, + modifiers: modifiers, + }); + } + + /* + * create assignment expression `this.__backing = controller`. + */ + static createSetControllerElements(controller: string): arkts.AstNode[] { + return controller.length !== 0 + ? [ + arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + generateThisBacking(backingField(controller)), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + arkts.factory.createIdentifier(CustomDialogNames.CONTROLLER) + ) + ), + ] + : []; + } + + /* + * Process string Literal type arguments for $r node. + */ + static processStringLiteralDollarResourceNode( + resourceNode: arkts.CallExpression, + resourceInfo: ResourceInfo, + projectConfig: ProjectConfig, + resourceData: string[], + fromOtherModule: boolean + ): arkts.CallExpression { + if ( + preCheckResourceData(resourceNode, resourceData, resourceInfo.resourcesList, fromOtherModule, projectConfig) + ) { + const resourceId: number = + projectConfig.moduleType === ModuleType.HAR || + fromOtherModule || + !resourceInfo.resourcesList[resourceData[0]] + ? -1 + : resourceInfo.resourcesList[resourceData[0]].get(resourceData[1])![resourceData[2]]; + return factory.generateTransformedResourceCall( + resourceNode, + getResourceParams( + resourceId, + RESOURCE_TYPE[resourceData[1].trim()], + projectConfig.moduleType === ModuleType.HAR || fromOtherModule + ? Array.from(resourceNode.arguments) + : Array.from(resourceNode.arguments.slice(1)) + ), + resourceData.length ? resourceData[0] : '', + fromOtherModule, + projectConfig, + Dollars.DOLLAR_RESOURCE + ); + } + return resourceNode; + } + + /* + * generate tramsformed resource node, e.g. {id, type, params, bundleName, moduleName}. + */ + static generateTransformedResourceCall( + resourceNode: arkts.CallExpression, + resourceParams: ResourceParameter, + resourceModuleName: string, + fromOtherModule: boolean, + projectConfig: ProjectConfig, + resourceKind: Dollars + ): arkts.CallExpression { + const transformedKey: string = + resourceKind === Dollars.DOLLAR_RESOURCE + ? Dollars.TRANSFORM_DOLLAR_RESOURCE + : Dollars.TRANSFORM_DOLLAR_RAWFILE; + ImportCollector.getInstance().collectImport(transformedKey); + const isDynamicBundleOrModule: boolean = isDynamicName(projectConfig); + const args: arkts.AstNode[] = [ + arkts.factory.createNumericLiteral(resourceParams.id), + arkts.factory.createNumericLiteral(resourceParams.type), + arkts.factory.createStringLiteral(generateResourceBundleName(projectConfig, isDynamicBundleOrModule)), + arkts.factory.createStringLiteral( + generateResourceModuleName(projectConfig, isDynamicBundleOrModule, resourceModuleName, fromOtherModule) + ), + ...resourceParams.params, + ]; + return arkts.factory.updateCallExpression( + resourceNode, + arkts.factory.createIdentifier(transformedKey), + undefined, + args + ); + } + + /** + * transform members in interface. + */ + static tranformInterfaceMembers( + node: arkts.TSInterfaceDeclaration, + externalSourceName?: string + ): arkts.TSInterfaceDeclaration { + if (!node.id || !node.body) { + return node; + } + if (externalSourceName === ARKUI_COMPONENT_COMMON_SOURCE_NAME && node.id.name === 'CommonMethod') { + return factory.modifyExternalComponentCommon(node); + } + if (isCustomDialogControllerOptions(node, externalSourceName)) { + return factory.transformControllerInterfaceType(node); + } + if (isCustomComponentInterface(node)) { + return factory.tranformCustomComponentInterfaceMembers(node); + } + return factory.tranformInterfaceBuildMember(node); + } + + static tranformInterfaceBuildMember(node: arkts.TSInterfaceDeclaration): arkts.TSInterfaceDeclaration { + const newBody: arkts.AstNode[] = node.body!.body.map((it) => { + if (arkts.isMethodDefinition(it)) { + PropertyFactory.addMemoToBuilderClassMethod(it); + } + return it; + }); + return arkts.factory.updateInterfaceDeclaration( + node, + node.extends, + node.id, + node.typeParams, + arkts.factory.updateInterfaceBody(node.body!, newBody), + node.isStatic, + node.isFromExternal + ); + } + + /** + * transform members in custom-component related interface. + */ + static tranformCustomComponentInterfaceMembers(node: arkts.TSInterfaceDeclaration): arkts.TSInterfaceDeclaration { + const propertyTranslators: InterfacePropertyTranslator[] = filterDefined( + node.body!.body.map((it) => classifyPropertyInInterface(it)) + ); + + let shouldUpdate: boolean = false; + const newBody = propertyTranslators.map((translator) => { + const newProperty = translator.translateProperty(); + shouldUpdate ||= translator.modified; + return newProperty; + }); + + if (shouldUpdate) { + return arkts.factory.updateInterfaceDeclaration( + node, + node.extends, + node.id, + node.typeParams, + arkts.factory.updateInterfaceBody(node.body!, newBody), + node.isStatic, + node.isFromExternal + ); + } + + return node; + } + + static transformETSGlobalClass(node: arkts.ClassDeclaration, externalSourceName?: string): arkts.ClassDeclaration { + if (!node.definition) { + return node; + } + const updatedBody = node.definition.body.map((member: arkts.AstNode) => { + arkts.isMethodDefinition(member) && PropertyFactory.addMemoToBuilderClassMethod(member); + if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.ANIMATABLE_EXTEND)) { + member = arkts.factory.updateMethodDefinition( + member, + member.kind, + member.name, + factory.transformAnimatableExtend(member.scriptFunction), + member.modifiers, + false + ); + } + return member; + }); + let newStatements: arkts.AstNode[] = []; + if (externalSourceName === ARKUI_BUILDER_SOURCE_NAME) { + newStatements.push(...BuilderLambdaFactory.addConditionBuilderDecls()); + } + return arkts.factory.updateClassDeclaration( + node, + arkts.factory.updateClassDefinition( + node.definition, + node.definition.ident, + node.definition.typeParams, + node.definition.superTypeParams, + node.definition.implements, + undefined, + node.definition.super, + [...updatedBody, ...newStatements], + node.definition.modifiers, + arkts.classDefinitionFlags(node.definition) + ) + ); + } + + static transformNormalClass(node: arkts.ClassDeclaration, externalSourceName?: string): arkts.ClassDeclaration { + if (!node.definition) { + return node; + } + if (isEtsGlobalClass(node)) { + return this.transformETSGlobalClass(node, externalSourceName); + } + const newClassDef = factory.updateObservedTrackClassDef(node.definition); + return arkts.factory.updateClassDeclaration(node, newClassDef); + } + + /** + * transform class definition with , `@ObservedV2` and `@Trace`, `@Observed` or `@Trace`. + */ + static updateObservedTrackClassDef(node: arkts.ClassDefinition): arkts.ClassDefinition { + const isObserved: boolean = hasDecorator(node, DecoratorNames.OBSERVED); + const isObservedV2: boolean = hasDecorator(node, DecoratorNames.OBSERVED_V2); + const classHasTrack: boolean = node.body.some( + (member) => arkts.isClassProperty(member) && hasDecorator(member, DecoratorNames.TRACK) + ); + const classHasTrace: boolean = node.body.some( + (member) => arkts.isClassProperty(member) && hasDecorator(member, DecoratorNames.TRACE) + ); + const className: string | undefined = node.ident?.name; + if (!className || !(isObserved || classHasTrack || isObservedV2)) { + return node; + } + const ObservedAnno: ObservedAnnoInfo = { isObserved, classHasTrack, isObservedV2, classHasTrace, className }; + const updateClassDef: arkts.ClassDefinition = arkts.factory.updateClassDefinition( + node, + node.ident, + node.typeParams, + node.superTypeParams, + [ + ...node.implements, + arkts.TSClassImplements.createTSClassImplements( + UIFactory.createTypeReferenceFromString(StateManagementTypes.OBSERVED_OBJECT) + ), + arkts.TSClassImplements.createTSClassImplements( + UIFactory.createTypeReferenceFromString(StateManagementTypes.SUBSCRIBED_WATCHES) + ), + ], + undefined, + node.super, + factory.observedTrackPropertyMembers(node, ObservedAnno), + node.modifiers, + arkts.classDefinitionFlags(node) + ); + collectStateManagementTypeImport(StateManagementTypes.OBSERVED_OBJECT); + return updateClassDef; + } + + static observedTrackPropertyMembers( + definition: arkts.ClassDefinition, + ObservedAnno: ObservedAnnoInfo + ): arkts.AstNode[] { + const watchMembers: arkts.AstNode[] = PropertyFactory.createWatchMembers(); + const v1RenderIdMembers: arkts.AstNode[] = PropertyFactory.createV1RenderIdMembers(ObservedAnno.isObservedV2); + const conditionalAddRef: arkts.MethodDefinition = PropertyFactory.conditionalAddRef(ObservedAnno.isObservedV2); + const getters: arkts.MethodDefinition[] = getGettersFromClassDecl(definition); + const classScopeInfo: ClassScopeInfo = { + isObserved: ObservedAnno.isObserved, + classHasTrack: ObservedAnno.classHasTrack, + isObservedV2: ObservedAnno.isObservedV2, + classHasTrace: ObservedAnno.classHasTrace, + className: ObservedAnno.className, + getters: getters, + }; + const body: readonly arkts.AstNode[] = definition.body; + const translators: (ObservedTranslator | MethodTranslator)[] = filterDefined( + body.map((it) => classifyInObservedClass(it, classScopeInfo)) + ); + const metaProperty: arkts.ClassProperty[] = ObservedAnno.classHasTrack + ? [] + : [PropertyFactory.createMetaInObservedClass()]; + const propertyMembers = translators.map((translator) => translator.translateMember()); + const restMembers: arkts.AstNode[] = getNoTransformationMembersInClass(definition, ObservedAnno); + const returnNodes = factory.addClassStaticBlock( + [ + ...[...watchMembers, ...v1RenderIdMembers, conditionalAddRef], + ...(ObservedAnno.isObserved ? metaProperty : []), + ...collect(...propertyMembers), + ...restMembers, + ...classScopeInfo.getters, + ], + body + ); + return ObservedAnno.isObservedV2 + ? returnNodes.concat(this.transformObservedV2Constuctor(definition, classScopeInfo.className)) + : returnNodes; + } + + static transformObservedV2Constuctor(definition: arkts.ClassDefinition, className: string): arkts.MethodDefinition { + const addConstructorNodes: arkts.AstNode[] = MonitorCache.getInstance().getCachedMonitors(className); + let originConstructorMethod: arkts.MethodDefinition | undefined = definition.body.find( + (it) => + arkts.isMethodDefinition(it) && + isKnownMethodDefinition(it, CustomComponentNames.COMPONENT_CONSTRUCTOR_ORI) + ) as arkts.MethodDefinition | undefined; + const isDecl: boolean = arkts.hasModifierFlag(definition, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE); + if (!originConstructorMethod) { + return UIFactory.createMethodDefinition({ + key: arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_CONSTRUCTOR_ORI), + function: { + key: arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_CONSTRUCTOR_ORI), + body: isDecl ? undefined : arkts.factory.createBlock(addConstructorNodes), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_CONSTRUCTOR, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONSTRUCTOR, + }, + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONSTRUCTOR, + }); + } + if (isDecl) { + return originConstructorMethod; + } + const originBody = originConstructorMethod.scriptFunction.body as arkts.BlockStatement | undefined; + return UIFactory.updateMethodDefinition(originConstructorMethod, { + function: { + body: originBody + ? arkts.factory.updateBlock(originBody, [...originBody.statements, ...addConstructorNodes]) + : arkts.factory.createBlock(addConstructorNodes), + }, + }); + } + + /* + * helper for transformAnimatableExtend to create callback argument in __createAnimatableProperty + */ + static createAniExtendCbArg( + param: arkts.ETSParameterExpression, + originStatements: arkts.Statement[] + ): arkts.ArrowFunctionExpression { + const assignmentExpr = arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + param.identifier.clone(), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + arkts.factory.createTSAsExpression(param.identifier.clone(), param.type as arkts.TypeNode, false) + ) + ); + const numberType = UIFactory.createTypeReferenceFromString('number'); + const AnimatableArithmeticType = arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(AnimationNames.ANIMATABLE_ARITHMETIC), + arkts.factory.createTSTypeParameterInstantiation([param.type as arkts.TypeNode]) + ) + ); + ImportCollector.getInstance().collectImport(AnimationNames.ANIMATABLE_ARITHMETIC); + return arkts.factory.createArrowFunction( + UIFactory.createScriptFunction({ + body: arkts.factory.createBlock([assignmentExpr, ...originStatements]), + params: [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + param.identifier.name, + arkts.factory.createUnionType([numberType, AnimatableArithmeticType]) + ), + undefined + ), + ], + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + }) + ); + } + + /* + * transform @AnimatableExtend method + */ + static transformAnimatableExtend(node: arkts.ScriptFunction): arkts.ScriptFunction { + if (!arkts.isEtsParameterExpression(node.params[1]) || !node.body || !arkts.isBlockStatement(node.body)) { + return node; + } + const funcName: arkts.StringLiteral = arkts.factory.createStringLiteral(node.id?.name!); + const paramValue: arkts.ETSParameterExpression = node.params[1]; + const originStatements: arkts.Statement[] = [...node.body.statements]; + const createOrSetStatement = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(AnimationNames.CREATE_OR_SET_ANIMATABLEPROPERTY), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + funcName, + paramValue.identifier, + factory.createAniExtendCbArg(paramValue, originStatements.slice(0, -1)), + ] + ) + ); return arkts.factory.updateScriptFunction( node, - node.body, - arkts.factory.createFunctionSignature( + arkts.factory.createBlock([createOrSetStatement, originStatements[originStatements.length - 1]]), + arkts.FunctionSignature.createFunctionSignature( node.typeParams, - [...node.params.slice(0, 2), info, ...node.params.slice(2)], + node.params, node.returnTypeAnnotation, - false + node.hasReceiver ), node.flags, node.modifiers @@ -362,24 +1072,270 @@ export class factory { } /* - * transform ExternalSource headers + * add arrow function type to arguments of call expression. */ - static transformExternalSource(externalSourceName: string, node: arkts.AstNode): arkts.AstNode { + static transformCallArguments(node: arkts.CallExpression): arkts.CallExpression { + const newFunc = UIFactory.createScriptFunction({ + body: arkts.factory.createBlock([arkts.factory.createReturnStatement(node.arguments[0])]), + returnTypeAnnotation: this.getReturnTypeWithArrowParameter(node) ?? this.getReturnTypeWithTsType(node), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + }); + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(newFunc); + collectScriptFunctionReturnTypeFromInfo(newFunc, returnMemoableInfo); + const newArrowArg: arkts.ArrowFunctionExpression = arkts.factory.createArrowFunction(newFunc); + return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ + newArrowArg, + ...node.arguments.slice(1), + ]); + } + + static getReturnTypeWithArrowParameter(node: arkts.CallExpression): arkts.TypeNode | undefined { + const secondArg: arkts.Expression = node.arguments[1]; + if (!arkts.isArrowFunctionExpression(secondArg) || secondArg.scriptFunction.params.length <= 0) { + return undefined; + } + const argTypeParam: arkts.Expression = secondArg.scriptFunction.params[0]; + if (!arkts.isEtsParameterExpression(argTypeParam)) { + return undefined; + } + const type: arkts.AstNode | undefined = argTypeParam.type; + if (type && arkts.isTypeNode(type)) { + return UIFactory.createComplexTypeFromStringAndTypeParameter(TypeNames.ARRAY, [type.clone()]); + } + return undefined; + } + + static getReturnTypeWithTsType(node: arkts.CallExpression): arkts.TypeNode | undefined { + if (node.typeArguments?.length) { + return UIFactory.createComplexTypeFromStringAndTypeParameter(TypeNames.ARRAY, node.typeArguments); + } + const firstArg: arkts.Expression = node.arguments[0]; + const type = arkts.createTypeNodeFromTsType(firstArg); + if (!type || !arkts.isTypeNode(type)) { + return undefined; + } + return type; + } + + static AddArrowTypeForParameter(node: arkts.MethodDefinition): arkts.MethodDefinition { + if (node.scriptFunction.params.length < 2) { + return node; + } + const paramFirst = node.scriptFunction.params[0]; + if (!arkts.isEtsParameterExpression(paramFirst) || !paramFirst.type || !arkts.isTypeNode(paramFirst.type)) { + return node; + } + const script = UIFactory.updateScriptFunction(node.scriptFunction, { + params: [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + paramFirst.identifier.name, + UIFactory.createLambdaFunctionType([], paramFirst.type) + ), + undefined + ), + ...node.scriptFunction.params.slice(1), + ], + }); + return arkts.factory.updateMethodDefinition(node, node.kind, node.name, script, node.modifiers, false); + } + + static transformCallExpression( + node: arkts.CallExpression, + projectConfig: ProjectConfig | undefined, + resourceInfo: ResourceInfo + ): arkts.CallExpression { + if (arkts.isCallExpression(node) && isResourceNode(node)) { + return this.transformResource(node, projectConfig, resourceInfo); + } + if (arkts.isCallExpression(node) && isForEachCall(node)) { + return this.transformCallArguments(node); + } + if (isArkUICompatible(node)) { + return generateArkUICompatible(node as arkts.CallExpression); + } + return node; + } + + static transformCustomDialogController( + node: arkts.ETSNewClassInstanceExpression + ): arkts.ETSNewClassInstanceExpression | arkts.Expression { + if (isInvalidDialogControllerOptions(node.getArguments)) { + return node; + } + const optionArg = node.getArguments[0]; + const options: arkts.ObjectExpression = arkts.isObjectExpression(optionArg) + ? optionArg + : ((optionArg as arkts.TSAsExpression).expr as arkts.ObjectExpression); + const properties = options.properties as arkts.Property[]; + const builderIndex: number = findBuilderIndexInControllerOptions(properties); + if (builderIndex < 0 || !properties.at(builderIndex)!.value) { + return node; + } + const builder: arkts.Property = properties.at(builderIndex)!; + const gensymName: string = GenSymGenerator.getInstance().id(); + const newBuilderValue = this.createDialogBuilderArrow(builder.value!, gensymName); + const newProperty = arkts.factory.updateProperty(builder, builder.key, newBuilderValue); + const newObj = arkts.factory.updateObjectExpression( + options, + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [ + ...(options.properties as arkts.Property[]).slice(0, builderIndex), + newProperty, + ...(options.properties as arkts.Property[]).slice(builderIndex + 1), + this.createBaseComponent(), + ], + false + ); + const newOptions = arkts.isTSAsExpression(optionArg) + ? arkts.factory.updateTSAsExpression(optionArg, newObj, optionArg.typeAnnotation, optionArg.isConst) + : newObj; + const typeRef = node.getTypeRef as arkts.ETSTypeReference; + const newNode = arkts.factory.updateETSNewClassInstanceExpression(node, typeRef, [newOptions]); + return factory.createBlockStatementForOptionalExpression(newNode, gensymName); + } + + static createDialogBuilderArrow(value: arkts.Expression, gensymName: string): arkts.Expression { if ( - externalSourceName === 'arkui.component.common' && - arkts.isTSInterfaceDeclaration(node) && - !!node.id && - node.id.name === 'CommonMethod' + arkts.isCallExpression(value) && + arkts.isMemberExpression(value.expression) && + arkts.isIdentifier(value.expression.property) && + value.expression.property.name === BuilderLambdaNames.TRANSFORM_METHOD_NAME ) { - return factory.modifyExternalComponentCommon(node); - } else if ( - externalSourceName === 'arkui.component.xcomponent' && - arkts.isScriptFunction(node) && - !!node.id && - node.id.name === 'XComponent' + return addMemoAnnotation( + arkts.factory.createArrowFunction( + UIFactory.createScriptFunction({ + body: arkts.factory.createBlock([ + arkts.factory.createExpressionStatement( + this.transformCustomDialogComponentCall(value, gensymName) + ), + ]), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + }) + ) + ); + } + if (arkts.isArrowFunctionExpression(value)) { + return addMemoAnnotation(value); + } + return value; + } + + static transformCustomDialogComponentCall(value: arkts.CallExpression, gensymName: string): arkts.CallExpression { + if (value.arguments.length >= 2 && arkts.isArrowFunctionExpression(value.arguments[1])) { + const originScript: arkts.ScriptFunction = value.arguments[1].scriptFunction; + const newScript: arkts.ScriptFunction = UIFactory.updateScriptFunction(originScript, { + body: this.generateInstanceSetController(originScript.body, gensymName), + }); + return arkts.factory.updateCallExpression(value, value.expression, value.typeArguments, [ + value.arguments[0], + arkts.factory.updateArrowFunction(value.arguments[1], newScript), + ...value.arguments.slice(2), + ]); + } + return value; + } + + static generateInstanceSetController( + body: arkts.AstNode | undefined, + gensymName: string + ): arkts.AstNode | undefined { + if ( + !!body && + arkts.isBlockStatement(body) && + body.statements.length > 0 && + arkts.isReturnStatement(body.statements[0]) ) { - return factory.modifyXcomponent(node); + const instanceIdent: arkts.Identifier = arkts.factory.createIdentifier( + BuilderLambdaNames.STYLE_ARROW_PARAM_NAME + ); + return arkts.factory.updateBlock(body, [ + arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONST, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_CONST, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_CONST, + instanceIdent, + body.statements[0].argument?.clone() + ), + ] + ), + this.genertateControllerSetCall(instanceIdent, gensymName), + arkts.factory.createReturnStatement(instanceIdent.clone()), + ]); } - return node; + return body; + } + + static genertateControllerSetCall(instanceIdent: arkts.Identifier, gensymName: string): arkts.AstNode { + return arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + instanceIdent.clone(), + arkts.factory.createIdentifier(CustomDialogNames.SET_DIALOG_CONTROLLER_METHOD), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createTSAsExpression( + arkts.factory.createIdentifier(gensymName), + UIFactory.createTypeReferenceFromString(CustomDialogNames.CUSTOM_DIALOG_CONTROLLER), + false + ), + ] + ) + ); + } + + static createBaseComponent(): arkts.Property { + return arkts.factory.createProperty( + arkts.factory.createIdentifier(CustomDialogNames.BASE_COMPONENT), + arkts.factory.createThisExpression() + ); + } + + static generateLetVariableDecl(left: arkts.Identifier): arkts.VariableDeclaration { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + left, + undefined + ), + ] + ); + } + + static createBlockStatementForOptionalExpression( + newNode: arkts.ETSNewClassInstanceExpression, + gensymName: string + ): arkts.Expression { + const statements: arkts.Statement[] = [ + factory.generateLetVariableDecl( + arkts.factory.createIdentifier(gensymName, UIFactory.createTypeReferenceFromString(TypeNames.ANY)) + ), + arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + arkts.factory.createIdentifier(gensymName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + newNode + ) + ), + arkts.factory.createExpressionStatement( + arkts.factory.createTSAsExpression( + arkts.factory.createIdentifier(gensymName), + UIFactory.createTypeReferenceFromString(CustomDialogNames.CUSTOM_DIALOG_CONTROLLER), + false + ) + ), + ]; + return arkts.factory.createBlockExpression(statements); } } diff --git a/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts b/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts index be9ebdb11..722453b7a 100644 --- a/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts +++ b/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts @@ -15,205 +15,98 @@ import * as arkts from '@koalaui/libarkts'; import { AbstractVisitor } from '../../common/abstract-visitor'; -import { annotation, collect, filterDefined } from '../../common/arkts-utils'; import { ProjectConfig } from '../../common/plugin-context'; -import { classifyProperty, PropertyTranslator } from '../property-translators'; +import { collectCustomComponentScopeInfo, CustomComponentNames, isCustomComponentClass } from '../utils'; import { - addMemoAnnotation, - CustomComponentNames, - getCustomComponentOptionsName, - getTypeNameFromTypeParameter, - getTypeParamsFromClassDecl, -} from '../utils'; -import { isCustomComponentClass, isKnownMethodDefinition, isEtsGlobalClass, isReourceNode, CustomComponentScopeInfo, findCanAddMemoFromArrowFunction } from './utils'; -import { factory as uiFactory } from '../ui-factory'; + CustomComponentScopeInfo, + isResourceNode, + ScopeInfoCollection, + LoaderJson, + ResourceInfo, + loadBuildJson, + initResourceInfo, +} from './utils'; import { factory } from './factory'; import { isEntryWrapperClass } from '../entry-translators/utils'; import { factory as entryFactory } from '../entry-translators/factory'; -import { DecoratorNames, hasDecorator } from '../property-translators/utils'; -import { ScopeInfoCollection } from './utils'; - -function tranformPropertyMembers( - className: string, - propertyTranslators: PropertyTranslator[], - optionsTypeName: string, - isDecl?: boolean, - scope?: CustomComponentScopeInfo -): arkts.AstNode[] { - const propertyMembers = propertyTranslators.map((translator) => translator.translateMember()); - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(className); - const collections = []; - if (!scope?.hasInitializeStruct) { - collections.push(factory.createInitializeStruct(currentStructInfo, optionsTypeName, isDecl)); - } - if (!scope?.hasUpdateStruct) { - collections.push(factory.createUpdateStruct(currentStructInfo, optionsTypeName, isDecl)); - } - if (currentStructInfo.isReusable) { - collections.push(factory.toRecord(optionsTypeName, currentStructInfo.toRecordBody)); - } - return collect(...collections, ...propertyMembers); -} - -function transformEtsGlobalClassMembers(node: arkts.ClassDeclaration): arkts.ClassDeclaration { - if (!node.definition) { - return node; - } - node.definition.body.map((member: arkts.AstNode) => { - if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.BUILDER)) { - member.scriptFunction.setAnnotations([annotation('memo')]); - } - return member; - }); - return node; -} - -function transformOtherMembersInClass( - member: arkts.AstNode, - classTypeName: string | undefined, - classOptionsName: string | undefined, - className: string, - isDecl?: boolean -): arkts.AstNode { - if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.BUILDER)) { - member.scriptFunction.setAnnotations([annotation('memo')]); - return member; - } - if ( - arkts.isMethodDefinition(member) && - isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_CONSTRUCTOR_ORI) && - !isDecl - ) { - return uiFactory.createConstructorMethod(member); - } - if (arkts.isMethodDefinition(member) && isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_BUILD_ORI)) { - return factory.transformBuildMethodWithOriginBuild( - member, - classTypeName ?? className, - classOptionsName ?? getCustomComponentOptionsName(className), - isDecl - ); - } - return member; -} - -function tranformClassMembers( - node: arkts.ClassDeclaration, - isDecl?: boolean, - scope?: CustomComponentScopeInfo -): arkts.ClassDeclaration { - if (!node.definition) { - return node; - } - - let classTypeName: string | undefined; - let classOptionsName: string | undefined; - if (isDecl) { - const [classType, classOptions] = getTypeParamsFromClassDecl(node); - classTypeName = getTypeNameFromTypeParameter(classType); - classOptionsName = getTypeNameFromTypeParameter(classOptions); - } - const definition: arkts.ClassDefinition = node.definition; - const className: string | undefined = node.definition.ident?.name; - if (!className) { - throw new Error('Non Empty className expected for Component'); - } - - const propertyTranslators: PropertyTranslator[] = filterDefined( - definition.body.map((it) => classifyProperty(it, className)) - ); - const translatedMembers: arkts.AstNode[] = tranformPropertyMembers( - className, - propertyTranslators, - classOptionsName ?? getCustomComponentOptionsName(className), - isDecl, - scope - ); - const updateMembers: arkts.AstNode[] = definition.body - .filter((member) => !arkts.isClassProperty(member)) - .map((member: arkts.AstNode) => - transformOtherMembersInClass(member, classTypeName, classOptionsName, className, isDecl) - ); - - const updateClassDef: arkts.ClassDefinition = factory.updateCustomComponentClass(definition, [ - ...translatedMembers, - ...updateMembers, - ]); - return arkts.factory.updateClassDeclaration(node, updateClassDef); -} - -function transformResource( - resourceNode: arkts.CallExpression, - projectConfig: ProjectConfig | undefined -): arkts.CallExpression { - const newArgs: arkts.AstNode[] = [ - arkts.factory.create1StringLiteral(projectConfig?.bundleName ? projectConfig.bundleName : ''), - arkts.factory.create1StringLiteral(projectConfig?.moduleName ? projectConfig.moduleName : ''), - ...resourceNode.arguments, - ]; - return factory.generateTransformedResource(resourceNode, newArgs); -} +import { ImportCollector } from '../../common/import-collector'; +import { DeclarationCollector } from '../../common/declaration-collector'; +import { generateArkUICompatible, isArkUICompatible } from '../interop/interop'; +import { PropertyCache } from '../property-translators/cache/propertyCache'; export class StructTransformer extends AbstractVisitor { - private scopeInfoCollection: ScopeInfoCollection; + private scope: ScopeInfoCollection; projectConfig: ProjectConfig | undefined; + aceBuildJson: LoaderJson; + resourceInfo: ResourceInfo; constructor(projectConfig: ProjectConfig | undefined) { super(); this.projectConfig = projectConfig; - this.scopeInfoCollection = { customComponents: [] }; + this.scope = { customComponents: [] }; + this.aceBuildJson = loadBuildJson(this.projectConfig); + this.resourceInfo = initResourceInfo(this.projectConfig, this.aceBuildJson); } reset(): void { super.reset(); - this.scopeInfoCollection = { customComponents: [] }; + this.scope = { customComponents: [] }; + PropertyCache.getInstance().reset(); + ImportCollector.getInstance().reset(); + DeclarationCollector.getInstance().reset(); } enter(node: arkts.AstNode): void { - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - this.scopeInfoCollection.customComponents.push({ name: node.definition!.ident!.name }); + if (arkts.isClassDeclaration(node) && !!node.definition && node.definition.body.length > 0) { + const customComponentInfo = collectCustomComponentScopeInfo(node); + if (!!customComponentInfo) { + this.scope.customComponents.push(customComponentInfo); + } } - if (arkts.isMethodDefinition(node) && this.scopeInfoCollection.customComponents.length > 0) { + if (arkts.isMethodDefinition(node) && this.scope.customComponents.length > 0) { const name = node.name.name; - const scopeInfo = this.scopeInfoCollection.customComponents.pop()!; + const scopeInfo = this.scope.customComponents.pop()!; scopeInfo.hasInitializeStruct ||= name === CustomComponentNames.COMPONENT_INITIALIZE_STRUCT; scopeInfo.hasUpdateStruct ||= name === CustomComponentNames.COMPONENT_UPDATE_STRUCT; - scopeInfo.hasReusableRebind ||= name === CustomComponentNames.REUSABLE_COMPONENT_REBIND_STATE; - this.scopeInfoCollection.customComponents.push(scopeInfo); + this.scope.customComponents.push(scopeInfo); } } - exit(node: arkts.AstNode): void { - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - this.scopeInfoCollection.customComponents.pop(); + if ( + arkts.isClassDeclaration(node) && + this.scope.customComponents.length > 0 && + isCustomComponentClass(node, this.scope.customComponents[this.scope.customComponents.length - 1]) + ) { + this.scope.customComponents.pop(); } } visitor(beforeChildren: arkts.AstNode): arkts.AstNode { this.enter(beforeChildren); const node = this.visitEachChild(beforeChildren); - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - let scope: CustomComponentScopeInfo | undefined; - const scopeInfos: CustomComponentScopeInfo[] = this.scopeInfoCollection.customComponents; - if (scopeInfos.length > 0) { - scope = scopeInfos[scopeInfos.length - 1]; - } - const newClass: arkts.ClassDeclaration = tranformClassMembers( - node, - arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE), - scope - ); + if ( + arkts.isClassDeclaration(node) && + this.scope.customComponents.length > 0 && + isCustomComponentClass(node, this.scope.customComponents[this.scope.customComponents.length - 1]) + ) { + const scope: CustomComponentScopeInfo = this.scope.customComponents[this.scope.customComponents.length - 1]; + const newClass: arkts.ClassDeclaration = factory.tranformClassMembers(node, scope); this.exit(beforeChildren); return newClass; } else if (isEntryWrapperClass(node)) { entryFactory.addMemoToEntryWrapperClassMethods(node); return node; - } else if (arkts.isClassDeclaration(node) && isEtsGlobalClass(node)) { - return transformEtsGlobalClassMembers(node); - } else if (arkts.isCallExpression(node) && isReourceNode(node)) { - return transformResource(node, this.projectConfig); - } else if (findCanAddMemoFromArrowFunction(node)) { - return addMemoAnnotation(node); + } else if (arkts.isClassDeclaration(node)) { + return factory.transformNormalClass(node); + } else if (arkts.isCallExpression(node) && isResourceNode(node)) { + return factory.transformResource(node, this.projectConfig, this.resourceInfo); + } else if (isArkUICompatible(node)) { + return generateArkUICompatible(node as arkts.CallExpression); + } else if (arkts.isTSInterfaceDeclaration(node)) { + return factory.tranformInterfaceMembers(node, this.externalSourceName); + } + if (arkts.isEtsScript(node) && ImportCollector.getInstance().importInfos.length > 0) { + ImportCollector.getInstance().insertCurrentImports(this.program); } return node; } diff --git a/arkui-plugins/ui-plugins/struct-translators/utils.ts b/arkui-plugins/ui-plugins/struct-translators/utils.ts index 2af217626..1e32a0786 100644 --- a/arkui-plugins/ui-plugins/struct-translators/utils.ts +++ b/arkui-plugins/ui-plugins/struct-translators/utils.ts @@ -13,104 +13,598 @@ * limitations under the License. */ +import * as fs from 'fs'; +import * as path from 'path'; import * as arkts from '@koalaui/libarkts'; -import { Dollars, isMemoAnnotation } from '../utils'; -import { CustomComponentNames } from '../utils'; -import { DecoratorNames, isDecoratorAnnotation } from '../property-translators/utils'; +import { CustomComponentInfo, CustomComponentNames, CustomDialogNames, isKnownMethodDefinition } from '../utils'; +import { matchPrefix } from '../../common/arkts-utils'; +import { + ARKUI_IMPORT_PREFIX_NAMES, + Dollars, + ModuleType, + DefaultConfiguration, + LogType, + RESOURCE_TYPE, + InnerComponentNames, + ARKUI_FOREACH_SOURCE_NAME, + DecoratorNames, +} from '../../common/predefines'; +import { DeclarationCollector } from '../../common/declaration-collector'; +import { ProjectConfig } from '../../common/plugin-context'; +import { LogCollector } from '../../common/log-collector'; +import { hasDecorator } from '../../ui-plugins/property-translators/utils'; export type ScopeInfoCollection = { customComponents: CustomComponentScopeInfo[]; }; -export type CustomComponentScopeInfo = { - name: string; +export type CustomComponentScopeInfo = CustomComponentInfo & { hasInitializeStruct?: boolean; hasUpdateStruct?: boolean; hasReusableRebind?: boolean; }; +type ResourceMap = Map>; + +export interface ResourceList { + [key: string]: ResourceMap; +} + +export interface ResourceInfo { + resourcesList: ResourceList; + rawfile: Set; +} + +export interface LoaderJson { + hspResourcesMap: Record; +} + +export interface ResourceParameter { + id: number; + type: number; + params: arkts.Expression[]; +} + +export interface ObservedAnnoInfo { + className: string; + isObserved: boolean; + isObservedV2: boolean; + classHasTrace: boolean; + classHasTrack: boolean; +} + +export type ClassScopeInfo = ObservedAnnoInfo & { + getters: arkts.MethodDefinition[]; +}; + +export function getResourceParams(id: number, type: number, params: arkts.Expression[]): ResourceParameter { + return { id, type, params }; +} + /** - * Determine whether it is a custom component. + * Determine whether it is ETSGLOBAL class. * * @param node class declaration node */ -export function isCustomComponentClass(node: arkts.ClassDeclaration): boolean { - if (!node.definition?.ident?.name) { - return false; +export function isEtsGlobalClass(node: arkts.ClassDeclaration): boolean { + if (node.definition?.ident?.name === 'ETSGLOBAL') { + return true; } - const name: string = node.definition.ident.name; - const structCollection: Set = arkts.GlobalInfo.getInfoInstance().getStructCollection(); - return name === CustomComponentNames.COMPONENT_CLASS_NAME || structCollection.has(name); + return false; } /** - * Determine whether it is method with specified name. + * Determine whether it is resource node begin with `$r` or `$rawfile`. * - * @param method method definition node - * @param name specified method name + * @param node call expression node */ -export function isKnownMethodDefinition(method: arkts.MethodDefinition, name: string): boolean { - if (!method || !arkts.isMethodDefinition(method)) { +export function isResourceNode(node: arkts.CallExpression, ignoreDecl: boolean = false): boolean { + if ( + !( + arkts.isIdentifier(node.expression) && + (node.expression.name === Dollars.DOLLAR_RESOURCE || node.expression.name === Dollars.DOLLAR_RAWFILE) + ) + ) { return false; } + if (!ignoreDecl) { + const decl = arkts.getDecl(node.expression); + if (!decl) { + return false; + } + const moduleName: string = arkts.getProgramFromAstNode(decl).moduleName; + if (!moduleName || !matchPrefix(ARKUI_IMPORT_PREFIX_NAMES, moduleName)) { + return false; + } + DeclarationCollector.getInstance().collect(decl); + } + return true; +} - // For now, we only considered matched method name. - const isNameMatched: boolean = method.name?.name === name; - return isNameMatched; +export function isForEachCall(node: arkts.CallExpression): boolean { + if ( + arkts.isIdentifier(node.expression) && + node.expression.name === InnerComponentNames.FOR_EACH && + node.arguments.length >= 2 + ) { + return true; + } + return false; } /** - * Determine whether it is ETSGLOBAL class. + * Read the content of file 'loader.json'. * - * @param node class declaration node + * @param projectConfig configuration information of the project. */ -export function isEtsGlobalClass(node: arkts.ClassDeclaration): boolean { - if (node.definition?.ident?.name === 'ETSGLOBAL') { - return true; +export function loadBuildJson(projectConfig: ProjectConfig | undefined): any { + if (!!projectConfig && projectConfig.buildLoaderJson && fs.existsSync(projectConfig.buildLoaderJson)) { + try { + const content = fs.readFileSync(projectConfig.buildLoaderJson, 'utf-8'); + const parsedContent = JSON.parse(content); + return parsedContent; + } catch (error) { + throw new Error('Error: The file is not a valid JSON format.'); + } } - return false; + return {}; } /** - * Determine whether it is resource node begin with '$r' or '$rawfile'. + * Initialize all resources information, including app resources, system resources, dependent hap resources and rawfile resources. * - * @param node call expression node + * @param projectConfig configuration information of the project. + * @param aceBuildJson content of the file 'loader.json'. */ -export function isReourceNode(node: arkts.CallExpression): boolean { - if (node.expression.dumpSrc() === Dollars.DOLLAR_RESOURCE || node.expression.dumpSrc() === Dollars.DOLLAR_RAWFILE) { - return true; +export function initResourceInfo(projectConfig: ProjectConfig | undefined, aceBuildJson: LoaderJson): ResourceInfo { + let resourcesList: ResourceList = { + app: new Map>(), + sys: new Map>(), + }; + let rawfile: Set = new Set(); + if (!!projectConfig) { + readAppResource(resourcesList, projectConfig, aceBuildJson, rawfile); } - return false; + return { resourcesList, rawfile }; } -export function isMemoCall(node: arkts.AstNode): node is arkts.CallExpression { - if (!arkts.isCallExpression(node)) { - return false; +/** + * Fill in the resource details to the resourcesList and rawfile. + * + * @param resourcesList resources including app, sys and hsp. + * @param projectConfig configuration information of the project. + * @param aceBuildJson content of the file 'loader.json'. + * @param rawfile rawfile resource name set. + */ +function readAppResource( + resourcesList: ResourceList, + projectConfig: ProjectConfig, + aceBuildJson: LoaderJson, + rawfile: Set +): void { + if ('hspResourcesMap' in aceBuildJson && aceBuildJson.hspResourcesMap) { + readHspResource(aceBuildJson, projectConfig, resourcesList); + } + readSystemResource(resourcesList); + if (!!projectConfig.appResource && fs.existsSync(projectConfig.appResource)) { + const appResource: string = fs.readFileSync(projectConfig.appResource, 'utf-8'); + const resourceArr: string[] = appResource.split(/\n/); + const resourceMap: ResourceMap = new Map>(); + processResourceArr(resourceArr, resourceMap, projectConfig.appResource); + for (let [key, value] of resourceMap) { + resourcesList.app.set(key, value); + } } - const expr: arkts.AstNode = node.expression; - const decl: arkts.AstNode | undefined = arkts.getDecl(expr); + if (projectConfig.rawFileResource) { + processResourcesRawfile(projectConfig, projectConfig.rawFileResource, rawfile); + } +} - if (!decl) { - return false; +/** + * Fill in the resource details to the system resource. + * + * @param resourcesList resources including app, sys and hsp. + */ +function readSystemResource(resourcesList: ResourceList): void { + const sysResourcePath = path.resolve(__dirname, '../sysResource.js'); + if (fs.existsSync(sysResourcePath)) { + const sysObj: Record> = require(sysResourcePath).sys; + Object.keys(sysObj).forEach((key: string) => { + resourcesList.sys.set(key, sysObj[key]); + }); } +} - if (arkts.isMethodDefinition(decl)) { - return decl.scriptFunction.annotations.some( - (anno) => isDecoratorAnnotation(anno, DecoratorNames.BUILDER) || isMemoAnnotation(anno, 'memo') - ); +/** + * generate resource map. + * + * @param resourceArr lines of file 'ResourceTable.txt'. + * @param resourceMap A map that records the mapping of resource type, name and id. + * @param resourcePath path of file 'ResourceTable.txt'. + */ +function processResourceArr( + resourceArr: string[], + resourceMap: Map>, + resourcePath: string +): void { + for (let i = 0; i < resourceArr.length; i++) { + if (!resourceArr[i].length) { + continue; + } + const resourceData = resourceArr[i].split(/\s/); + if (resourceData.length === 3 && !isNaN(Number(resourceData[2]))) { + rescordResourceNameAndIdMap(resourceMap, resourceData); + } else { + console.warn(`ArkTS:WARN The format of file '${resourcePath}' is incorrect.`); + break; + } + } +} + +/** + * Construct the mapping of resource type, name and id with 'ResourceTable.txt'. + * + * @param resourceMap A map that records the mapping of resource type, name and id. + * @param resourceData array of type, name and id. + */ +function rescordResourceNameAndIdMap(resourceMap: Map>, resourceData: string[]): void { + if (resourceMap.get(resourceData[0])) { + const resourceNameAndId: Record = resourceMap.get(resourceData[0])!; + if (!resourceNameAndId[resourceData[1]] || resourceNameAndId[resourceData[1]] !== Number(resourceData[2])) { + resourceNameAndId[resourceData[1]] = Number(resourceData[2]); + } + } else { + let obj: Record = {}; + obj[resourceData[1]] = Number(resourceData[2]); + resourceMap.set(resourceData[0], obj); + } +} + +/** + * Fill in the resource details to the hsp resource. + * + * @param projectConfig configuration information of the project. + * @param aceBuildJson content of the file 'loader.json'. + * @param resourcesList resources including app, sys and hsp. + */ +function readHspResource(aceBuildJson: LoaderJson, projectConfig: ProjectConfig, resourcesList: ResourceList): void { + projectConfig.hspResourcesMap = true; + for (const hspName in aceBuildJson.hspResourcesMap) { + if (fs.existsSync(aceBuildJson.hspResourcesMap[hspName])) { + const resourceMap: ResourceMap = new Map>(); + resourcesList[hspName] = new Map>(); + const hspResource: string = fs.readFileSync(aceBuildJson.hspResourcesMap[hspName], 'utf-8'); + const resourceArr: string[] = hspResource.split(/\n/); + processResourceArr(resourceArr, resourceMap, aceBuildJson.hspResourcesMap[hspName]); + for (const [key, value] of resourceMap) { + resourcesList[hspName].set(key, value); + } + } + } +} + +/** + * Record the information of the rawfile resource. + * + * @param projectConfig configuration information of the project. + * @param rawfilePath path of rawfile directory. + * @param rawfileSet a set includes rawfile resource names. + * @param resourceName combination of existing directory names. + */ +function processResourcesRawfile( + projectConfig: ProjectConfig, + rawfilePath: string, + rawfileSet: Set, + resourceName: string = '' +): void { + if (fs.existsSync(projectConfig.rawFileResource) && fs.statSync(rawfilePath).isDirectory()) { + const files: string[] = fs.readdirSync(rawfilePath); + files.forEach((file: string) => { + if (fs.statSync(path.join(rawfilePath, file)).isDirectory()) { + processResourcesRawfile( + projectConfig, + path.join(rawfilePath, file), + rawfileSet, + resourceName ? resourceName + '/' + file : file + ); + } else { + addRawfileResourceToSet(rawfileSet, file, resourceName); + } + }); + } +} + +/** + * Add rawfile name to the collection of rawfile set. + * + * @param rawfileSet a set includes rawfile resource names. + * @param file rawfile name. + * @param resourceName combination of existing directory names. + */ +function addRawfileResourceToSet(rawfileSet: Set, file: string, resourceName: string = ''): void { + if (resourceName) { + rawfileSet.add(resourceName + '/' + file); + } else { + rawfileSet.add(file); + } +} + +/** + * Verify whether the rawfile resource exists in the current module. + * + * @param resourceNode resource node. + * @param rawfileStr rawfile string. + * @param fromOtherModule flag about whether it is a resource for other modules. + * @param rawfileSet a set that records all the rawfile resources. + */ +export function checkRawfileResource( + resourceNode: arkts.CallExpression, + rawfileStr: arkts.StringLiteral, + fromOtherModule: boolean, + rawfileSet: Set +): void { + if (!fromOtherModule && !rawfileSet.has(rawfileStr.str)) { + LogCollector.getInstance().collectLogInfo({ + type: LogType.ERROR, + node: resourceNode, + message: `No such '${rawfileStr.str}' resource in current module.`, + code: '10904333', + }); } - return false; } -export function findCanAddMemoFromArrowFunction(node: arkts.AstNode): node is arkts.ArrowFunctionExpression { - if (!arkts.isArrowFunctionExpression(node)) { +/** + * Check the format and the existance of resource string literal. + * + * @param resourceData array of resource string literals. + * @param resourcesList resources including app, sys and hsp. + * @param literalArg string literal argument node. + * @param fromOtherModule flag about whether it is a resource for other modules. + * @param projectConfig configuration information of the project. + */ +export function preCheckResourceData( + resourceNode: arkts.CallExpression, + resourceData: string[], + resourcesList: ResourceList, + fromOtherModule: boolean, + projectConfig: ProjectConfig +): boolean { + let code: string | undefined; + let message: string | undefined; + if (resourceData.length !== 3) { + message = 'The input parameter is not supported.'; + code = '10905332'; + } + if (!RESOURCE_TYPE[resourceData[1]]) { + message = `The resource type ${resourceData[1]} is not supported.`; + code = '10906334'; + } + if (!!code && !!message) { + LogCollector.getInstance().collectLogInfo({ + type: LogType.ERROR, + node: resourceNode, + message: message, + code: code, + }); return false; } - const hasMemo: boolean = node.annotations.some((anno) => isMemoAnnotation(anno, 'memo')); - if (!hasMemo && !!node.scriptFunction.body && arkts.isBlockStatement(node.scriptFunction.body)) { - return node.scriptFunction.body.statements.some( - (st) => arkts.isExpressionStatement(st) && isMemoCall(st.expression) - ); + return preCheckResourceDataExistance(resourceNode, resourceData, resourcesList, fromOtherModule, projectConfig); +} + +/** + * Check the existance of resource string literal when the format of the string literal is correct. + * + * @param resourceData array of resource string literals. + * @param resourcesList resources including app, sys and hsp. + * @param literalArg string literal argument node. + * @param fromOtherModule flag about whether it is a resource for other modules. + * @param projectConfig configuration information of the project. + */ +export function preCheckResourceDataExistance( + resourceNode: arkts.CallExpression, + resourceData: string[], + resourcesList: ResourceList, + fromOtherModule: boolean, + projectConfig: ProjectConfig +): boolean { + if (fromOtherModule) { + if (/^\[.*\]$/.test(resourceData[0]) && projectConfig.hspResourcesMap) { + const resourceDataFirst: string = resourceData[0].replace(/^\[/, '').replace(/\]$/, '').trim(); + return resourceCheck(resourceNode, resourceData, resourcesList, true, resourceDataFirst, false); + } else { + return resourceCheck(resourceNode, resourceData, resourcesList, false, resourceData[0], true); + } + } else { + return resourceCheck(resourceNode, resourceData, resourcesList, false, resourceData[0], false); } - return false; +} + +/** + * Verify whether the app resource exists in the current module. + * + * @param resourceNode resource node. + * @param resourceData array of resource string literals. + * @param resourcesList resources including app, sys and hsp. + * @param isHarHspResourceModule flag about whether it is from hsp or har module. + * @param resourceDataFirst the first element of resource string literals. + * @param noHspResourcesMap the non-existence of hspResourcesMap. + */ +function resourceCheck( + resourceNode: arkts.CallExpression, + resourceData: string[], + resourcesList: ResourceList, + isHarHspResourceModule: boolean, + resourceDataFirst: string, + noHspResourcesMap: boolean +): boolean { + let checkResult: boolean = true; + const logType: LogType = isHarHspResourceModule ? LogType.WARN : LogType.ERROR; + let code: string | undefined; + let message: string | undefined; + if (!noHspResourcesMap && !resourcesList[resourceDataFirst]) { + code = '10903331'; + message = `Unknown resource source '${resourceDataFirst}'.`; + checkResult = isHarHspResourceModule ? checkResult : false; + } else if (!noHspResourcesMap && !resourcesList[resourceDataFirst].get(resourceData[1])) { + code = '10903330'; + message = `Unknown resource type '${resourceData[1]}'.`; + checkResult = isHarHspResourceModule ? checkResult : false; + } else if (!noHspResourcesMap && !resourcesList[resourceDataFirst].get(resourceData[1])![resourceData[2]]) { + code = '10903329'; + message = `Unknown resource name '${resourceData[2]}'.`; + checkResult = isHarHspResourceModule ? checkResult : false; + } + if (!!code && !!message) { + LogCollector.getInstance().collectLogInfo({ + type: logType, + node: resourceNode, + message: message, + code: code, + }); + } + return checkResult; +} + +/** + * generate bundleName for $r and $rawfile. + * + * @param projectConfig project config. + * @param isDynamicBundleOrModule a flag for determining whether to use dynamic module name and bundle name. + */ +export function generateResourceBundleName(projectConfig: ProjectConfig, isDynamicBundleOrModule: boolean): string { + if (projectConfig.resetBundleName || projectConfig.allowEmptyBundleName) { + return ''; + } + if (isDynamicBundleOrModule) { + return DefaultConfiguration.DYNAMIC_BUNDLE_NAME; + } + return projectConfig.moduleType === ModuleType.HAR + ? DefaultConfiguration.HAR_DEFAULT_BUNDLE_NAME + : projectConfig.bundleName + ? projectConfig.bundleName + : ''; +} + +/** + * generate moduleName for $r and $rawfile. + * + * @param projectConfig project config. + * @param isDynamicBundleOrModule a flag for determining whether to use dynamic module name and bundle name. + */ +export function generateResourceModuleName( + projectConfig: ProjectConfig, + isDynamicBundleOrModule: boolean = false, + resourceModuleName: string, + fromOtherModule: boolean +): string { + if (fromOtherModule && resourceModuleName) { + return resourceModuleName.replace(/^\[|\]$/g, ''); + } + if (isDynamicBundleOrModule) { + return DefaultConfiguration.DYNAMIC_MODULE_NAME; + } + return projectConfig.moduleType === ModuleType.HAR + ? DefaultConfiguration.HAR_DEFAULT_MODULE_NAME + : projectConfig.moduleName + ? projectConfig.moduleName + : ''; +} + +/** + * Determine whether to use dynamic module name and bundle name. + * + * @param projectConfig project config. + */ +export function isDynamicName(projectConfig: ProjectConfig): boolean { + const isByteCodeHar: boolean = projectConfig.moduleType === ModuleType.HAR && projectConfig.byteCodeHar; + const uiTransformOptimization: boolean = !!projectConfig.uiTransformOptimization; + return uiTransformOptimization ? uiTransformOptimization : isByteCodeHar; +} + +/** + * Determine whether the node is ForEach method declaration. + * + * @param node method definition node. + * @param sourceName external source name. + */ +export function isForEachDecl(node: arkts.MethodDefinition, sourceName: string | undefined): boolean { + const isForEach: boolean = !!node.name && node.name.name === InnerComponentNames.FOR_EACH; + const isMethodDecl: boolean = + !!node.scriptFunction && + arkts.hasModifierFlag(node.scriptFunction, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE); + return isForEach && isMethodDecl && !!sourceName && sourceName === ARKUI_FOREACH_SOURCE_NAME; +} + +export function getCustomDialogController(node: arkts.ClassProperty): string { + const key = node.key; + let controllerName: string = ''; + if (!(key && arkts.isIdentifier(key) && node.typeAnnotation)) { + return ''; + } + const typeAnno = node.typeAnnotation; + if (arkts.isETSUnionType(typeAnno)) { + for (const type of typeAnno.types) { + if (arkts.isETSTypeReference(type)) { + controllerName = hasController(type, key.name); + } + } + } else if (arkts.isETSTypeReference(typeAnno)) { + controllerName = hasController(typeAnno, key.name); + } + return controllerName; +} + +export function hasController(node: arkts.ETSTypeReference, keyName: string): string { + const ident = node.part?.name; + if (ident && arkts.isIdentifier(ident) && ident.name === CustomDialogNames.CUSTOM_DIALOG_CONTROLLER) { + return keyName; + } + return ''; +} + +export function isInvalidDialogControllerOptions(args: readonly arkts.Expression[]): boolean { + const firstOptionsParameter: arkts.AstNode = args[0]; + return ( + args.length <= 0 || + !(isObjectAsExpression(firstOptionsParameter) || arkts.isObjectExpression(firstOptionsParameter)) + ); +} + +function isObjectAsExpression(param: arkts.AstNode): boolean { + return arkts.isTSAsExpression(param) && !!param.expr && arkts.isObjectExpression(param.expr); +} + +export function isComputedMethod(node: arkts.AstNode): boolean { + return arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.COMPUTED); +} + +export function findBuilderIndexInControllerOptions(properties: readonly arkts.Expression[]): number { + return properties.findIndex((item: arkts.Expression) => { + return ( + arkts.isProperty(item) && + !!item.key && + arkts.isIdentifier(item.key) && + item.key.name === CustomDialogNames.OPTIONS_BUILDER + ); + }); +} + +export function getNoTransformationMembersInClass( + definition: arkts.ClassDefinition, + ObservedAnno: ObservedAnnoInfo +): arkts.AstNode[] { + return definition.body.filter( + (member) => + !arkts.isClassProperty(member) && + !( + arkts.isMethodDefinition(member) && + arkts.hasModifierFlag(member, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_GETTER) + ) && + !( + ObservedAnno.isObservedV2 && + arkts.isMethodDefinition(member) && + (hasDecorator(member, DecoratorNames.COMPUTED) || + isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_CONSTRUCTOR_ORI)) + ) + ); } diff --git a/arkui-plugins/ui-plugins/ui-factory.ts b/arkui-plugins/ui-plugins/ui-factory.ts index 2b64ac64f..8b7280f0d 100644 --- a/arkui-plugins/ui-plugins/ui-factory.ts +++ b/arkui-plugins/ui-plugins/ui-factory.ts @@ -14,9 +14,44 @@ */ import * as arkts from '@koalaui/libarkts'; -import { BuilderLambdaNames, CustomComponentNames, hasPropertyInAnnotation, hasNullOrUndefinedType } from './utils'; -import { annotation } from '../common/arkts-utils'; -import { DecoratorNames, needDefiniteOrOptionalModifier } from './property-translators/utils'; +import { + BuilderLambdaNames, + CustomComponentAnontations, + CustomComponentNames, + CustomDialogNames, + hasNullOrUndefinedType, + hasPropertyInAnnotation, +} from './utils'; +import { PartialExcept, PartialNested, PartialNestedExcept } from '../common/safe-types'; +import { ArkTsDefaultNames, DecoratorNames } from '../common/predefines'; +import { needDefiniteOrOptionalModifier } from './property-translators/utils'; +import { addMemoAnnotation } from '../collectors/memo-collectors/utils'; + +export interface ScriptFunctionConfiguration { + key: arkts.Identifier | undefined; + body: arkts.AstNode | undefined; + typeParams: arkts.TSTypeParameterDeclaration | undefined; + params: readonly arkts.Expression[]; + returnTypeAnnotation: arkts.TypeNode | undefined; + hasReceiver: boolean; + flags: arkts.Es2pandaScriptFunctionFlags; + modifiers: arkts.Es2pandaModifierFlags; + annotations: arkts.AnnotationUsage[]; +} + +export interface MethodDefinitionConfiguration { + key: arkts.Identifier; + kind: arkts.Es2pandaMethodDefinitionKind; + function: ScriptFunctionConfiguration; + overloads: arkts.MethodDefinition[]; + modifiers: arkts.Es2pandaModifierFlags; + isComputed: boolean; +} + +export interface IntrinsicAnnotationDeclarationConfiguration { + expr: arkts.Identifier; + properties: arkts.AstNode[]; +} export class factory { /** @@ -64,16 +99,6 @@ export class factory { ); } - /** - * create `@memo() style: ((instance: ) => void) | undefined` as parameter - */ - static createStyleParameter(typeName: string): arkts.ETSParameterExpression { - const styleParam: arkts.Identifier = factory.createStyleIdentifier(typeName); - const param = arkts.factory.createParameterDeclaration(styleParam, undefined); - param.annotations = [annotation('memo')]; - return param; - } - /** * create `initializers: | undefined` as identifier */ @@ -112,8 +137,8 @@ export class factory { */ static createContentParameter(): arkts.ETSParameterExpression { const contentParam: arkts.Identifier = factory.createContentIdentifier(); - const param = arkts.factory.createParameterDeclaration(contentParam, undefined); - param.annotations = [annotation('memo')]; + const param: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration(contentParam, undefined); + addMemoAnnotation(param); return param; } @@ -126,6 +151,18 @@ export class factory { ); } + /** + * create complex type from string and type parameter, e.g. `Set` + */ + static createComplexTypeFromStringAndTypeParameter(name: string, params: readonly arkts.TypeNode[]): arkts.TypeNode { + return arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(name), + arkts.factory.createTSTypeParameterInstantiation(params) + ) + ); + } + /** * create `() => `. If returnType is not given, then using `void`. */ @@ -145,27 +182,6 @@ export class factory { } /** - * create and insert `import { as } from ` to the top of script's statements. - */ - static createAndInsertImportDeclaration( - source: arkts.StringLiteral, - imported: arkts.Identifier, - local: arkts.Identifier, - importKind: arkts.Es2pandaImportKinds, - program: arkts.Program - ): void { - const importDecl: arkts.ETSImportDeclaration = arkts.factory.createImportDeclaration( - source, - [arkts.factory.createImportSpecifier(imported, local)], - importKind, - program, - arkts.Es2pandaImportFlags.IMPORT_FLAGS_NONE - ); - arkts.importDeclarationInsert(importDecl, program); - return; - } - - /* * create `import { as } ...`. */ static createAdditionalImportSpecifier(imported: string, local: string): arkts.ImportSpecifier { @@ -175,62 +191,129 @@ export class factory { ); } - /* - * create `constructor() {}`. + /** + * update ScriptFunction with configurations. */ - static createConstructorMethod(member: arkts.MethodDefinition): arkts.MethodDefinition { - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, - member.name, - member.scriptFunction, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONSTRUCTOR, - false + static updateScriptFunction( + original: arkts.ScriptFunction, + config: Partial + ): arkts.ScriptFunction { + const newFunc: arkts.ScriptFunction = arkts.factory.updateScriptFunction( + original, + config.body ?? original.body, + arkts.factory.createFunctionSignature( + config.typeParams ?? original.typeParams, + config.params ?? original.params, + config.returnTypeAnnotation ?? original.returnTypeAnnotation, + config.hasReceiver ?? original.hasReceiver + ), + config.flags ?? original.flags, + config.modifiers ?? original.modifiers + ); + if (!!config.key) { + newFunc.setIdent(config.key); + } + if (!!config.annotations) { + newFunc.setAnnotations(config.annotations); + } + return newFunc; + } + + /** + * create ScriptFunction with configurations. + */ + static createScriptFunction(config: Partial): arkts.ScriptFunction { + const newFunc: arkts.ScriptFunction = arkts.factory.createScriptFunction( + config.body ?? undefined, + arkts.factory.createFunctionSignature( + config.typeParams ?? undefined, + config.params ?? [], + config.returnTypeAnnotation ?? undefined, + config.hasReceiver ?? false + ), + config.flags ?? arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_NONE, + config.modifiers ?? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE ); + if (!!config.key) { + newFunc.setIdent(config.key); + } + if (!!config.annotations) { + newFunc.setAnnotations(config.annotations); + } + return newFunc; } - /* - * create `@memo() _build(<>)`. + /** + * update MethodDefinition with configurations. */ - static transformBuildMethodWithOriginBuild( - method: arkts.MethodDefinition, - typeName: string, - optionsName: string, - isDecl?: boolean + static updateMethodDefinition( + original: arkts.MethodDefinition, + config: PartialNested ): arkts.MethodDefinition { - const updateKey: arkts.Identifier = arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_BUILD); - - const scriptFunction: arkts.ScriptFunction = method.scriptFunction; - const updateScriptFunction = arkts.factory - .createScriptFunction( - scriptFunction.body, - arkts.FunctionSignature.createFunctionSignature( - scriptFunction.typeParams, - [ - factory.createStyleParameter(typeName), - factory.createContentParameter(), - factory.createInitializersOptionsParameter(optionsName), - ], - arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), + const key: arkts.Identifier = config.key ?? original.name; + const newFunc: arkts.ScriptFunction = factory.updateScriptFunction(original.scriptFunction, { + ...config.function, + key, + }); + const newMethod: arkts.MethodDefinition = arkts.factory.updateMethodDefinition( + original, + config.kind ?? original.kind, + key, + newFunc, + config.modifiers ?? original.modifiers, + config.isComputed ?? false + ); + if (!!config.overloads) { + newMethod.setOverloads(config.overloads); + } + return newMethod; + } + + /** + * create MethodDefinition with configurations. + */ + static createMethodDefinition(config: PartialNested): arkts.MethodDefinition { + const newFunc: arkts.ScriptFunction = factory.createScriptFunction({ + ...config.function, + key: config.key, + }); + const newMethod: arkts.MethodDefinition = arkts.factory.createMethodDefinition( + config.kind ?? arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_NONE, + config.key!, + newFunc, + config.modifiers ?? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + config.isComputed ?? false + ); + if (!!config.overloads) { + newMethod.setOverloads(config.overloads); + } + return newMethod; + } + + /** + * create intrinsic `@Retention({policy:"SOURCE"})` AnnotationDeclaration with configurations. + */ + static createIntrinsicAnnotationDeclaration( + config: PartialExcept + ): arkts.AnnotationDeclaration { + const intrinsicAnnotations: arkts.AnnotationUsage[] = [ + arkts.factory.create1AnnotationUsage(arkts.factory.createIdentifier('Retention'), [ + arkts.factory.createClassProperty( + arkts.factory.createIdentifier('policy'), + arkts.factory.createStringLiteral('SOURCE'), + undefined, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, false ), - scriptFunction.flags, - scriptFunction.modifiers - ) - .setAnnotations([annotation('memo')]); - - const modifiers: arkts.Es2pandaModifierFlags = isDecl - ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_ABSTRACT - : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, - updateKey, - updateScriptFunction, - modifiers, - false - ); + ]), + ]; + const newAnnotationDecl: arkts.AnnotationDeclaration = arkts.factory + .createAnnotationDeclaration(config.expr, config.properties ?? []) + .setAnnotations(intrinsicAnnotations); + return newAnnotationDecl; } - /* + /** * add alias: to @Provide annotation when no alias in @Provide({...}). */ static processNoAliasProvideVariable(property: arkts.ClassProperty): void { @@ -258,7 +341,7 @@ export class factory { property.setAnnotations(newAnnos); } - /* + /** * create class property : `alias: `. */ static createAliasClassProperty(value: arkts.Identifier): arkts.ClassProperty { @@ -271,17 +354,138 @@ export class factory { ); } - /* + /** * add optional or definite modifier for class property needs initializing without assignment. */ - static PreprocessClassPropertyModifier(st: arkts.AstNode): arkts.AstNode { - if (arkts.isClassProperty(st) && needDefiniteOrOptionalModifier(st)) { + static PreprocessClassPropertyModifier(st: arkts.AstNode, isDecl: boolean): arkts.AstNode { + if (!isDecl && arkts.isClassProperty(st) && needDefiniteOrOptionalModifier(st)) { if (st.typeAnnotation && hasNullOrUndefinedType(st.typeAnnotation)) { - st.modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL; + st.modifiers |= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL; } else { - st.modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DEFINITE; + st.modifiers |= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DEFINITE; } } return st; } + + /** + * create class implements : `implements `. + */ + static createClassImplements( + interfaceName: string, + typeParameters?: arkts.TSTypeParameterInstantiation + ): arkts.TSClassImplements { + return arkts.factory.createTSClassImplements( + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(interfaceName)) + ), + typeParameters + ); + } + + /** + * Generate class implements for struct with struct annotations. + * + * @param method method definition node + */ + static generateImplementsForStruct(annotations: CustomComponentAnontations): arkts.TSClassImplements[] { + const implementsInfo: arkts.TSClassImplements[] = []; + if (annotations.entry) { + implementsInfo.push(factory.createClassImplements(CustomComponentNames.PAGE_LIFE_CYCLE)); + } + if (annotations.customLayout) { + implementsInfo.push(factory.createClassImplements(CustomComponentNames.LAYOUT_CALLBACK)); + } + return implementsInfo; + } + + /** + * create class property node: `:`. + * + * @param method method definition node + */ + static createPropertyInInterface(key: string, type?: arkts.TypeNode): arkts.ClassProperty { + const keyIdent: arkts.Identifier = arkts.factory.createIdentifier(key); + return arkts.factory.createClassProperty( + keyIdent, + undefined, + type, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL, + false + ); + } + + /** + * add `baseComponent: ExtendableComponent` to interface CustomDialogControllerOptions. + * + * @param method method definition node + */ + static updateCustomDialogOptionsInterface(newNode: arkts.TSInterfaceDeclaration): arkts.TSInterfaceDeclaration { + if (!newNode.body?.body || newNode.body?.body.length <= 0) { + return newNode; + } + + return arkts.factory.updateInterfaceDeclaration( + newNode, + newNode.extends, + newNode.id, + newNode.typeParams, + arkts.factory.updateInterfaceBody(newNode.body!, [ + ...newNode.body.body, + factory.createPropertyInInterface( + CustomDialogNames.BASE_COMPONENT, + factory.createTypeReferenceFromString(CustomDialogNames.EXTENDABLE_COMPONENT) + ), + ]), + newNode.isStatic, + newNode.isFromExternal + ); + } + + /** + * Generate member expression, e.g. `.`. + * + * @param method method definition node + */ + static generateMemberExpression(object: arkts.AstNode, property: string, optional = false): arkts.MemberExpression { + return arkts.factory.createMemberExpression( + object, + arkts.factory.createIdentifier(property), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + optional + ); + } + + /** + * create `: = ` as parameter + */ + static createParameterDeclaration( + keyName: string, + typeName: string, + initializers?: arkts.AstNode + ): arkts.ETSParameterExpression { + return arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(keyName, this.createTypeReferenceFromString(typeName)), + initializers + ); + } + + /** + * create class static block, e.g. `static {}`. + */ + static createClassStaticBlock(): arkts.ClassStaticBlock { + return arkts.factory.createClassStaticBlock( + arkts.factory.createFunctionExpression( + factory.createScriptFunction({ + key: arkts.factory.createIdentifier(ArkTsDefaultNames.DEFAULT_STATIC_BLOCK_NAME), + body: arkts.factory.createBlock([]), + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, + flags: + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_STATIC_BLOCK | + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_EXPRESSION, + }) + ) + ); + } } diff --git a/arkui-plugins/ui-plugins/utils.ts b/arkui-plugins/ui-plugins/utils.ts index 847a830a5..210a2aeab 100644 --- a/arkui-plugins/ui-plugins/utils.ts +++ b/arkui-plugins/ui-plugins/utils.ts @@ -14,25 +14,42 @@ */ import * as arkts from '@koalaui/libarkts'; -import { annotation } from '../common/arkts-utils'; -import { DecoratorNames } from './property-translators/utils'; +import { matchPrefix } from '../common/arkts-utils'; +import { + ARKUI_IMPORT_PREFIX_NAMES, + CUSTOM_DIALOG_CONTROLLER_SOURCE_NAME, + DecoratorNames, + StructDecoratorNames, +} from '../common/predefines'; +import { DeclarationCollector } from '../common/declaration-collector'; +import { hasDecorator } from './property-translators/utils'; export enum CustomComponentNames { - ENTRY_ANNOTATION_NAME = 'Entry', - COMPONENT_ANNOTATION_NAME = 'Component', - RESUABLE_ANNOTATION_NAME = 'Reusable', COMPONENT_BUILD_ORI = 'build', COMPONENT_CONSTRUCTOR_ORI = 'constructor', - COMPONENT_DEFAULT_IMPORT = 'arkui.component.customComponent', COMPONENT_CLASS_NAME = 'CustomComponent', + COMPONENT_V2_CLASS_NAME = 'CustomComponentV2', + BASE_CUSTOM_DIALOG_NAME = 'BaseCustomDialog', COMPONENT_INTERFACE_PREFIX = '__Options_', COMPONENT_INITIALIZE_STRUCT = '__initializeStruct', COMPONENT_UPDATE_STRUCT = '__updateStruct', - COMPONENT_BUILD = '_build', - REUSABLE_COMPONENT_REBIND_STATE = '__rebindStates', COMPONENT_INITIALIZERS_NAME = 'initializers', BUILDCOMPATIBLENODE = '_buildCompatibleNode', OPTIONS = 'options', + PAGE_LIFE_CYCLE = 'PageLifeCycle', + LAYOUT_CALLBACK = 'LayoutCallback', +} + +export enum CustomDialogNames { + CUSTOM_DIALOG_ANNOTATION_NAME = 'CustomDialog', + CUSTOM_DIALOG_CONTROLLER = 'CustomDialogController', + CUSTOM_DIALOG_CONTROLLER_OPTIONS = 'CustomDialogControllerOptions', + SET_DIALOG_CONTROLLER_METHOD = '__setDialogController__', + CONTROLLER = 'controller', + OPTIONS_BUILDER = 'builder', + BASE_COMPONENT = 'baseComponent', + EXTENDABLE_COMPONENT = 'ExtendableComponent', + CUSTOM_BUILDER = 'CustomBuilder', } export enum BuilderLambdaNames { @@ -42,15 +59,23 @@ export enum BuilderLambdaNames { STYLE_PARAM_NAME = 'style', STYLE_ARROW_PARAM_NAME = 'instance', CONTENT_PARAM_NAME = 'content', - ANIMATION_NAME = 'animation', - ANIMATION_START = 'animationStart', - ANIMATION_STOP = 'animationStop', } -export enum Dollars { - DOLLAR_RESOURCE = '$r', - DOLLAR_RAWFILE = '$rawfile', - DOLLAR_DOLLAR = '$$', +// IMPORT +export function findImportSourceByName(importName: string): string { + const source = DeclarationCollector.getInstance().findExternalSourceFromName(importName); + if (!source) { + throw new Error(`cannot find import source by name: "${importName}".`); + } + return source; +} + +export function findImportSourceByNode(declNode: arkts.AstNode): string { + const source = DeclarationCollector.getInstance().findExternalSourceFromNode(declNode); + if (!source) { + throw new Error(`cannot find import source by peer.`); + } + return source; } export function findLocalImport( @@ -67,22 +92,30 @@ export function findLocalImport( return importSpecifier?.local ?? importSpecifier?.imported; } -// TODO: currently, we forcely assume initializerOptions is named in pattern __Options_xxx -export function getCustomComponentNameFromInitializerOptions(name: string): string | undefined { - const prefix: string = CustomComponentNames.COMPONENT_INTERFACE_PREFIX; - if (name.startsWith(prefix)) { - return name.substring(prefix.length); - } -} - -export function getCustomComponentOptionsName(className: string): string { - return `${CustomComponentNames.COMPONENT_INTERFACE_PREFIX}${className}`; -} - +// AST NODE export function isStatic(node: arkts.AstNode): boolean { return node.isStatic; } +/** + * Determine whether the type node includes null or undefined type. + * + * @param type type node + */ +export function hasNullOrUndefinedType(type: arkts.TypeNode): boolean { + let res: boolean = false; + if (arkts.isETSUnionType(type)) { + type.types.forEach((item: arkts.TypeNode) => { + res = res || hasNullOrUndefinedType(item); + }); + } + if (arkts.isETSUndefinedType(type) || arkts.isETSNullType(type)) { + res = true; + } + return res; +} + +// TYPE PARAMETER export function getTypeParamsFromClassDecl(node: arkts.ClassDeclaration | undefined): readonly arkts.TSTypeParameter[] { return node?.definition?.typeParams?.params ?? []; } @@ -91,109 +124,264 @@ export function getTypeNameFromTypeParameter(node: arkts.TSTypeParameter | undef return node?.name?.name; } -export function createOptionalClassProperty( - name: string, - property: arkts.ClassProperty, - stageManagementIdent: string, - modifiers: arkts.Es2pandaModifierFlags, - needMemo: boolean = false -): arkts.ClassProperty { - const newType: arkts.TypeNode | undefined = property.typeAnnotation?.clone(); - if (needMemo) { - newType?.setAnnotations([annotation('memo')]); - } - const newProperty = arkts.factory.createClassProperty( - arkts.factory.createIdentifier(name), - undefined, - stageManagementIdent.length ? createStageManagementType(stageManagementIdent, property) : newType, - modifiers, - false - ); - return arkts.classPropertySetOptional(newProperty, true); -} - -export function createStageManagementType( - stageManagementIdent: string, - property: arkts.ClassProperty -): arkts.ETSTypeReference { - return arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier(stageManagementIdent), - arkts.factory.createTSTypeParameterInstantiation([ - property.typeAnnotation ? property.typeAnnotation.clone() : arkts.factory.createETSUndefinedType(), - ]) - ) - ); -} - +// GETTER export function getGettersFromClassDecl(definition: arkts.ClassDefinition): arkts.MethodDefinition[] { return definition.body.filter( (member) => arkts.isMethodDefinition(member) && - arkts.hasModifierFlag(member, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_GETTER) + arkts.hasModifierFlag(member, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_GETTER) && + !hasDecorator(member, DecoratorNames.COMPUTED) ) as arkts.MethodDefinition[]; } -export type MemoAstNode = - | arkts.ScriptFunction - | arkts.ETSParameterExpression - | arkts.ClassProperty - | arkts.TSTypeAliasDeclaration - | arkts.ETSFunctionType - | arkts.ArrowFunctionExpression - | arkts.ETSUnionType; +// ANNOTATION +export function hasPropertyInAnnotation(annotation: arkts.AnnotationUsage, propertyName: string): boolean { + return !!annotation.properties.find( + (annoProp: arkts.AstNode) => + arkts.isClassProperty(annoProp) && + annoProp.key && + arkts.isIdentifier(annoProp.key) && + annoProp.key.name === propertyName + ); +} + +// CUSTOM COMPONENT +export type CustomComponentInfo = { + name: string; + isDecl: boolean; + annotations: CustomComponentAnontations; +}; + +export type CustomComponentAnontations = { + component?: arkts.AnnotationUsage; + componentV2?: arkts.AnnotationUsage; + entry?: arkts.AnnotationUsage; + reusable?: arkts.AnnotationUsage; + reusableV2?: arkts.AnnotationUsage; + customLayout?: arkts.AnnotationUsage; + customdialog?: arkts.AnnotationUsage; +}; + +type StructAnnoationInfo = { + isComponent: boolean; + isComponentV2: boolean; + isEntry: boolean; + isReusable: boolean; + isReusableV2: boolean; + isCustomLayout: boolean; + isCustomDialog: boolean; +}; + +export type ComponentType = { + hasComponent: boolean; + hasComponentV2: boolean; + hasCustomDialog: boolean; + hasCustomLayout: boolean; +}; + +export type ClassInfo = { + className: string; + isFromStruct: boolean; +}; -export function isMemoAnnotation(node: arkts.AnnotationUsage, memoName: string): boolean { - if (!(node.expr !== undefined && arkts.isIdentifier(node.expr) && node.expr.name === memoName)) { +export function isCustomComponentAnnotation( + anno: arkts.AnnotationUsage, + decoratorName: StructDecoratorNames, + ignoreDecl?: boolean +): boolean { + if (!(!!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === decoratorName)) { return false; } + if (!ignoreDecl) { + const decl = arkts.getDecl(anno.expr); + if (!decl) { + return false; + } + const moduleName: string = arkts.getProgramFromAstNode(decl).moduleName; + if (!moduleName || !matchPrefix(ARKUI_IMPORT_PREFIX_NAMES, moduleName)) { + return false; + } + DeclarationCollector.getInstance().collect(decl); + } return true; } -export function addMemoAnnotation(node: T, memoName: string = 'memo'): T { - if (arkts.isETSUnionType(node)) { - const functionType = node.types.find((type) => arkts.isETSFunctionType(type)); - if (!functionType) { - return node; +export function collectCustomComponentScopeInfo( + node: arkts.ClassDeclaration | arkts.StructDeclaration +): CustomComponentInfo | undefined { + const definition: arkts.ClassDefinition | undefined = node.definition; + if (!definition || !definition?.ident?.name) { + return undefined; + } + const isStruct = arkts.isStructDeclaration(node); + const isDecl: boolean = arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE); + const isCustomComponentClassDecl = !isStruct && isDecl; + const shouldIgnoreDecl = isStruct || isDecl; + if ( + isCustomComponentClassDecl && + definition.ident.name !== CustomComponentNames.COMPONENT_CLASS_NAME && + definition.ident.name !== CustomComponentNames.COMPONENT_V2_CLASS_NAME && + definition.ident.name !== CustomComponentNames.BASE_CUSTOM_DIALOG_NAME + ) { + return undefined; + } + let annotations: CustomComponentAnontations = {}; + if (!isCustomComponentClassDecl) { + let isCustomComponent: boolean = false; + for (const anno of definition.annotations) { + const { isComponent, isComponentV2, isEntry, isReusable, isReusableV2, isCustomLayout, isCustomDialog } = + getAnnotationInfoForStruct(anno, shouldIgnoreDecl); + isCustomComponent ||= isComponent || isComponentV2 || isCustomDialog; + annotations = { + ...annotations, + ...(isComponent && !annotations?.component && { component: anno }), + ...(isComponentV2 && !annotations?.componentV2 && { componentV2: anno }), + ...(isEntry && !annotations?.entry && { entry: anno }), + ...(isReusable && !annotations?.reusable && { reusable: anno }), + ...(isReusableV2 && !annotations?.reusableV2 && { reusableV2: anno }), + ...(isCustomLayout && !annotations?.customLayout && { customLayout: anno }), + ...(isCustomDialog && !annotations?.customdialog && { customdialog: anno }), + }; + } + if (!isCustomComponent) { + return undefined; } - addMemoAnnotation(functionType, memoName); - return node; } - const newAnnotations: arkts.AnnotationUsage[] = [ - ...node.annotations.filter((it) => !isMemoAnnotation(it, memoName)), - annotation(memoName), - ]; - if (arkts.isEtsParameterExpression(node)) { - node.annotations = newAnnotations; - return node; + return { + name: definition.ident.name, + isDecl, + annotations: annotations as CustomComponentAnontations, + }; +} + +export function getAnnotationInfoForStruct( + anno: arkts.AnnotationUsage, + shouldIgnoreDecl: boolean +): StructAnnoationInfo { + const isComponent = isCustomComponentAnnotation(anno, StructDecoratorNames.COMPONENT, shouldIgnoreDecl); + const isComponentV2 = isCustomComponentAnnotation(anno, StructDecoratorNames.COMPONENT_V2, shouldIgnoreDecl); + const isEntry = isCustomComponentAnnotation(anno, StructDecoratorNames.ENTRY, shouldIgnoreDecl); + const isReusable = isCustomComponentAnnotation(anno, StructDecoratorNames.RESUABLE, shouldIgnoreDecl); + const isReusableV2 = isCustomComponentAnnotation(anno, StructDecoratorNames.RESUABLE_V2, shouldIgnoreDecl); + const isCustomLayout = isCustomComponentAnnotation(anno, StructDecoratorNames.CUSTOM_LAYOUT, shouldIgnoreDecl); + const isCustomDialog = isCustomComponentAnnotation(anno, StructDecoratorNames.CUSTOMDIALOG, shouldIgnoreDecl); + return { isComponent, isComponentV2, isEntry, isReusable, isReusableV2, isCustomLayout, isCustomDialog }; +} + +export function isComponentStruct(node: arkts.StructDeclaration, scopeInfo: CustomComponentInfo): boolean { + return scopeInfo.name === node.definition.ident?.name; +} + +/** + * Determine whether it is a custom component. + * + * @param node class declaration node + */ +export function isCustomComponentClass(node: arkts.ClassDeclaration, scopeInfo: CustomComponentInfo): boolean { + if (!node.definition?.ident?.name) { + return false; + } + const name: string = node.definition.ident.name; + if (scopeInfo.isDecl) { + return ( + name === CustomComponentNames.COMPONENT_CLASS_NAME || + name === CustomComponentNames.COMPONENT_V2_CLASS_NAME || + name === CustomComponentNames.BASE_CUSTOM_DIALOG_NAME + ); } - return node.setAnnotations(newAnnotations) as T; + return name === scopeInfo.name; } -export function hasPropertyInAnnotation(annotation: arkts.AnnotationUsage, propertyName: string): boolean { - return !!annotation.properties.find( - (annoProp: arkts.AstNode) => - arkts.isClassProperty(annoProp) && - annoProp.key && - arkts.isIdentifier(annoProp.key) && - annoProp.key.name === propertyName +export function isCustomComponentInterface(node: arkts.TSInterfaceDeclaration): boolean { + const checkPrefix = !!node.id?.name.startsWith(CustomComponentNames.COMPONENT_INTERFACE_PREFIX); + const checkComponent = node.annotations.some( + (anno) => + isCustomComponentAnnotation(anno, StructDecoratorNames.COMPONENT) || + isCustomComponentAnnotation(anno, StructDecoratorNames.COMPONENT_V2) || + isCustomComponentAnnotation(anno, StructDecoratorNames.CUSTOMDIALOG) ); + return checkPrefix && checkComponent; +} + +export function getCustomComponentOptionsName(className: string): string { + return `${CustomComponentNames.COMPONENT_INTERFACE_PREFIX}${className}`; } /** - * Determine whether the type node includes null or undefined type. + * Determine whether it is method with specified name. * - * @param type type node + * @param method method definition node + * @param name specified method name */ -export function hasNullOrUndefinedType(type: arkts.TypeNode): boolean { - let res: boolean = false; - if (arkts.isETSUnionType(type)) { - type.types.forEach((item: arkts.TypeNode) => { - res = res || hasNullOrUndefinedType(item); - }); +export function isKnownMethodDefinition(method: arkts.MethodDefinition, name: string): boolean { + if (!method || !arkts.isMethodDefinition(method)) { + return false; } - if (arkts.isETSUndefinedType(type) || arkts.isETSNullType(type)) { - res = true; + + // For now, we only considered matched method name. + const isNameMatched: boolean = method.name?.name === name; + return isNameMatched; +} + +export function isSpecificNewClass(node: arkts.ETSNewClassInstanceExpression, className: string): boolean { + if ( + node.getTypeRef && + arkts.isETSTypeReference(node.getTypeRef) && + node.getTypeRef.part && + arkts.isETSTypeReferencePart(node.getTypeRef.part) && + node.getTypeRef.part.name && + arkts.isIdentifier(node.getTypeRef.part.name) && + node.getTypeRef.part.name.name === className + ) { + return true; } - return res; -} \ No newline at end of file + return false; +} + +export function isCustomDialogControllerOptions( + newNode: arkts.TSInterfaceDeclaration, + externalSourceName: string | undefined +): boolean { + return ( + externalSourceName === CUSTOM_DIALOG_CONTROLLER_SOURCE_NAME && + !!newNode.id && + newNode.id.name === CustomDialogNames.CUSTOM_DIALOG_CONTROLLER_OPTIONS + ); +} + +export function getComponentExtendsName(annotations: CustomComponentAnontations, componentType: ComponentType): string { + if (!!annotations.customLayout) { + componentType.hasCustomLayout ||= true; + } + if (!!annotations.customdialog) { + componentType.hasCustomDialog ||= true; + return CustomComponentNames.BASE_CUSTOM_DIALOG_NAME; + } + if (!!annotations.componentV2) { + componentType.hasComponentV2 ||= true; + return CustomComponentNames.COMPONENT_V2_CLASS_NAME; + } + componentType.hasComponent ||= true; + return CustomComponentNames.COMPONENT_CLASS_NAME; +} + +export function computedField(name: string): string { + return `__computed_${name}`; +} + +export function monitorField(name: string): string { + return `__monitor_${name}`; +} + +export function getClassPropertyType(property: arkts.ClassProperty): arkts.TypeNode | undefined { + const type = property.typeAnnotation; + if (!!type) { + return type.clone(); + } + let value: arkts.Expression | undefined = property.value; + let inferredType: arkts.AstNode | undefined = value ? arkts.createTypeNodeFromTsType(value) : undefined; + if (inferredType && arkts.isTypeNode(inferredType)) { + return inferredType; + } + return undefined; +} diff --git a/arkui-plugins/ui-syntax-plugins/index.ts b/arkui-plugins/ui-syntax-plugins/index.ts index 53232081e..c32e154c1 100644 --- a/arkui-plugins/ui-syntax-plugins/index.ts +++ b/arkui-plugins/ui-syntax-plugins/index.ts @@ -14,31 +14,106 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PluginContext, Plugins } from '../common/plugin-context'; -import { ParsedUISyntaxLinterTransformer } from './transformers/parsed-ui-syntax-linter-transformer'; -import { createUISyntaxRuleProcessor } from './processor'; +import { PluginContext, PluginHandler, Plugins } from '../common/plugin-context'; +import { + CheckedUISyntaxLinterTransformer, + ParsedUISyntaxLinterTransformer, +} from './transformers/ui-syntax-linter-transformer'; +import { createUISyntaxRuleProcessor, UISyntaxRuleProcessor } from './processor'; +import { UISyntaxLinterVisitor } from './transformers/ui-syntax-linter-visitor'; import rules from './rules'; +import { matchPrefix } from '../common/arkts-utils'; +import { EXCLUDE_EXTERNAL_SOURCE_PREFIXES, tracePerformance } from './utils'; export function uiSyntaxLinterTransform(): Plugins { - const processor = createUISyntaxRuleProcessor(rules); - return { - name: 'ui-syntax-plugin', - parsed(this: PluginContext): arkts.EtsScript | undefined { - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); - if (!contextPtr) { + const processor = createUISyntaxRuleProcessor(rules); + const parsedTransformer = new ParsedUISyntaxLinterTransformer(processor); + const checkedTransformer = new CheckedUISyntaxLinterTransformer(processor); + return { + name: 'ui-syntax-plugin', + parsed: createTransformer('parsed', processor, parsedTransformer), + checked: createTransformer('checked', processor, checkedTransformer), + }; +} + +function createTransformer( + phase: string, + processor: UISyntaxRuleProcessor, + transformer: UISyntaxLinterVisitor +): PluginHandler { + const visitedPrograms: Set = new Set(); + const visitedExternalSources: Set = new Set(); + return tracePerformance(`UISyntaxPlugin::${phase}`, function (this: PluginContext): arkts.EtsScript | undefined { + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; + if (!contextPtr) { + return undefined; + } + const projectConfig = this.getProjectConfig(); + if (!projectConfig) { + return undefined; + } + processor.setProjectConfig(projectConfig); + if (projectConfig.frameworkMode) { + return undefined; + } + const program = arkts.getOrUpdateGlobalContext(contextPtr).program; + if (visitedPrograms.has(program.peer) || isHeaderFile(program.absName)) { + return undefined; + } + const isCoding = this.isCoding?.() ?? false; + if (isCoding) { + const codingFilePath = this.getCodingFilePath(); + if (program.absName === codingFilePath) { + return transformProgram.call(this, transformer, program); + } + } else { + transformExternalSources.call(this, program, visitedExternalSources, visitedPrograms, transformer); + if (program.absName) { + return transformProgram.call(this, transformer, program); + } + } + visitedPrograms.add(program.peer); return undefined; - } - let program = arkts.getOrUpdateGlobalContext(contextPtr).program; - const node = program.astNode; - if (node) { - const script = new ParsedUISyntaxLinterTransformer(processor).visitor( - node, - ) as arkts.EtsScript; - arkts.setAllParents(script); - this.setArkTSAst(script); - return script; - } - return undefined; + }); +} + +function transformExternalSources( + this: PluginContext, + program: arkts.Program, + visitedExternalSources: Set, + visitedPrograms: Set, + transformer: UISyntaxLinterVisitor +): void { + const externalSources = program.externalSources; + for (const externalSource of externalSources) { + if (matchPrefix(EXCLUDE_EXTERNAL_SOURCE_PREFIXES, externalSource.getName())) { + continue; + } + if (visitedExternalSources.has(externalSource)) { + continue; + } + const programs = externalSource.programs; + for (const program of programs) { + if (visitedPrograms.has(program.peer) || isHeaderFile(program.absName)) { + continue; + } + const script = transformer.transform(program.astNode) as arkts.EtsScript; + this.setArkTSAst(script); + } + visitedExternalSources.add(externalSource.peer); } - }; +} + +function transformProgram( + this: PluginContext, + transformer: UISyntaxLinterVisitor, + program: arkts.Program +): arkts.EtsScript { + const script = transformer.transform(program.astNode) as arkts.EtsScript; + this.setArkTSAst(script); + return script; +} + +function isHeaderFile(fileName: string): boolean { + return fileName.endsWith('.d.ets'); } diff --git a/arkui-plugins/ui-syntax-plugins/processor/index.ts b/arkui-plugins/ui-syntax-plugins/processor/index.ts index 0a447759e..6aa704560 100644 --- a/arkui-plugins/ui-syntax-plugins/processor/index.ts +++ b/arkui-plugins/ui-syntax-plugins/processor/index.ts @@ -14,51 +14,177 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from '../rules/ui-syntax-rule'; -import { getContainerComponents } from '../utils'; +import * as path from 'node:path'; +import { + AbstractUISyntaxRule, + ReportOptions, + UISyntaxRule, + UISyntaxRuleConfig, + UISyntaxRuleContext, + UISyntaxRuleHandler, +} from '../rules/ui-syntax-rule'; +import { getUIComponents, readJSON, UISyntaxRuleComponents } from '../utils'; +import { ProjectConfig } from 'common/plugin-context'; export type UISyntaxRuleProcessor = { - parsed(node: arkts.AstNode): void; + setProjectConfig(projectConfig: ProjectConfig): void; + beforeTransform(): void; + afterTransform(): void; + parsed(node: arkts.AstNode): void; + checked(node: arkts.AstNode): void; }; -export function createUISyntaxRuleProcessor( - rules: UISyntaxRule[], -): UISyntaxRuleProcessor { - const containerComponents = getContainerComponents('../../components/'); - const context: UISyntaxRuleContext = { - report(options) { - const position = arkts.getStartPosition(options.node); - let message: string; - if (!options.data) { - message = options.message; - } else { - message = Object.entries(options.data).reduce( - (message, [placehoderName, placehoderValue]) => { - return message.replace(`{{${placehoderName}}}`, placehoderValue); - }, - options.message, +type ModuleConfig = { + module: { + pages: string; + }; +}; + +type MainPages = { + src: string[]; +}; + +const BASE_RESOURCE_PATH = 'src/main/resources/base'; +const ETS_PATH = 'src/main/ets'; + +class ConcreteUISyntaxRuleContext implements UISyntaxRuleContext { + public componentsInfo: UISyntaxRuleComponents | undefined; + public projectConfig?: ProjectConfig; + + constructor() { + this.componentsInfo = getUIComponents('../../components/'); + } + + public report(options: ReportOptions): void { + let message: string; + if (!options.data) { + message = options.message; + } else { + message = this.format(options.message, options.data); + } + + const diagnosticKind: arkts.DiagnosticKind = arkts.DiagnosticKind.create( + message, + options.level === 'error' + ? arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_ERROR + : arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_WARNING ); - } - // todo - if (options.fix) { - const suggestion = options.fix(options.node); - console.log(`error: ${message}`); - console.log(`range: (${suggestion.range[0].index()}, ${suggestion.range[0].line()}) - (${suggestion.range[1].index()}, ${suggestion.range[1].line()})`, - `code: ${suggestion.code}`); - } else { - console.log(`syntax-error: ${message} (${position.index()},${position.line()})`); - } - }, - containerComponents: containerComponents, - }; - - const instances = rules.map((rule) => rule.setup(context)); - - return { - parsed(node): void { - for (const instance of instances) { - instance.parsed?.(node); - } - }, - }; + if (options.fix) { + const diagnosticInfo: arkts.DiagnosticInfo = arkts.DiagnosticInfo.create(diagnosticKind, + arkts.getStartPosition(options.node)); + const fixSuggestion = options.fix(options.node); + const suggestionKind: arkts.DiagnosticKind = arkts.DiagnosticKind.create( + message, + arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_SUGGESTION + ); + const [startPosition, endPosition] = fixSuggestion.range; + const sourceRange: arkts.SourceRange = arkts.SourceRange.create(startPosition, endPosition); + const suggestionInfo: arkts.SuggestionInfo = arkts.SuggestionInfo.create( + suggestionKind, + fixSuggestion.code, + fixSuggestion.title ? fixSuggestion.title : '', + sourceRange + ); + arkts.Diagnostic.logDiagnosticWithSuggestion(diagnosticInfo, suggestionInfo); + } else { + arkts.Diagnostic.logDiagnostic(diagnosticKind, arkts.getStartPosition(options.node)); + } + } + + getMainPages(): string[] { + if (!this.projectConfig) { + return []; + } + const { moduleRootPath, aceModuleJsonPath } = this.projectConfig; + if (!aceModuleJsonPath) { + return []; + } + const moduleConfig = readJSON(aceModuleJsonPath); + if (!moduleConfig) { + return []; + } + if (!moduleConfig.module || !moduleConfig.module.pages) { + return []; + } + const pagesPath = moduleConfig.module.pages; + const matcher = /\$(?[_A-Za-z]+):(?[_A-Za-z]+)/.exec(pagesPath); + if (matcher && matcher.groups) { + const { directory, filename } = matcher.groups; + const mainPagesPath = path.resolve(moduleRootPath, BASE_RESOURCE_PATH, directory, `${filename}.json`); + const mainPages = readJSON(mainPagesPath); + if (!mainPages) { + return []; + } + if (!mainPages.src || !Array.isArray(mainPages.src)) { + return []; + } + return mainPages.src.map((page) => path.resolve(moduleRootPath, ETS_PATH, `${page}.ets`)); + } else { + return []; + } + } + + private format(content: string, placeholders: object): string { + return Object.entries(placeholders).reduce((content, [placehoderName, placehoderValue]) => { + // Fixed a bug where $$ was converted to $ + placehoderValue = placehoderValue.split('$$').join('$$$$'); + return content.replace(`{{${placehoderName}}}`, placehoderValue); + }, content); + } +} + +class ConcreteUISyntaxRuleProcessor implements UISyntaxRuleProcessor { + protected context: UISyntaxRuleContext; + protected handlers: UISyntaxRuleHandler[]; + + constructor(rules: Array) { + this.context = new ConcreteUISyntaxRuleContext(); + this.handlers = rules.reduce((handlers, rule) => { + if (Array.isArray(rule)) { + const [RuleConstructor, level] = rule; + if (level !== 'none') { + handlers.push(new RuleConstructor(this.context, level)); + } + } else { + handlers.push(rule.setup(this.context)); + } + return handlers; + }, []); + } + + beforeTransform(): void { + for (const handler of this.handlers) { + if (handler instanceof AbstractUISyntaxRule) { + handler.beforeTransform(); + } + } + } + + afterTransform(): void { + for (const handler of this.handlers) { + if (handler instanceof AbstractUISyntaxRule) { + handler.afterTransform(); + } + } + } + + parsed(node: arkts.AstNode): void { + for (const handler of this.handlers) { + handler.parsed?.(node); + } + } + + checked(node: arkts.AstNode): void { + for (const handler of this.handlers) { + handler.checked?.(node); + } + } + + setProjectConfig(projectConfig: ProjectConfig): void { + this.context.projectConfig = projectConfig; + } +} + +export function createUISyntaxRuleProcessor(rules: Array): UISyntaxRuleProcessor { + return new ConcreteUISyntaxRuleProcessor(rules); } diff --git a/arkui-plugins/ui-syntax-plugins/rules/attribute-no-invoke.ts b/arkui-plugins/ui-syntax-plugins/rules/attribute-no-invoke.ts new file mode 100644 index 000000000..47f7b4ae4 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/attribute-no-invoke.ts @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { getCallee, isBuildInComponent } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + + +class AttributeNoInvokeRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + cannotInitializePrivateVariables: `'{{componentNode}}' does not meet UI component syntax.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (arkts.isExpressionStatement(node) && !arkts.isIdentifier(node.expression)) { + this.attributeNoInvoke(node); + } + } + + private attributeNoInvoke(node: arkts.AstNode): void { + const childNode = node.getChildren(); + if (!Array.isArray(childNode) || childNode.length < 1) { + return; + } + // Determine if the last chained property is an identifier + if (!arkts.isMemberExpression(childNode[0]) || !arkts.isIdentifier(childNode[0].property)) { + return; + } + if (!arkts.isCallExpression(childNode[0].object)) { + return; + } + const callee = getCallee(childNode[0].object); + + // Determine whether it is a built-in component + if (callee && isBuildInComponent(this.context, callee.name)) { + this.report({ + node, + message: this.messages.cannotInitializePrivateVariables, + data: { + componentNode: node.dumpSrc(), + }, + }); + } + } +}; + +export default AttributeNoInvokeRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts b/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts index c3678f1fe..10cb57d43 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts @@ -14,87 +14,125 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getIdentifierName, getAnnotationUsage, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getIdentifierName, getAnnotationUsage, PresetDecorators, BUILD_NAME } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const BUILD_NAME: string = 'build'; -const BUILD_ROOT_NUM: number = 1; const STATEMENT_LENGTH: number = 1; +const BUILD_COUNT_LIMIT: number = 1; -function isBuildOneRoot(statements: readonly arkts.Statement[], buildNode: arkts.Identifier, - context: UISyntaxRuleContext): void { - if (statements.length > STATEMENT_LENGTH && buildNode) { - context.report({ - node: buildNode, - message: rule.messages.invalidBuildRootCount, - }); - } -} - -function checkBuildRootNode(node: arkts.AstNode, context: UISyntaxRuleContext): void { - const loadedContainerComponents = context.containerComponents; - if (!arkts.isStructDeclaration(node)) { - return; - } - const entryDecoratorUsage = getAnnotationUsage(node, PresetDecorators.ENTRY); - node.definition.body.forEach(member => { - // Determine the number of root node - if (!arkts.isMethodDefinition(member) || getIdentifierName(member.name) !== BUILD_NAME) { - return; +class BuildRootNodeRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidEntryBuildRoot: `In an '@Entry' decorated component, the 'build' function can have only one root node, which must be a container component.`, + invalidBuildRoot: `The 'build' function can have only one root node.`, + }; } - const blockStatement = member.scriptFunction.body; - if (!blockStatement || !arkts.isBlockStatement(blockStatement)) { - return; - } - const buildNode = member.scriptFunction.id; - const statements = blockStatement.statements; - // rule1: The 'build' method cannot have more than one root node. - if (buildNode) { - isBuildOneRoot(statements, buildNode, context); + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + const entryDecoratorUsage = getAnnotationUsage(node, PresetDecorators.ENTRY); + node.definition.body.forEach((member) => { + if (!arkts.isMethodDefinition(member) || getIdentifierName(member.name) !== BUILD_NAME) { + return; + } + const blockStatement = member.scriptFunction.body; + const buildNode = member.scriptFunction.id; + if (!blockStatement || !arkts.isBlockStatement(blockStatement) || !buildNode) { + return; + } + const statements = blockStatement.statements; + let buildCount = 0; + // rule1: The 'build' method cannot have more than one root node. + if (statements.length > STATEMENT_LENGTH) { + if (!this.isBuildOneRoot(statements, buildCount)) { + this.report({ + node: buildNode, + message: entryDecoratorUsage ? this.messages.invalidEntryBuildRoot : this.messages.invalidBuildRoot, + }); + } + } + // rule2: its 'build' function can have only one root node, which must be a container component. + if (!statements.length || !entryDecoratorUsage) { + return; + } + this.validateContainerInBuild(statements, buildNode); + }); } - if (statements.length !== BUILD_ROOT_NUM) { - return; + + private isBuildOneRoot( + statements: readonly arkts.Statement[], + buildCount: number + ): boolean { + statements.forEach(statement => { + if (!arkts.isExpressionStatement(statement)) { + return; + } + if (!statement.expression) { + return; + } + const componentName = this.getComponentName(statement.expression); + if (componentName && componentName !== 'hilog') { + buildCount++; + } + }); + return buildCount <= BUILD_COUNT_LIMIT; } - // Determine whether it is a container component - const expressionStatement = statements[0]; - if (!arkts.isExpressionStatement(expressionStatement)) { - return; + + private validateContainerInBuild(statements: readonly arkts.Statement[], buildNode: arkts.Identifier): void { + const expressionStatement = statements[0]; + if (!arkts.isExpressionStatement(expressionStatement)) { + return; + } + const callExpression = expressionStatement.expression; + if (!arkts.isCallExpression(callExpression)) { + return; + } + let componentName = this.getComponentName(callExpression); + if (!componentName) { + return; + } + let isContainer = this.isContainerComponent(componentName); + if (!isContainer) { + this.report({ + node: buildNode, + message: this.messages.invalidEntryBuildRoot, + }); + } } - const callExpression = expressionStatement.expression; - if (!arkts.isCallExpression(callExpression)) { - return; + + private isContainerComponent(componentName: string): boolean { + const loadedContainerComponents = this.context.componentsInfo?.containerComponents; + if (!componentName || !loadedContainerComponents) { + return false; + } + return loadedContainerComponents.includes(componentName); } - const componentName = callExpression.expression.dumpSrc(); - let isContainer: boolean = false; - loadedContainerComponents?.forEach(container => { - if (componentName.includes(container)) { - isContainer = true; - } - }); - // rule2: If the component is decorated by '@Entry', - // its 'build' function can have only one root node, which must be a container component. - if (entryDecoratorUsage && !isContainer && buildNode) { - context.report({ - node: buildNode, - message: rule.messages.invalidBuildRoot, - }); + + private getComponentName(node: arkts.AstNode): string | undefined { + let children = node.getChildren(); + let componentName: string | undefined; + + while (true) { + if (!children || children.length === 0) { + return undefined; + } + + const firstChild = children[0]; + + if (arkts.isIdentifier(firstChild)) { + componentName = getIdentifierName(firstChild); + return componentName; + } + + if (!arkts.isMemberExpression(firstChild) && !arkts.isCallExpression(firstChild)) { + return undefined; + } + + children = firstChild.getChildren(); + } } - }); } -const rule: UISyntaxRule = { - name: 'build-root-node', - messages: { - invalidBuildRootCount: `The 'build' method cannot have more than one root node.`, - invalidBuildRoot: `If the component is decorated by '@Entry', its 'build' function can have only one root node, which must be a container component.` - }, - setup(context) { - return { - parsed: (node): void => { - checkBuildRootNode(node, context); - }, - }; - }, -}; - -export default rule; +export default BuildRootNodeRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/builderparam-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/builderparam-decorator-check.ts new file mode 100644 index 000000000..2b97c5808 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/builderparam-decorator-check.ts @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { getIdentifierName, PresetDecorators, BUILD_NAME, findDecorator } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class BuilderParamDecoratorCheckRule extends AbstractUISyntaxRule { + private structNameWithMultiplyBuilderParam: string[] = []; + private structNameWithoutBuilderParam: string[] = []; + + public setup(): Record { + return { + onlyOneBuilderParamProperty: `In the trailing lambda case, '{{structName}}' must have one and only one property decorated with @BuilderParam, and its @BuilderParam expects no parameter.`, + }; + } + + public beforeTransform(): void { + this.structNameWithMultiplyBuilderParam = []; + } + + public parsed(node: arkts.AstNode): void { + this.getStructNameWithMultiplyBuilderParam(node); + this.checkComponentInitialize(node); + } + + private getStructNameWithMultiplyBuilderParam( + node: arkts.AstNode, + ): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident) { + return; + } + let count: number = 0; + let structName: string = member.definition.ident.name ?? ''; + member.definition.body.forEach((item) => { + if (!arkts.isClassProperty(item) || !item.key) { + return; + } + const builderParam = findDecorator(item, PresetDecorators.BUILDER_PARAM); + + if (builderParam) { + count++; + } + }); + if (count > 1) { + this.structNameWithMultiplyBuilderParam.push(structName); + } + if (count === 0) { + this.structNameWithoutBuilderParam.push(structName); + } + }); + } + + private isInBuild(node: arkts.AstNode): boolean { + if (!node.parent) { + return false; + } + let structNode = node.parent; + while (!arkts.isMethodDefinition(structNode) || getIdentifierName(structNode.name) !== BUILD_NAME) { + if (!structNode.parent) { + return false; + } + structNode = structNode.parent; + } + return arkts.isMethodDefinition(structNode) && getIdentifierName(structNode.name) === BUILD_NAME; + } + + private hasBlockStatement(node: arkts.AstNode): boolean { + if (!node.parent) { + return false; + } + let parentNode = node.parent; + const siblings = parentNode.getChildren(); + if (!Array.isArray(siblings) || siblings.length < 2) { + return false; + } + if (arkts.isStringLiteral(siblings[1]) && arkts.isBlockStatement(siblings[2])) { + return true; + } + if (arkts.isBlockStatement(siblings[1])) { + return true; + } + return false; + } + + private checkComponentInitialize( + node: arkts.AstNode, + ): void { + if (!node.parent) { + return; + } + let parentNode: arkts.AstNode = node.parent; + if (!arkts.isCallExpression(parentNode)) { + return; + } + let structName: string = getIdentifierName(node); + if (!arkts.isIdentifier(node) || !(this.structNameWithMultiplyBuilderParam.includes(structName) || + this.structNameWithoutBuilderParam.includes(structName))) { + return; + } + if (!this.hasBlockStatement(node)) { + return; + } + let structNode = node.parent; + while (!arkts.isStructDeclaration(structNode)) { + if (!structNode.parent) { + return; + } + structNode = structNode.parent; + } + if (!this.isInBuild(node)) { + return; + } + this.report({ + node: node, + message: this.messages.onlyOneBuilderParamProperty, + data: { structName }, + }); + } +} + +export default BuilderParamDecoratorCheckRule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/check-construct-private-parameter.ts b/arkui-plugins/ui-syntax-plugins/rules/check-construct-private-parameter.ts index 7dfa570b0..21cab0ab1 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/check-construct-private-parameter.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/check-construct-private-parameter.ts @@ -15,79 +15,78 @@ import * as arkts from '@koalaui/libarkts'; import { getClassPropertyName, isPrivateClassProperty } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function addProperty(item: arkts.AstNode, structName: string, privateMap: Map): void { - if (!arkts.isClassProperty(item) || !isPrivateClassProperty(item)) { - return; - } - // Check if structName already exists in privateMap - if (privateMap.has(structName)) { - // If it exists, retrieve the current string[] and append the new content - privateMap.get(structName)?.push(getClassPropertyName(item)); - } else { - // If it doesn't exist, create a new string[] and add the content - privateMap.set(structName, [getClassPropertyName(item)]); - } -} +class CheckConstructPrivateParameterRule extends AbstractUISyntaxRule { + private privatePropertyMap: Map = new Map(); + + public setup(): Record { + return { + cannotInitializePrivateVariables: `Property '{{propertyName}}' is private and can not be initialized through the component constructor.`, + }; + } + + public beforeTransform(): void { + this.privatePropertyMap = new Map(); + } -function checkPrivateVariables( - node: arkts.AstNode, - context: UISyntaxRuleContext, - privateMap: Map -): void { - // Check if the current node is the root node - if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - node.getChildren().forEach((member) => { - if (!arkts.isStructDeclaration(member)) { - return; - } - const structName: string = member.definition.ident?.name ?? ''; - member.definition?.body?.forEach((item) => { - addProperty(item, structName, privateMap); - }); - }); - } - if (!arkts.isCallExpression(node)) { - return; - } - const componentName = node.expression.dumpSrc(); - // If the initialization is for a component with private properties - if (!privateMap.has(componentName)) { - return; - } - node.arguments?.forEach((member) => { - member.getChildren().forEach((property) => { - if (!arkts.isProperty(property)) { - return; - } - const propertyName: string = property.key?.dumpSrc() ?? ''; - if (privateMap.get(componentName)!.includes(propertyName)) { - context.report({ - node: property, - message: rule.messages.cannotInitializePrivateVariables, - data: { - propertyName: propertyName, - }, + public parsed(node: arkts.StructDeclaration): void { + // Check if the current node is the root node + if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident || !member.definition.ident.name) { + return; + } + const structName: string = member.definition.ident.name; + member.definition.body.forEach((item) => { + this.addProperty(item, structName); + }); + }); + } + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; + } + const componentName = node.expression.name; + // If the initialization is for a component with private properties + if (!this.privatePropertyMap.has(componentName)) { + return; + } + node.arguments.forEach((member) => { + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property) || !property.key || !arkts.isIdentifier(property.key)) { + return; + } + const propertyName: string = property.key.name; + if (this.privatePropertyMap.get(componentName)!.includes(propertyName)) { + this.report({ + node: property, + message: this.messages.cannotInitializePrivateVariables, + data: { + propertyName: propertyName, + }, + }); + } + }); }); - } - }); - }); -} + } -const rule: UISyntaxRule = { - name: 'check-construct-private-parameter', - messages: { - cannotInitializePrivateVariables: `Property '{{propertyName}}' is private and can not be initialized through the component constructor.`, - }, - setup(context) { - let privateMap: Map = new Map(); - return { - parsed: (node): void => { - checkPrivateVariables(node, context, privateMap); - }, - }; - }, -}; + private addProperty(item: arkts.AstNode, structName: string): void { + if (!arkts.isClassProperty(item) || !isPrivateClassProperty(item)) { + return; + } + const propertyName = getClassPropertyName(item); + if (!propertyName) { + return; + } + // Check if structName already exists in privateMap + if (this.privatePropertyMap.has(structName)) { + // If it exists, retrieve the current string[] and append the new content + this.privatePropertyMap.get(structName)!.push(propertyName); + } else { + // If it doesn't exist, create a new string[] and add the content + this.privatePropertyMap.set(structName, [propertyName]); + } + } +} -export default rule; \ No newline at end of file +export default CheckConstructPrivateParameterRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/check-decorated-property-type.ts b/arkui-plugins/ui-syntax-plugins/rules/check-decorated-property-type.ts index 8448fa435..90c6a82ed 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/check-decorated-property-type.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/check-decorated-property-type.ts @@ -14,64 +14,74 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; import { + forbiddenUseStateType, getAnnotationUsage, getClassPropertyAnnotationNames, getClassPropertyName, getClassPropertyType, PresetDecorators } from '../utils'; -function checkDecoratedPropertyType( - member: arkts.AstNode, - context: UISyntaxRuleContext, - relationship: Record -): void { - if (!arkts.isClassProperty(member)) { - return; +const forbiddenUseStateTypeForDecorators: string[] = [ + PresetDecorators.STATE, + PresetDecorators.PROP_REF, + PresetDecorators.LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.OBJECT_LINK, + PresetDecorators.BUILDER_PARAM, + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.STORAGE_LINK, + PresetDecorators.LOCAL_STORAGE_LINK, +]; + +class CheckDecoratedPropertyTypeRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidDecoratedPropertyType: `The '@{{decoratorName}}' property '{{propertyName}}' cannot be a '{{propertyType}}' object.`, + }; + } + + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + if (!node.definition) { + return; + } + node.definition.body.forEach(member => { + this.checkDecoratedPropertyType(member, forbiddenUseStateTypeForDecorators, forbiddenUseStateType); + }); } - const propertyName = getClassPropertyName(member); - const propertyType = getClassPropertyType(member); - const propertyAnnotationNames = getClassPropertyAnnotationNames(member); - Object.entries(relationship).forEach(([decoratorName, invalidPropertyTypes]) => { - if (propertyAnnotationNames.some(annotationName => annotationName === decoratorName) && - invalidPropertyTypes - .some(invalidPropertyType => invalidPropertyType === propertyType)) { - if (!arkts.isClassProperty || member.key === undefined) { - return; - } - const errorNode = member.key; - context.report({ + + private checkDecoratedPropertyType( + member: arkts.AstNode, + annoList: string[], + typeList: string[] + ): void { + if (!arkts.isClassProperty(member)) { + return; + } + const propertyName = getClassPropertyName(member); + const propertyType = getClassPropertyType(member); + if (!propertyName || !propertyType) { + return; + } + const propertyAnnotationNames: string[] = getClassPropertyAnnotationNames(member); + const decoratorName: string | undefined = + propertyAnnotationNames.find((annotation) => annoList.includes(annotation)); + const isType: boolean = typeList.includes(propertyType); + if (!member.key) { + return; + } + const errorNode = member.key; + if (decoratorName && isType) { + this.report({ node: errorNode, - message: rule.messages.invalidDecoratedPropertyType, + message: this.messages.invalidDecoratedPropertyType, data: { decoratorName, propertyName, propertyType }, }); } - }); + } } -const rule: UISyntaxRule = { - name: 'check-decorated-property-type', - messages: { - invalidDecoratedPropertyType: `The {{decoratorName}} property '{{propertyName}}' cannot be a '{{propertyType}}' object.`, - }, - setup(context) { - const relationship: Record = { - [PresetDecorators.STATE]: ['CustomDialogController'], - }; - return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; - } - const componentDecorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); - if (!componentDecorator) { - return; - } - node.definition.body.forEach(member => { - checkDecoratedPropertyType(member, context, relationship); - }); - }, - }; - }, -}; - -export default rule; \ No newline at end of file +export default CheckDecoratedPropertyTypeRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/check-property-modifiers.ts b/arkui-plugins/ui-syntax-plugins/rules/check-property-modifiers.ts new file mode 100644 index 000000000..808113783 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/check-property-modifiers.ts @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { + getClassPropertyAnnotationNames, + getClassPropertyName, + isPrivateClassProperty, + isProtectedClassProperty, + isPublicClassProperty, + PresetDecorators +} from '../utils'; + + +class CheckPropertyModifiersRule extends AbstractUISyntaxRule { + private static readonly noPublicDecorators: string[] = [ + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.STORAGE_LINK, + PresetDecorators.LOCAL_STORAGE_LINK, + ]; + + private static readonly noPrivateDecorators: string[] = [ + PresetDecorators.LINK, + PresetDecorators.OBJECT_LINK, + ]; + + public setup(): Record { + return { + invalidPublic: `The {{decoratorName}} decorated '{{propertyName}}' cannot be declared as public.`, + invalidPrivate: `The {{decoratorName}} decorated '{{propertyName}}' cannot be declared as private.`, + invalidProtected: `The member attributes of a struct can not be protected.` + }; + } + + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + node.definition.body.forEach(member => { + if (!arkts.isClassProperty(member)) { + return; + } + if (arkts.isDefaultAccessModifierClassProperty(member)) { + return; + } + const propertyName = getClassPropertyName(member); + if (!propertyName) { + return; + } + const propertyAnnotationNames = getClassPropertyAnnotationNames(member); + propertyAnnotationNames.forEach((propertyAnnotationName) => { + this.checkInvalidPublic(propertyAnnotationName, propertyName, member); + this.checkInvalidPrivate(propertyAnnotationName, propertyName, member); + }); + this.checkInvalidProtected(propertyName, member); + }); + } + + private checkInvalidPublic(propertyAnnotationName: string, propertyName: string, + member: arkts.ClassProperty + ): void { + if (!CheckPropertyModifiersRule.noPublicDecorators.includes(propertyAnnotationName) || + !isPublicClassProperty(member)) { + return; + } + const errorNode = member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + CheckPropertyModifiersRule.noPublicDecorators.includes(annotation.expr.name) + ); + if (!errorNode) { + return; + } + this.report({ + node: errorNode, + message: this.messages.invalidPublic, + data: { + decoratorName: propertyAnnotationName, + propertyName: propertyName, + } + }); + } + + private checkInvalidPrivate(propertyAnnotationName: string, propertyName: string, + member: arkts.ClassProperty + ): void { + if (!CheckPropertyModifiersRule.noPrivateDecorators.includes(propertyAnnotationName) || + !isPrivateClassProperty(member)) { + return; + } + const errorNode = member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + CheckPropertyModifiersRule.noPrivateDecorators.includes(annotation.expr.name) + ); + if (!errorNode) { + return; + } + this.report({ + node: errorNode, + message: this.messages.invalidPrivate, + data: { + decoratorName: propertyAnnotationName, + propertyName: propertyName, + }, + }); + } + + private checkInvalidProtected(propertyName: string, member: arkts.ClassProperty): void { + if (!isProtectedClassProperty(member)) { + return; + } + this.report({ + node: member, + message: this.messages.invalidProtected, + data: { + propertyName: propertyName, + } + }); + } +} + +export default CheckPropertyModifiersRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-init-check.ts b/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-init-check.ts new file mode 100644 index 000000000..7230b8f6d --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-init-check.ts @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { getAnnotationUsage, getIdentifierName, hasAnnotation, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class ComponentComponentV2InitCheckRule extends AbstractUISyntaxRule { + private componentV1WithLinkList: string[] = []; + + public setup(): Record { + return { + componentInitLinkCheck: `A V2 component cannot be used with any member property decorated by '@Link' in a V1 component.`, + }; + } + + public beforeTransform(): void { + this.componentV1WithLinkList = []; + } + + public parsed(node: arkts.StructDeclaration): void { + this.initComponentV1WithLinkList(node); + this.checkComponentInitLink(node); + } + + private initComponentV1WithLinkList(node: arkts.AstNode): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident || + !hasAnnotation(member?.definition.annotations, PresetDecorators.COMPONENT_V1)) { + return; + } + let structName: string = member.definition.ident?.name ?? ''; + member.definition?.body?.forEach((item) => { + if (!arkts.isClassProperty(item) || !item.key) { + return; + } + if (item.annotations.some(annotation => annotation.expr && + getIdentifierName(annotation.expr) === PresetDecorators.LINK)) { + this.componentV1WithLinkList.push(structName); + } + }); + }); + } + + private checkComponentInitLink(node: arkts.AstNode): void { + if (!arkts.isIdentifier(node) || !this.componentV1WithLinkList.includes(getIdentifierName(node))) { + return; + } + if (!node.parent) { + return; + } + let structNode = node.parent; + while (!arkts.isStructDeclaration(structNode)) { + if (!structNode.parent) { + return; + } + structNode = structNode.parent; + } + if (getAnnotationUsage(structNode, PresetDecorators.COMPONENT_V2) !== undefined) { + const parentNode = node.parent; + this.report({ + node: parentNode, + message: this.messages.componentInitLinkCheck, + fix: () => { + return { + title: 'Remove the component', + range: [parentNode.startPosition, parentNode.endPosition], + code: '', + }; + } + }); + } + } +} + +export default ComponentComponentV2InitCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-mix-use-check.ts b/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-mix-use-check.ts index ce7afdf8f..70714c3b3 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-mix-use-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-mix-use-check.ts @@ -14,164 +14,176 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -// Report an Observed version violation error -function reportObservedConflict( - node: arkts.ClassProperty, - context: UISyntaxRuleContext, - message: string -): void { - node.annotations.forEach((anno) => { - if (anno.expr?.dumpSrc()) { - context.report({ - node: anno, - message: message, - data: { - annotation: anno.expr?.dumpSrc(), +import { PresetDecorators, getIdentifierName } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const v1ComponentDecorators: string[] = [ + PresetDecorators.STATE, + PresetDecorators.PROP_REF, + PresetDecorators.LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.STORAGE_LINK, + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.LOCAL_STORAGE_LINK, +]; + +class ComponentComponentV2MixUseCheckRule extends AbstractUISyntaxRule { + private observedV2Names: Set = new Set(); + + public setup(): Record { + return { + observedv2_v1: `The type of the @{{annotation}} property cannot be a class decorated with '@ObservedV2'.` + }; + } + + public beforeTransform(): void { + this.observedV2Names = new Set(); + } + + public parsed(node: arkts.AstNode): void { + if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + this.findAllObserved(node); + this.findAllTSTypeAliasDeclaration(node); + } + + if (arkts.isStructDeclaration(node)) { + this.processComponentAnnotations(node); + } + } + + private findAllObserved(node: arkts.AstNode): void { + if (arkts.isClassDeclaration(node)) { + node.definition?.annotations.forEach((anno) => { + if (!anno.expr) { + return; + } + + const annotationName = getIdentifierName(anno.expr); + + if (annotationName === PresetDecorators.OBSERVED_V2) { + const componentV2Name = node.definition?.ident?.name; + componentV2Name ? this.observedV2Names.add(componentV2Name) : null; } }); } - }); -} -function processNode( - node: arkts.ClassProperty, - annotationName: string, - observedV1Name: Set, - observedV2Name: Set, - context: UISyntaxRuleContext -): void { - const queue: Array = [node]; - while (queue.length > 0) { - const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; - if (arkts.isIdentifier(currentNode)) { - if (observedV1Name.has(currentNode.dumpSrc()) && annotationName === PresetDecorators.COMPONENT_V2) { - reportObservedConflict(node, context, rule.messages.observedv1_v2); - break; - } - if (observedV2Name.has(currentNode.dumpSrc()) && annotationName === PresetDecorators.COMPONENT_V1) { - reportObservedConflict(node, context, rule.messages.observedv2_v1); - break; + for (const child of node.getChildren()) { + this.findAllObserved(child); + } + } + + private findAllTSTypeAliasDeclaration(node: arkts.AstNode): void { + if ( + arkts.nodeType(node) === + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION + ) { + for (const child of node.getChildren()) { + if (arkts.isIdentifier(child)) { + const typeName = getIdentifierName(child); + this.findAllObservedType(node, typeName); + } } } - const children = currentNode.getChildren(); - for (const child of children) { - queue.push(child); + + for (const child of node.getChildren()) { + this.findAllTSTypeAliasDeclaration(child); } } -} -function traverseTree( - node: arkts.AstNode, - annotationName: string, - observedV1Name: Set, - observedV2Name: Set, - context: UISyntaxRuleContext -): void { - if (arkts.isClassProperty(node)) { - processNode(node, annotationName, observedV1Name, observedV2Name, context); - } - const children = node.getChildren(); - for (const child of children) { - traverseTree(child, annotationName, observedV1Name, observedV2Name, context); + private findAllObservedType( + node: arkts.AstNode, + typeName: string + ): void { + if (arkts.isIdentifier(node)) { + const name = getIdentifierName(node); + + if (this.observedV2Names.has(name)) { + this.observedV2Names.add(typeName); + } + } + + for (const child of node.getChildren()) { + this.findAllObservedType(child, typeName); + } } -} -function findAllObserved(node: arkts.AstNode, observedV1Name: Set, observedV2Name: Set): void { - if (arkts.isClassDeclaration(node)) { - node.definition?.annotations.forEach((anno) => { - if (anno.expr?.dumpSrc() === PresetDecorators.OBSERVED_V1) { - const componentV1Name = node?.definition?.ident?.name; - componentV1Name ? observedV1Name.add(componentV1Name) : null; + private processComponentAnnotations( + node: arkts.StructDeclaration + ): void { + node.definition.annotations.forEach((anno) => { + if (!anno.expr) { + return; } - if (anno.expr?.dumpSrc() === PresetDecorators.OBSERVED_V2) { - const componentV2Name = node?.definition?.ident?.name; - componentV2Name ? observedV2Name.add(componentV2Name) : null; + const annotationName = getIdentifierName(anno.expr); + if ( + annotationName === PresetDecorators.COMPONENT_V2 || + annotationName === PresetDecorators.COMPONENT_V1 + ) { + this.traverseTree(node, annotationName); } }); } - const children = node.getChildren(); - for (const child of children) { - findAllObserved(child, observedV1Name, observedV2Name); + + private traverseTree( + node: arkts.AstNode, + annotationName: string + ): void { + if (arkts.isClassProperty(node)) { + this.processNode(node, annotationName); + } + + for (const child of node.getChildren()) { + this.traverseTree(child, annotationName); + } } -} -function findAllTSTypeAliasDeclaration( - node: arkts.AstNode, - observedV1Name: Set, - observedV2Name: Set -): void { - if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION) { - node.getChildren().forEach((child) => { - if (arkts.isIdentifier(child)) { - const typeName = child.dumpSrc(); - findAllObservedType(node, typeName, observedV1Name, observedV2Name); + private processNode( + node: arkts.ClassProperty, + annotationName: string + ): void { + const queue: Array = [node]; + while (queue.length > 0) { + const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; + if (arkts.isIdentifier(currentNode)) { + const name = getIdentifierName(currentNode); + + if ( + annotationName === PresetDecorators.COMPONENT_V1 && + this.observedV2Names.has(name) + ) { + this.checkObservedConflict(node, v1ComponentDecorators); + break; + } } - }); - } - const children = node.getChildren(); - for (const child of children) { - findAllTSTypeAliasDeclaration(child, observedV1Name, observedV2Name); + const children = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } } -} -function findAllObservedType( - node: arkts.AstNode, - typeName: string, - observedV1Name: Set, - observedV2Name: Set -): void { - if (arkts.isIdentifier(node) && observedV1Name.has(node.dumpSrc())) { - observedV1Name.add(typeName); - } - if (arkts.isIdentifier(node) && observedV2Name.has(node.dumpSrc())) { - observedV2Name.add(typeName); - } - const children = node.getChildren(); - for (const child of children) { - findAllObservedType(child, typeName, observedV1Name, observedV2Name); - } -} + private checkObservedConflict( + node: arkts.ClassProperty, + componentDecorators: string[] + ): void { + node.annotations.forEach((anno) => { + if (!anno.expr) { + return; + } -function processComponentAnnotations( - node: arkts.StructDeclaration, - observedV1Name: Set, - observedV2Name: Set, - context: UISyntaxRuleContext -): void { - node?.definition?.annotations.forEach((anno) => { - if (anno.expr?.dumpSrc() === PresetDecorators.COMPONENT_V2) { - traverseTree(node, PresetDecorators.COMPONENT_V2, observedV1Name, observedV2Name, context); - } - if (anno.expr?.dumpSrc() === PresetDecorators.COMPONENT_V1) { - traverseTree(node, PresetDecorators.COMPONENT_V1, observedV1Name, observedV2Name, context); - } - }); + const annotationName = getIdentifierName(anno.expr); + if (annotationName && componentDecorators.includes(annotationName)) { + this.report({ + node: anno, + message: this.messages.observedv2_v1, + data: { + annotation: annotationName, + }, + }); + } + }); + } } -const rule: UISyntaxRule = { - name: 'component-componentV2-mix-use-check', - messages: { - observedv1_v2: `The type of the @{{annotation}} Decorator property can not be a class decorated with @Observed.`, - observedv2_v1: `The type of the @{{annotation}} Decorator property can not be a class decorated with @ObservedV2.` - }, - setup(context) { - let observedV1Name: Set = new Set(); - let observedV2Name: Set = new Set(); - return { - parsed: (node): void => { - if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - findAllObserved(node, observedV1Name, observedV2Name); - findAllTSTypeAliasDeclaration(node, observedV1Name, observedV2Name); - } - if (arkts.isStructDeclaration(node)) { - processComponentAnnotations(node, observedV1Name, observedV2Name, context); - } - }, - }; - }, -}; - -export default rule; \ No newline at end of file +export default ComponentComponentV2MixUseCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/componentV2-mix-check.ts b/arkui-plugins/ui-syntax-plugins/rules/componentV2-mix-check.ts index 082d768d1..7db394582 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/componentV2-mix-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/componentV2-mix-check.ts @@ -15,43 +15,47 @@ import * as arkts from '@koalaui/libarkts'; import { getAnnotationUsage, PresetDecorators } from '../utils'; -import { UISyntaxRule } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const rule: UISyntaxRule = { - name: 'componentV2-mix-check', - messages: { - conflictWithComponentV2: `The struct '{{structName}}' can not be decorated with '@ComponentV2' and '@Component', '@Reusable', '@CustomDialog' at the same time.`, - }, - setup(context) { +class ComponentV2MixCheckRule extends AbstractUISyntaxRule { + public setup(): Record { return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; - } - const structName = node.definition.ident?.name ?? ''; - const structNameNode = node.definition.ident; - if (!structNameNode) { - return; - } - // Check if the struct has the '@ComponentV2' annotation - const hasComponentV2 = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); - if (!hasComponentV2) { - return; - } - // Check for the presence of conflicting decorators: '@Component', '@Reusable', '@CustomDialog' - const hasComponent = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); - const hasReusable = getAnnotationUsage(node, PresetDecorators.REUSABLE_V1); - const hasCustomDialog = getAnnotationUsage(node, PresetDecorators.CUSTOM_DIALOG); - if (hasComponent || hasReusable || hasCustomDialog) { - context.report({ - node: structNameNode, - message: rule.messages.conflictWithComponentV2, - data: { structName }, - }); - } - }, + conflictWithComponentV2: `The struct '{{structName}}' can not be decorated with '@ComponentV2' and '@Component', '@Reusable', '@CustomDialog' at the same time.`, }; - }, -}; + } -export default rule; + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + const definition = node.definition; + if (!definition) { + return; + } + const structNameNode = definition.ident; + if (!structNameNode) { + return; + } + const structName = structNameNode.name ?? ''; + // Check if the struct has the '@ComponentV2' annotation + const componentV2Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); + if (!componentV2Decorator) { + return; + } + + // Check for conflicting decorators + const componentDecorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); + const reusableDecorator = getAnnotationUsage(node, PresetDecorators.REUSABLE_V1); + const customDialogDecorator = getAnnotationUsage(node, PresetDecorators.CUSTOM_DIALOG); + + if (componentDecorator || reusableDecorator || customDialogDecorator) { + this.report({ + node: structNameNode, + message: this.messages.conflictWithComponentV2, + data: { structName }, + }); + } + } +} + +export default ComponentV2MixCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/componentV2-state-usage-validation.ts b/arkui-plugins/ui-syntax-plugins/rules/componentV2-state-usage-validation.ts index b60587aee..47d5088ec 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/componentV2-state-usage-validation.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/componentV2-state-usage-validation.ts @@ -14,155 +14,346 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getClassPropertyAnnotationNames, PresetDecorators, getAnnotationUsage } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -// Helper functions for rules -const hasisComponentV2 = (node: arkts.StructDeclaration): boolean => !!getAnnotationUsage(node, - PresetDecorators.COMPONENT_V2); - -const hasComponent = (node: arkts.StructDeclaration): boolean => !!getAnnotationUsage(node, - PresetDecorators.COMPONENT_V1); -function checkMultipleBuiltInDecorators(context: UISyntaxRuleContext, member: arkts.ClassProperty, - propertyDecorators: string[]): void { - const builtInDecorators = [PresetDecorators.LOCAL, PresetDecorators.PARAM, PresetDecorators.EVENT]; - const appliedBuiltInDecorators = propertyDecorators.filter(d => builtInDecorators.includes(d)); - if (appliedBuiltInDecorators.length > 1) { - member.annotations?.forEach(annotation => { - const annotationsName = annotation.expr?.dumpSrc(); - reportMultipleBuiltInDecoratorsError(context, annotation, annotationsName, builtInDecorators); - }); - } -}; +import { + getClassPropertyAnnotationNames, PresetDecorators, getAnnotationUsage, getClassPropertyName, getIdentifierName +} from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const builtInDecorators = [PresetDecorators.LOCAL, PresetDecorators.PARAM, PresetDecorators.EVENT]; -function reportMultipleBuiltInDecoratorsError(context: UISyntaxRuleContext, annotation: arkts.AstNode, - annotationsName: string | undefined, builtInDecorators: string[]): void { - if (annotationsName && builtInDecorators.includes(annotationsName)) { - context.report({ - node: annotation, - message: rule.messages.multipleBuiltInDecorators, - fix: (annotation) => { - const startPosition = arkts.getStartPosition(annotation); - const endPosition = arkts.getEndPosition(annotation); +class ComponentV2StateUsageValidationRule extends AbstractUISyntaxRule { + private componentV2PropertyMap: Map> = new Map(); + public setup(): Record { return { - range: [startPosition, endPosition], - code: '', + multipleBuiltInDecorators: `The member property or method cannot be decorated by multiple built-in annotations.`, + paramRequiresRequire: `When a variable decorated with '@Param' is not assigned a default value, it must also be decorated with '@Require'.`, + requireOnlyWithParam: `In a struct decorated with '@ComponentV2', '@Require' can only be used with '@Param' or '@BuilderParam'.`, + localNeedNoInit: `The '{{decoratorName}}' property '{{key}}' in the custom component '{{componentName}}' cannot be initialized here (forbidden to specify).`, + useStateDecoratorsWithProperty: `'@{{annotationName}}' can only decorate member property.`, }; - }, - }); - } -} - -function checkDecoratorOnlyInisComponentV2(context: UISyntaxRuleContext, member: arkts.ClassProperty, - node: arkts.StructDeclaration, hasisComponentV2: boolean, hasComponent: boolean): void { - const builtInDecorators = [PresetDecorators.LOCAL, PresetDecorators.PARAM, PresetDecorators.EVENT]; - member.annotations?.forEach(annotation => { - const annotationsName = annotation.expr?.dumpSrc(); - if (annotationsName && builtInDecorators.includes(annotationsName) && !hasisComponentV2 && !hasComponent) { - reportDecoratorOnlyInisComponentV2Error(context, annotation, annotationsName, node); } - }); -}; -function reportDecoratorOnlyInisComponentV2Error(context: UISyntaxRuleContext, annotation: arkts.AnnotationUsage, - annotationsName: string, node: arkts.StructDeclaration): void { - context.report({ - node: annotation, - message: rule.messages.decoratorOnlyInisComponentV2, - data: { annotationsName }, - fix: (annotation) => { - const startPosition = arkts.getStartPosition(node); - return { - range: [startPosition, startPosition], - code: `@${PresetDecorators.COMPONENT_V2}\n`, - }; - }, - }); -} - -function checkParamRequiresRequire(context: UISyntaxRuleContext, member: arkts.ClassProperty, - propertyDecorators: string[]): void { - if (propertyDecorators.includes(PresetDecorators.PARAM) && !member.value && - !propertyDecorators.includes(PresetDecorators.REQUIRE) && member.key) { - const memberKey = member.key; - context.report({ - node: memberKey, - message: rule.messages.paramRequiresRequire, - fix: (memberKey) => { - const startPosition = arkts.getStartPosition(memberKey); - return { - range: [startPosition, startPosition], - code: `@${PresetDecorators.REQUIRE} `, - }; - }, - }); - } -}; + public beforeTransform(): void { + this.componentV2PropertyMap = new Map(); + } -function checkRequireOnlyWithParam(context: UISyntaxRuleContext, member: arkts.ClassProperty, - propertyDecorators: string[]): void { - const requireDecorator = member.annotations?.find(annotation => - annotation.expr && annotation.expr.dumpSrc() === PresetDecorators.REQUIRE - ); - if (requireDecorator && !propertyDecorators.includes(PresetDecorators.PARAM)) { - context.report({ - node: requireDecorator, - message: rule.messages.requireOnlyWithParam, - fix: (requireDecorator) => { - const startPosition = arkts.getStartPosition(requireDecorator); - const endPosition = arkts.getEndPosition(requireDecorator); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); - } -}; + public parsed(node: arkts.AstNode): void { + this.initComponentV2PropertyMap(node, this.componentV2PropertyMap); + this.checkInitializeRule(node, this.componentV2PropertyMap); + if (arkts.isClassDeclaration(node) && node.definition) { + this.checkuseStateDecoratorsWithProperty(node.definition); + } + if (arkts.isFunctionDeclaration(node) || + arkts.isVariableDeclaration(node) || + arkts.isScriptFunction(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node)) { + // Rule 5: Local, Param, Event decorators must be used with Property + this.checkuseStateDecoratorsWithProperty(node); + } + if (!arkts.isStructDeclaration(node)) { + return; + } + this.validateClassPropertyDecorators(node); + } -function validateClassPropertyDecorators(context: UISyntaxRuleContext, node: arkts.StructDeclaration): void { - const isComponentV2 = hasisComponentV2(node); - const isComponent = hasComponent(node); - node.definition.body.forEach(member => { - if (!arkts.isClassProperty(member)) { - return; + private hasComponentV2Annotation = (node: arkts.StructDeclaration): boolean => !!getAnnotationUsage(node, + PresetDecorators.COMPONENT_V2); + + private checkMultipleBuiltInDecorators(member: arkts.ClassProperty, + propertyDecorators: string[]): void { + const appliedBuiltInDecorators = propertyDecorators.filter(d => builtInDecorators.includes(d)); + if (appliedBuiltInDecorators.length > 1) { + member.annotations.forEach(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr)) { + const annotationsName = annotation.expr.name; + this.reportMultipleBuiltInDecoratorsError(annotation, annotationsName, builtInDecorators); + } + }); + } + }; + + private reportMultipleBuiltInDecoratorsError(annotation: arkts.AstNode, + annotationsName: string | undefined, builtInDecorators: string[]): void { + if (annotationsName && builtInDecorators.includes(annotationsName)) { + this.report({ + node: annotation, + message: this.messages.multipleBuiltInDecorators, + }); + } } - const propertyDecorators = getClassPropertyAnnotationNames(member); - // Rule 1: Multiple built-in decorators - checkMultipleBuiltInDecorators(context, member, propertyDecorators); + private checkParamRequiresRequire(member: arkts.ClassProperty, + propertyDecorators: string[]): void { + if (propertyDecorators.includes(PresetDecorators.PARAM) && !member.value && + !propertyDecorators.includes(PresetDecorators.REQUIRE) && member.key) { + const memberKey = member.key; + this.report({ + node: memberKey, + message: this.messages.paramRequiresRequire, + fix: (memberKey) => { + const startPosition = memberKey.startPosition; + return { + title: 'Add @Require annotation', + range: [startPosition, startPosition], + code: `@${PresetDecorators.REQUIRE} `, + }; + }, + }); + } + }; - // Rule 2: Built-in decorators only allowed in @isComponentV2 - checkDecoratorOnlyInisComponentV2(context, member, node, isComponentV2, isComponent); + private checkRequireOnlyWithParam(member: arkts.ClassProperty, + propertyDecorators: string[], isComponentV2: boolean): void { + const requireDecorator = member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && annotation.expr.name === PresetDecorators.REQUIRE + ); + if (isComponentV2 && + requireDecorator && + !propertyDecorators.includes(PresetDecorators.PARAM) && + !propertyDecorators.includes(PresetDecorators.BUILDER_PARAM)) { + this.report({ + node: requireDecorator, + message: this.messages.requireOnlyWithParam, + fix: (requireDecorator) => { + let startPosition = requireDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = requireDecorator.endPosition; + return { + title: 'Remove the @Require annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + }; - // Rule 3: @Param without default value must be combined with @Require - checkParamRequiresRequire(context, member, propertyDecorators); + private checkuseStateDecoratorsWithProperty( + node: arkts.FunctionDeclaration | + arkts.VariableDeclaration | + arkts.ScriptFunction | + arkts.TSInterfaceDeclaration | + arkts.ClassDefinition | + arkts.TSTypeAliasDeclaration): void { + node.annotations.forEach(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr) && + builtInDecorators.includes(annotation.expr.name)) { + const annotationName = annotation.expr.name; + this.reportInvalidDecoratorOnMethod(annotation, annotationName); + } + }); + } - // Rule 4: @Require must be used together with @Param - checkRequireOnlyWithParam(context, member, propertyDecorators); - }); -} + private reportInvalidDecoratorOnMethod(annotation: arkts.AnnotationUsage, + annotationName: string): void { + this.report({ + node: annotation, + message: this.messages.useStateDecoratorsWithProperty, + data: { annotationName }, + fix: (annotation) => { + let startPosition = annotation.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = annotation.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } -const rule: UISyntaxRule = { - name: 'iscomponentV2-state-usage-validation', - messages: { - multipleBuiltInDecorators: `The member property or method cannot be decorated by multiple built-in decorators.`, - decoratorOnlyInisComponentV2: `The '@{{annotationsName}}' decorator can only be used in a 'struct' decorated with '@isComponentV2'.`, - paramRequiresRequire: `When a variable decorated with @Param is not assigned a default value, it must also be decorated with @Require.`, - requireOnlyWithParam: `In a struct decorated with @isComponentV2, @Require can only be used with @Param. ` - }, + private validateClassPropertyDecorators(node: arkts.StructDeclaration): void { + this.checkuseStateDecoratorsWithProperty(node.definition); + const isComponentV2 = this.hasComponentV2Annotation(node); + node.definition.body.forEach(member => { + if (!arkts.isClassProperty(member)) { + return; + } + const propertyDecorators = getClassPropertyAnnotationNames(member); + // Rule 1: Multiple built-in decorators + this.checkMultipleBuiltInDecorators(member, propertyDecorators); - setup(context) { - return { - parsed: (node): void => { + // Rule 2: @Param without default value must be combined with @Require + this.checkParamRequiresRequire(member, propertyDecorators); - if (!arkts.isStructDeclaration(node)) { - return; + // Rule 3: @Require must be used together with @Param + this.checkRequireOnlyWithParam(member, propertyDecorators, isComponentV2); + }); + } + + private checkDecorator(annoArray: readonly arkts.AnnotationUsage[], decorator: string): boolean { + let flag = false; + annoArray.forEach((anno) => { + if (!anno.expr) { + return; + } + const annoName = getIdentifierName(anno.expr); + if (annoName === decorator) { + flag = true; + } + }); + return flag; + } + + // Define a function to add property data to the property map + private addPropertyMap( + structName: string, + propertyName: string, + annotationName: string, + propertyMap: Map> + ): void { + if (!propertyMap.has(structName)) { + propertyMap.set(structName, new Map()); } - validateClassPropertyDecorators(context, node); - }, - }; - }, + const structProperties = propertyMap.get(structName); + if (structProperties) { + structProperties.set(propertyName, annotationName); + } + } + + // Iterate through the incoming componentv2 node to see if there are any state variables for decorator decorations + private initComponentV2PropertyMap( + node: arkts.AstNode, + componentV2PropertyMap: Map> + ): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident || + !this.checkDecorator(member.definition.annotations, PresetDecorators.COMPONENT_V2)) { + return; + } + let structName: string = member.definition.ident.name; + member.definition.body.forEach((item) => { + this.processClassPropertyAnnotations(item, structName, componentV2PropertyMap); + }); + }); + } + + private processClassPropertyAnnotations( + item: arkts.AstNode, + structName: string, + componentV2PropertyMap: Map> + ): void { + if (!arkts.isClassProperty(item) || !item.key) { + return; + } + let propertyName: string = getIdentifierName(item.key); + // If there is no decorator, it is a regular type + if (item.annotations.length === 0) { + let annotationName: string = PresetDecorators.REGULAR; + this.addPropertyMap(structName, propertyName, annotationName, componentV2PropertyMap); + } + item.annotations.forEach((annotation) => { + if (!annotation.expr) { + return; + } + let annotationName: string = getIdentifierName(annotation.expr); + if (annotationName === PresetDecorators.LOCAL) { + this.addPropertyMap(structName, propertyName, annotationName, componentV2PropertyMap); + } + }); + } + + private checkInitializeRule( + node: arkts.AstNode, + + componentV2PropertyMap: Map>, + ): void { + if (!arkts.isIdentifier(node) || !componentV2PropertyMap.has(getIdentifierName(node)) || !node.parent) { + return; + } + let structName: string = getIdentifierName(node); + let parentNode: arkts.AstNode = node.parent; + if (!arkts.isCallExpression(parentNode)) { + return; + } + let structNode = node.parent; + while (!arkts.isStructDeclaration(structNode)) { + if (!structNode.parent) { + return; + } + structNode = structNode.parent; + } + let parentPropertyMap: Map = new Map(); + structNode.definition.body.forEach((property) => { + if (!arkts.isClassProperty(property)) { + return; + } + let propertyArray: string[] = []; + property.annotations.forEach((annotation) => { + if (!annotation.expr || !arkts.isIdentifier(annotation.expr)) { + return; + } + propertyArray.push(annotation.expr.name); + }); + const classPropertyName = getClassPropertyName(property); + if (!classPropertyName) { + return; + } + parentPropertyMap.set(classPropertyName, propertyArray); + }); + // Gets all the properties recorded by the struct + const childPropertyName: Map = componentV2PropertyMap.get(structName)!; + parentNode.arguments.forEach((argument) => { + if (!arkts.isObjectExpression(argument)) { + return; + } + argument.getChildren().forEach((property) => { + if (!arkts.isProperty(property) || !property.key) { + return; + } + const childkeyName = getIdentifierName(property.key); + if (!childPropertyName.has(childkeyName)) { + return; + } + this.reportLocalNeedInit(childPropertyName, childkeyName, property, structName); + }); + }); + } + + private reportLocalNeedInit(childPropertyName: Map, childkeyName: string, + property: arkts.Property, structName: string): void { + if (childPropertyName.get(childkeyName) === PresetDecorators.LOCAL) { + this.report({ + node: property, + message: this.messages.localNeedNoInit, + data: { + decoratorName: '@' + childPropertyName.get(childkeyName)!, + key: childkeyName, + componentName: structName, + }, + fix: (property) => { + return { + title: 'Remove the property', + range: [property.startPosition, property.endPosition], + code: '', + }; + } + }); + } + if (childPropertyName.get(childkeyName) === PresetDecorators.REGULAR) { + this.report({ + node: property, + message: this.messages.localNeedNoInit, + data: { + decoratorName: PresetDecorators.REGULAR, + key: childkeyName, + componentName: structName, + }, + fix: (property) => { + return { + title: 'Remove the property', + range: [property.startPosition, property.endPosition], + code: '', + }; + } + }); + } + } }; -export default rule; \ No newline at end of file +export default ComponentV2StateUsageValidationRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts new file mode 100644 index 000000000..fd271307d --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { getClassAnnotationUsage, getIdentifierName, PresetDecorators, getAnnotationUsage, BUILD_NAME, findDecorator } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class ComputedDecoratorCheckRule extends AbstractUISyntaxRule { + private computedGetters: Map = new Map(); + private computedSetters: Map = new Map(); + + public setup(): Record { + return { + onlyOnGetter: `@Computed can only decorate 'GetAccessor'.`, + onlyInObservedV2: `The '@Computed' can decorate only member method within a 'class' decorated with ObservedV2.`, + componentV2InStruct: `The '@Computed' annotation can only be used in a 'struct' decorated with ComponentV2.`, + noTwoWayBinding: `A property decorated by '@Computed' cannot be used with two-way bind syntax.`, + computedMethodDefineSet: `A property decorated by '@Computed' cannot define a set method.` + }; + } + + public beforeTransform(): void { + this.computedGetters = new Map(); + this.computedSetters = new Map(); + } + + public parsed(node: arkts.AstNode): void { + if (arkts.isStructDeclaration(node)) { + this.validateComponentV2InStruct(node); + this.validateStructBody(node); + } + + if (arkts.isClassDeclaration(node)) { + this.validateClassBody(node); + } + } + + private validateStructBody(node: arkts.StructDeclaration): void { + let computedDecorator: arkts.AnnotationUsage | undefined; + node.definition.body.forEach((member) => { + if (arkts.isClassProperty(member)) { + this.validateComputedOnClassProperty(member); + return; + } + + if (arkts.isMethodDefinition(member)) { + const methodName = getIdentifierName(member.name); + computedDecorator = findDecorator(member.scriptFunction, PresetDecorators.COMPUTED); + + this.validateComputedMethodKind(member, computedDecorator, methodName); + if (member.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET) { + this.computedSetters.set(methodName, member); + } + + if (methodName === BUILD_NAME) { + this.validateBuildMethod(member); + } + } + }); + + this.validateGetterSetterConflict(); + } + + private validateComputedOnClassProperty(member: arkts.ClassProperty): void { + const computedDecorator = findDecorator(member, PresetDecorators.COMPUTED); + if (computedDecorator) { + this.report({ + node: computedDecorator, + message: this.messages.onlyOnGetter, + fix: (computedDecorator) => { + let startPosition = computedDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = computedDecorator.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + } + + private validateComputedMethodKind( + member: arkts.MethodDefinition, + computedDecorator: arkts.AnnotationUsage | undefined, + methodName: string + ): void { + if (computedDecorator) { + const isGetter = member.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; + if (!isGetter) { + this.reportValidateComputedMethodKind(computedDecorator); + } else { + this.computedGetters.set(methodName, member); + } + } + } + + private reportValidateComputedMethodKind(computedDecorator: arkts.AnnotationUsage | undefined): void { + if (!computedDecorator) { + return; + } + this.report({ + node: computedDecorator, + message: this.messages.onlyOnGetter, + fix: (computedDecorator) => { + let startPosition = computedDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = computedDecorator.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private validateBuildMethod(member: arkts.MethodDefinition): void { + member.scriptFunction.body?.getChildren().forEach((childNode) => { + if (!arkts.isExpressionStatement(childNode)) { + return; + } + const queue: Array = [childNode]; + while (queue.length > 0) { + const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; + if (arkts.isCallExpression(currentNode)) { + this.validateCallExpression(currentNode); + } + // Continue traversing the child nodes + const children = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } + }); + } + + private validateCallExpression(currentNode: arkts.CallExpression): void { + if (!arkts.isIdentifier(currentNode.expression) || getIdentifierName(currentNode.expression) !== '$$') { + return; + } + + currentNode.arguments.forEach((argument) => { + if (arkts.isMemberExpression(argument)) { + const getterName = getIdentifierName(argument.property); + this.reportValidateCallExpression(currentNode, getterName); + } + }); + } + + private reportValidateCallExpression( + currentNode: arkts.CallExpression, + getterName: string + ): void { + if (this.computedGetters.has(getterName)) { + this.report({ + node: currentNode, + message: this.messages.noTwoWayBinding, + }); + } + } + + private validateGetterSetterConflict(): void { + for (const [name] of this.computedGetters) { + if (this.computedSetters.has(name)) { + this.reportValidateGetterSetterConflict(name); + } + } + } + + private reportValidateGetterSetterConflict(name: string): void { + const setter = this.computedSetters.get(name)!; + this.report({ + node: setter, + message: this.messages.computedMethodDefineSet, + fix: (node) => { + const startPosition = node.startPosition; + const endPosition = node.endPosition; + return { + title: 'Remove the set method', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private validateClassBody(node: arkts.ClassDeclaration): void { + const observedV2Decorator = getClassAnnotationUsage(node, PresetDecorators.OBSERVED_V2); + const observedDecorator = getClassAnnotationUsage(node, PresetDecorators.OBSERVED_V1); + + node.definition?.body.forEach((member) => { + if (arkts.isMethodDefinition(member)) { + + this.validateComputedInClass(node, member, observedV2Decorator, observedDecorator); + const computedDecorator = findDecorator(member.scriptFunction, PresetDecorators.COMPUTED); + if (!arkts.isIdentifier(member.name)) { + return; + } + const methodName = getIdentifierName(member.name); + + this.validateComputedMethodKind(member, computedDecorator, methodName); + } + }); + } + + private validateComponentV2InStruct(node: arkts.StructDeclaration): void { + const componentV2Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); + const componentDecorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); + + node.definition?.body.forEach((member) => { + if (arkts.isMethodDefinition(member)) { + this.checkComponentV2InStruct(node, member, componentV2Decorator, componentDecorator); + } + }); + } + + private checkComponentV2InStruct( + node: arkts.StructDeclaration | arkts.ClassDeclaration, + member: arkts.MethodDefinition, + componentV2Decorator: arkts.AnnotationUsage | undefined, + componentDecorator: arkts.AnnotationUsage | undefined + ): void { + const computedDecorator = findDecorator(member.scriptFunction, PresetDecorators.COMPUTED); + if (computedDecorator && !componentV2Decorator && !componentDecorator) { + this.report({ + node: computedDecorator, + message: this.messages.componentV2InStruct, + fix: () => { + const startPosition = node.startPosition; + const endPosition = node.startPosition; + return { + title: 'Add @ComponentV2 annotation', + range: [startPosition, endPosition], + code: `@${PresetDecorators.COMPONENT_V2}\n`, + }; + }, + }); + } + + if (computedDecorator && !componentV2Decorator && componentDecorator) { + this.report({ + node: computedDecorator, + message: this.messages.componentV2InStruct, + fix: () => { + const startPosition = componentDecorator.startPosition; + const endPosition = componentDecorator.endPosition; + return { + title: 'Change @Component to @ComponentV2', + range: [startPosition, endPosition], + code: `${PresetDecorators.COMPONENT_V2}`, + }; + }, + }); + } + } + + private validateComputedInClass( + node: arkts.AstNode, + member: arkts.MethodDefinition, + observedV2Decorator: arkts.AnnotationUsage | undefined, + observedDecorator: arkts.AnnotationUsage | undefined + ): void { + const computedDecorator = findDecorator(member.scriptFunction, PresetDecorators.COMPUTED); + if (computedDecorator && !observedV2Decorator && !observedDecorator && + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET === member.kind) { + this.report({ + node: computedDecorator, + message: this.messages.onlyInObservedV2, + fix: () => { + const startPosition = node.startPosition; + return { + title: 'Add @ObservedV2 annotation', + range: [startPosition, startPosition], + code: `@${PresetDecorators.OBSERVED_V2}\n`, + }; + }, + }); + } + if (computedDecorator && !observedV2Decorator && observedDecorator && + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET === member.kind) { + this.report({ + node: computedDecorator, + message: this.messages.onlyInObservedV2, + fix: () => { + const startPosition = observedDecorator.startPosition; + const endPosition = observedDecorator.endPosition; + return { + title: 'Change @Observed to @ObservedV2', + range: [startPosition, endPosition], + code: `${PresetDecorators.OBSERVED_V2}`, + }; + }, + }); + } + } +} + +export default ComputedDecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts b/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts index f893b01de..784cecad5 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts @@ -14,99 +14,126 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; -import { PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { getClassPropertyAnnotationNames, getClassPropertyName, getIdentifierName, PresetDecorators } from '../utils'; -function recordStructWithLinkDecorators(item: arkts.AstNode, structName: string, linkMap: Map): void { - if (!arkts.isClassProperty(item)) { - return; - } - item.annotations?.forEach((annotation) => { - const annotationName: string = annotation.expr?.dumpSrc() ?? ''; - if (annotationName === '') { - return; + +class ConstructParameterLiteralRule extends AbstractUISyntaxRule { + // Record all @Link and @ObjectLink attributes + private linkMap: Map> = new Map(); + + public setup(): Record { + return { + initializerIsLiteral: `The 'regular' property '{{initializerName}}' cannot be assigned to the '{{parameter}}' property '{{parameterName}}'.`, + }; } - // If the node has properties decorated with Link or ObjectLink, record this structure node - if (annotationName === PresetDecorators.LINK || annotationName === PresetDecorators.OBJECT_LINK) { - linkMap.set(structName, annotationName); + + public beforeTransform(): void { + this.linkMap = new Map(); } - }); -} -function initMap(node: arkts.AstNode, linkMap: Map): void { - // Check if the current node is the root node - if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - return; - } - node.getChildren().forEach((member) => { - if (!arkts.isStructDeclaration(member)) { - return; + public parsed(node: arkts.StructDeclaration): void { + this.initMap(node); + this.checkInitializeWithLiteral(node); } - const structName: string = member.definition.ident?.name ?? ''; - if (structName === '') { - return; + + private addLinkProperty( + structName: string, + propertyName: string, + annotationName: string + ): void { + if (!this.linkMap.has(structName)) { + this.linkMap.set(structName, new Map()); + } + const structProperties = this.linkMap.get(structName); + if (!structProperties) { + return; + } + structProperties.set(propertyName, annotationName); } - member.definition?.body?.forEach((item) => { - recordStructWithLinkDecorators(item, structName, linkMap); - }); - }); -} -function checkInitializeWithLiteral(node: arkts.AstNode, context: UISyntaxRuleContext, - linkMap: Map -): void { - if (!arkts.isCallExpression(node)) { - return; - } - const componentName = node.expression.dumpSrc(); - // Only assignments to properties decorated with Link or ObjectLink trigger rule checks - if (!linkMap.has(componentName)) { - return; - } - node.arguments.forEach((member) => { - member.getChildren().forEach((property) => { - if (!arkts.isProperty(property)) { - return; - } - if (property.value === undefined) { - return; - } - const propertyType: arkts.Es2pandaAstNodeType = arkts.nodeType(property.value); - const key: string = property.key?.dumpSrc() ?? ''; - if (key === '') { - return; - } - const value = property.value?.dumpSrc() ? property.value.dumpSrc() : ''; - // If the assignment statement is not of type MemberExpression, throw an error - if (propertyType !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_MEMBER_EXPRESSION) { - context.report({ - node: property, - message: rule.messages.cannotInitializeWithLiteral, - data: { - value: value, - annotationName: linkMap.get(componentName)!, - key: key, - }, + private recordStructWithLinkDecorators(item: arkts.AstNode, structName: string): void { + if (!arkts.isClassProperty(item)) { + return; + } + const ClassPropertyAnnotations = getClassPropertyAnnotationNames(item); + + const classPropertyName = getClassPropertyName(item); + if (!classPropertyName) { + return; + } + if (ClassPropertyAnnotations.includes(PresetDecorators.OBJECT_LINK)) { + this.addLinkProperty(structName, classPropertyName, PresetDecorators.OBJECT_LINK); + } + if (ClassPropertyAnnotations.includes(PresetDecorators.LINK)) { + this.addLinkProperty(structName, classPropertyName, PresetDecorators.LINK); + } + } + + private initMap(node: arkts.AstNode): void { + // Check if the current node is the root node + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!(arkts.isStructDeclaration(member))) { + return; + } + if (!member.definition || !member.definition.ident || !arkts.isIdentifier(member.definition.ident)) { + return; + } + const structName: string = member.definition.ident.name; + if (structName === '') { + return; + } + member.definition?.body?.forEach((item) => { + this.recordStructWithLinkDecorators(item, structName); + }); }); - } - }); - }); -} + } -const rule: UISyntaxRule = { - name: 'construct-parameter-literal', - messages: { - cannotInitializeWithLiteral: `Assigning the attribute'{{value}}' to the '@{{annotationName}}' decorated attribute '{{key}}' is not allowed.`, - }, - setup(context) { - let linkMap: Map = new Map(); - return { - parsed: (node): void => { - initMap(node, linkMap); - checkInitializeWithLiteral(node, context, linkMap); - }, - }; - }, -}; + private checkInitializeWithLiteral(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; + } + const componentName = node.expression.name; + // Only assignments to properties decorated with Link or ObjectLink trigger rule checks + if (!this.linkMap.has(componentName)) { + return; + } + const structLinkMap = this.linkMap.get(componentName); + if (!structLinkMap) { + return; + } + node.arguments.forEach((member) => { + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property) || !property.key || !property.value) { + return; + } + const propertyName: string = getIdentifierName(property.key); + if (propertyName === '' || !structLinkMap.has(propertyName)) { + return; + } + // If the statement type is single-level MemberExpression or Identifier, construct-parameter is validated. + if (arkts.isMemberExpression(property.value) && arkts.isThisExpression(property.value.object)) { + return; + } + if (arkts.isIdentifier(property.value)) { + return; + } + const initializerName = property.value.dumpSrc().replace(/\(this\)/g, 'this'); + this.report({ + node: property, + message: this.messages.initializerIsLiteral, + data: { + initializerName: initializerName, + parameter: `@${structLinkMap.get(propertyName)}`, + parameterName: propertyName, + }, + }); + }); + }); + } +} -export default rule; \ No newline at end of file +export default ConstructParameterLiteralRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/construct-parameter.ts b/arkui-plugins/ui-syntax-plugins/rules/construct-parameter.ts new file mode 100644 index 000000000..9a4d5695c --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/construct-parameter.ts @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { + getIdentifierName, + getClassPropertyName, + getClassPropertyAnnotationNames, + PresetDecorators, + getAnnotationName, +} from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +// When a specific decorator is used as a parameter, the assigned decorator is not allowed +const disallowAssignedDecorators: string[] = [ + PresetDecorators.REGULAR, PresetDecorators.LINK, PresetDecorators.OBJECT_LINK, + PresetDecorators.BUILDER_PARAM, PresetDecorators.BUILDER, PresetDecorators.STATE, + PresetDecorators.PROP_REF, PresetDecorators.PROVIDE, PresetDecorators.CONSUME, + PresetDecorators.BUILDER, +]; +// The decorator structure prohibits initializing the assignment list +const restrictedDecoratorInitializations: Map = new Map([ + [PresetDecorators.REGULAR, [PresetDecorators.OBJECT_LINK, PresetDecorators.LINK]], + [PresetDecorators.PROVIDE, [PresetDecorators.REGULAR]], + [PresetDecorators.CONSUME, [PresetDecorators.REGULAR]], + [PresetDecorators.STORAGE_PROP_REF, [PresetDecorators.REGULAR]], + [PresetDecorators.VARIABLE, [PresetDecorators.LINK]], + [PresetDecorators.LOCAL_STORAGE_LINK, [PresetDecorators.REGULAR]], +]); +// When there are multiple Decorators, filter out the Decorators that are not relevant to the rule +const decoratorsFilter: string[] = [ + PresetDecorators.PROVIDE, PresetDecorators.CONSUME, PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.LOCAL_STORAGE_LINK, PresetDecorators.BUILDER_PARAM, +]; + +class ConstructParameterRule extends AbstractUISyntaxRule { + private propertyMap: Map> = new Map(); + private regularVariableList: string[] = []; + private builderFunctionList: string[] = []; + + public setup(): Record { + return { + constructParameter: `The '{{initializer}}' property '{{initializerName}}' cannot be assigned to the '{{parameter}}' property '{{parameterName}}'.`, + initializerIsBuilder: `'@Builder' function '{{initializerName}}' can only initialize '@BuilderParam' attribute.`, + parameterIsBuilderParam: `'@BuilderParam' attribute '{{parameterName}}' can only initialized by '@Builder' function or '@Builder' method in struct.`, + }; + } + + public beforeTransform(): void { + this.propertyMap = new Map(); + this.regularVariableList = []; + this.builderFunctionList = []; + } + + public parsed(node: arkts.StructDeclaration): void { + this.initList(node); + this.initPropertyMap(node); + this.checkConstructParameter(node); + } + + private getPropertyAnnotationName(node: arkts.AstNode, propertyName: string): string { + while (!arkts.isStructDeclaration(node)) { + if (!node.parent) { + return ''; + } + node = node.parent; + } + let annotationNames: string[] = []; + node.definition.body.forEach((item) => { + if (arkts.isClassProperty(item) && getClassPropertyName(item) === propertyName) { + annotationNames = getClassPropertyAnnotationNames(item); + } + if (arkts.isMethodDefinition(item) && getIdentifierName(item.name) === propertyName) { + annotationNames = item.scriptFunction.annotations.map((annotation) => + getAnnotationName(annotation) + ); + } + }); + if (annotationNames.length === 0) { + return PresetDecorators.REGULAR; + } + const annotationName = annotationNames.find((item) => { return decoratorsFilter.includes(item) }); + if (annotationName) { + return annotationName; + } + return ''; + } + + // Define a function to add property data to the property map + private addProperty( + structName: string, + propertyName: string, + annotationName: string + ): void { + if (!this.propertyMap.has(structName)) { + this.propertyMap.set(structName, new Map()); + } + const structProperties = this.propertyMap.get(structName); + if (!structProperties) { + return; + } + structProperties.set(propertyName, annotationName); + } + + private collectBuilderFunctions(member: arkts.AstNode): void { + if (!arkts.isFunctionDeclaration(member) || !member.annotations) { + return; + } + member.annotations.forEach(annotation => { + if (annotation.expr && getIdentifierName(annotation.expr) === PresetDecorators.BUILDER && + member.scriptFunction.id) { + this.builderFunctionList.push(member.scriptFunction.id.name); + } + }); + } + + private collectRegularVariables(member: arkts.AstNode): void { + if (!arkts.isVariableDeclaration(member) || !member.declarators) { + return; + } + member.getChildren().forEach((item) => { + if (!arkts.isVariableDeclarator(item) || !item.name || + (item.initializer && arkts.isArrowFunctionExpression(item.initializer))) { + return; + } + this.regularVariableList.push(item.name.name); + }); + } + + private initList(node: arkts.AstNode): void { + // Record variables and functions that @builder decorate + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + this.collectBuilderFunctions(member); + this.collectRegularVariables(member); + }); + } + + private recordRestrictedDecorators( + item: arkts.AstNode, + structName: string, + ): void { + if (!arkts.isClassProperty(item) || !item.key) { + return; + } + let propertyName: string = getIdentifierName(item.key); + // If there is no decorator, it is a regular type + if (item.annotations.length === 0) { + let annotationName: string = PresetDecorators.REGULAR; + this.addProperty(structName, propertyName, annotationName); + } + // Iterate through the decorator of the property, and when the decorator is in the disallowAssignedDecorators array, the property is recorded + item.annotations.forEach((annotation) => { + if (!annotation.expr) { + return; + } + let annotationName: string = getIdentifierName(annotation.expr); + if (disallowAssignedDecorators.includes(annotationName)) { + this.addProperty(structName, propertyName, annotationName); + } + }); + } + + private initPropertyMap(node: arkts.AstNode): void { + // Iterate through the root node ahead of time, noting the structure name, variable name, and corresponding decorator type + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident) { + return; + } + let structName: string = member.definition.ident?.name ?? ''; + if (structName === '') { + return; + } + member.definition?.body?.forEach((item) => { + this.recordRestrictedDecorators(item, structName); + }); + }); + } + + private reportRegularVariableError( + property: arkts.AstNode, + childType: string, + childName: string, + ): void { + if (!arkts.isProperty(property) || !property.value) { + return; + } + if (childType !== PresetDecorators.LINK) { + return; + } + if (arkts.isIdentifier(property.value) && this.regularVariableList.includes(property.value.name)) { + this.report({ + node: property, + message: this.messages.constructParameter, + data: { + initializer: PresetDecorators.REGULAR, + initializerName: property.value.name, + parameter: `@${childType}`, + parameterName: childName, + }, + }); + } + } + + private reportBuilderError( + property: arkts.AstNode, + childType: string, + childName: string, + ): void { + if (!arkts.isProperty(property) || !property.value) { + return; + } + let isBuilderInStruct: boolean = false; + if (arkts.isMemberExpression(property.value) && arkts.isIdentifier(property.value.property)) { + const parentName = property.value.property.name; + const parentType: string = this.getPropertyAnnotationName(property, parentName); + if (parentType === '') { + return; + } + isBuilderInStruct = parentType === PresetDecorators.BUILDER; + } + let isBuilder: boolean = false; + if (arkts.isIdentifier(property.value)) { + isBuilder = this.builderFunctionList.includes(property.value.name); + if (this.builderFunctionList.includes(property.value.name) && childType !== PresetDecorators.BUILDER_PARAM) { + this.report({ + node: property, + message: this.messages.initializerIsBuilder, + data: { + initializerName: property.value.name, + parameterName: childName, + }, + }); + } + } + if (childType === PresetDecorators.BUILDER_PARAM && !isBuilder && !isBuilderInStruct) { + this.report({ + node: property, + message: this.messages.parameterIsBuilderParam, + data: { + parameterName: childName, + }, + }); + } + } + + private checkConstructParameter(node: arkts.AstNode): void { + if (!arkts.isIdentifier(node) || !this.propertyMap.has(getIdentifierName(node))) { + return; + } + let structName: string = getIdentifierName(node); + if (!node.parent) { + return; + } + let parentNode: arkts.AstNode = node.parent; + if (!arkts.isCallExpression(parentNode)) { + return; + } + // Gets all the properties recorded by the struct + const childPropertyName: Map = this.propertyMap.get(structName)!; + parentNode.arguments.forEach((member) => { + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property) || !property.key) { + return; + } + const childName = getIdentifierName(property.key); + if (!childPropertyName.has(childName) || !property.value) { + return; + } + const childType: string = childPropertyName.get(childName)!; + this.reportRegularVariableError(property, childType, childName); + this.reportBuilderError(property, childType, childName); + if (!arkts.isMemberExpression(property.value) || !arkts.isThisExpression(property.value.object)) { + return; + } + const parentName = getIdentifierName(property.value.property); + const parentType: string = this.getPropertyAnnotationName(node, parentName); + if (parentType === '') { + return; + } + if (restrictedDecoratorInitializations.has(parentType) && + restrictedDecoratorInitializations.get(parentType)!.includes(childType)) { + this.report({ + node: property, + message: this.messages.constructParameter, + data: { + initializer: parentType === PresetDecorators.REGULAR ? PresetDecorators.REGULAR : `@${parentType}`, + initializerName: parentName, + parameter: childType === PresetDecorators.REGULAR ? PresetDecorators.REGULAR : `@${childType}`, + parameterName: childName, + }, + }); + } + }); + }); + } +} + +export default ConstructParameterRule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/consumer-provider-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/consumer-provider-decorator-check.ts index cb4f7fcd8..538f7e9fc 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/consumer-provider-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/consumer-provider-decorator-check.ts @@ -14,258 +14,312 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getAnnotationUsage, MultiMap, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; -// Traverse the member variables of the struct, recording the members of the @Consumer modifications -function processStructMembers( - node: arkts.StructDeclaration, - structName: string, - componentv2WithConsumer: MultiMap -): void { - node.definition.body.forEach((member) => { - // When a member variable is @consumer modified, it is stored to mark fields that cannot be initialized - if (arkts.isClassProperty(member)) { - const memberName = member?.key?.dumpSrc(); - structName && memberName ? componentv2WithConsumer.add(structName, memberName) : null; - } - }); -} -function rememberStructName(node: arkts.AstNode, componentv2WithConsumer: MultiMap): void { - // First it has to be of the struct type - if (arkts.isStructDeclaration(node)) { - node?.definition?.annotations.forEach((anno) => { - // Second, it must be decorated with a @component v2 decorator - if (anno.expr?.dumpSrc() === PresetDecorators.COMPONENT_V2) { - const structName = node.definition.ident?.name ?? ''; - processStructMembers(node, structName, componentv2WithConsumer); - } - }); - } -} -function findDecorator(member: arkts.ClassProperty, decorator: string): arkts.AnnotationUsage | undefined { - return member.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === decorator - ); -} -// Verify that the @Consumer decorator is used on the method -function validateConsumerOnMethod(member: arkts.MethodDefinition, context: UISyntaxRuleContext): void { - const annotationNode = member.scriptFunction.annotations?.find(annotation => - annotation.expr && annotation.expr.dumpSrc() === PresetDecorators.CONSUMER - ); - if (annotationNode) { - context.report({ - node: annotationNode, - message: rule.messages.consumerOnlyOnMember, - fix: (annotationNode) => { - const startPosition = arkts.getStartPosition(annotationNode); - const endPosition = arkts.getEndPosition(annotationNode); +import { getIdentifierName, MultiMap, PresetDecorators, getAnnotationName } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class ConsumerProviderDecoratorCheckRule extends AbstractUISyntaxRule { + private componentV2WithConsumer: MultiMap = new MultiMap(); + private componentV2WithProvider: MultiMap = new MultiMap(); + + public setup(): Record { return { - range: [startPosition, endPosition], - code: '', + providerAndConsumerOnlyOnProperty: `'@{{decorator}}' can only decorate member property.`, + multipleBuiltInDecorators: `The struct member variable can not be decorated by multiple built-in annotations.`, + providerAndConsumerOnlyInStruct: `The '@{{decorator}}' annotation can only be used with 'struct'.`, + forbiddenInitialization: `The '@{{decorator}}' property '{{value}}' in the custom component '{{structName}}' cannot be initialized here (forbidden to specify).`, }; - } - }); - } -} -// @Consumer Bugs that conflict with other decorators -function reportMultipleBuiltInDecorators( - hasConsumeDecorator: arkts.AnnotationUsage, - otherDecorators: arkts.AnnotationUsage, - context: UISyntaxRuleContext, -): void { - context.report({ - node: hasConsumeDecorator, - message: rule.messages.multipleBuiltInDecorators, - fix: (hasConsumeDecorator) => { - const startPosition = arkts.getStartPosition(otherDecorators); - const endPosition = arkts.getEndPosition(otherDecorators); - return { - range: [startPosition, endPosition], - code: '', - }; } - }); -} -// Report a bug where @Provider is missing @ComponentV2 -function reportProviderRequiresComponentV2( - hasProviderDecorator: arkts.AnnotationUsage, - hasComponent: arkts.AnnotationUsage | undefined, - node: arkts.AstNode, - context: UISyntaxRuleContext, -): void { - if (hasComponent) { - context.report({ - node: hasProviderDecorator, - message: rule.messages.providerRequiresComponentV2, - fix: (hasProviderDecorator) => { - const startPosition = arkts.getStartPosition(hasComponent); - const endPosition = arkts.getEndPosition(hasComponent); - return { - range: [startPosition, endPosition], - code: `@${PresetDecorators.COMPONENT_V2}`, - }; - } - }); - } else { - context.report({ - node: hasProviderDecorator, - message: rule.messages.providerRequiresComponentV2, - fix: (hasProviderDecorator) => { - const startPosition = arkts.getStartPosition(node); - const endPosition = startPosition; - return { - range: [startPosition, endPosition], - code: `@${PresetDecorators.COMPONENT_V2}\n`, - }; - } - }); - } -} -// Verify decorator conflicts on member variables -function validateMemberDecorators(member: arkts.ClassProperty, - hasComponentV2: arkts.AnnotationUsage | undefined, - hasComponent: arkts.AnnotationUsage | undefined, - node: arkts.AstNode, - context: UISyntaxRuleContext -): void { - const hasConsumeDecorator = findDecorator(member, PresetDecorators.CONSUMER); - const hasProviderDecorator = findDecorator(member, PresetDecorators.PROVIDER); - const otherDecorators = member.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() !== PresetDecorators.CONSUMER - ); - if (hasConsumeDecorator && otherDecorators) { - reportMultipleBuiltInDecorators(hasConsumeDecorator, otherDecorators, context); - } - if (hasProviderDecorator && !hasComponentV2) { - reportProviderRequiresComponentV2(hasProviderDecorator, hasComponent, node, context); - } -} -// Verify that @Provider is being used in the class -function validateProviderInClass(member: arkts.ClassProperty, context: UISyntaxRuleContext): void { - const hasProviderDecorator = findDecorator(member, PresetDecorators.PROVIDER); - if (hasProviderDecorator) { - context.report({ - node: hasProviderDecorator, - message: rule.messages.providerOnlyInStruct, - fix: (hasProviderDecorator) => { - const startPosition = arkts.getStartPosition(hasProviderDecorator); - const endPosition = arkts.getEndPosition(hasProviderDecorator); - return { - range: [startPosition, endPosition], - code: '', - }; - } - }); - } -} -// Verify that the current identifier is an illegally initialized @Consumer member variable -function reportValidateConsumer( - currentNode: arkts.Identifier, - callExpName: string, - componentv2WithConsumer: MultiMap, - context: UISyntaxRuleContext -): void { - if (componentv2WithConsumer.get(callExpName).includes(currentNode.dumpSrc())) { - context.report({ - node: currentNode.parent, - message: rule.messages.forbiddenInitialization, - data: { - value: currentNode.dumpSrc(), - structName: callExpName - }, - fix: () => { - const startPosition = arkts.getStartPosition(currentNode.parent); - const endPosition = arkts.getEndPosition(currentNode.parent); - return { - range: [startPosition, endPosition], - code: '', - }; - } - }); - } -} -// Verify that the @Consumer-decorated property is initialized -function validateConsumerInitialization(node: arkts.CallExpression, componentv2WithConsumer: MultiMap, - context: UISyntaxRuleContext): void { - const callExpName: string = node.expression.dumpSrc(); - if (componentv2WithConsumer.has(callExpName)) { - const queue: Array = [node]; - while (queue.length > 0) { - const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; - if (arkts.isIdentifier(currentNode)) { - reportValidateConsumer(currentNode, callExpName, componentv2WithConsumer, context); - } - const children = currentNode.getChildren(); - for (const child of children) { - queue.push(child); - } - } - } -} -function collectStructsWithConsumer(node: arkts.AstNode, componentv2WithConsumer: MultiMap): void { - // Used to document all V2 structs that use '@Consumer' - if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - // Breadth traversal is done through while and queues - const queue: Array = [node]; - while (queue.length > 0) { - const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; - // Filter and record the nodes of the tree - rememberStructName(currentNode, componentv2WithConsumer); - const children = currentNode.getChildren(); - for (const child of children) { - queue.push(child); - } + + public beforeTransform(): void { + this.componentV2WithConsumer = new MultiMap(); + this.componentV2WithProvider = new MultiMap(); } - } -} -function validateStructDecoratorsAndMembers(node: arkts.AstNode, context: UISyntaxRuleContext): void { - if (arkts.isStructDeclaration(node)) { - const hasComponentV2 = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); - const hasComponent = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); - node.definition.body.forEach(member => { - if (arkts.isMethodDefinition(member)) { - validateConsumerOnMethod(member, context); - } - if (arkts.isClassProperty(member)) { - validateMemberDecorators(member, hasComponentV2, hasComponent, node, context); - } - }); - } -} -function validateProviderInClasses(node: arkts.AstNode, context: UISyntaxRuleContext): void { - if (arkts.isClassDeclaration(node)) { - node.definition?.body.forEach(member => { - if (arkts.isClassProperty(member)) { - validateProviderInClass(member, context); - } - }); - } -} -const rule: UISyntaxRule = { - name: 'consumer-provider-decorator-check', - messages: { - consumerOnlyOnMember: `'@Consumer' can only decorate member property.`, - multipleBuiltInDecorators: `The struct member variable can not be decorated by multiple built-in decorators.`, - providerRequiresComponentV2: `The '@Provider' decorator can only be used in a 'struct' decorated with '@ComponentV2'.`, - providerOnlyInStruct: `The '@Provider' decorator can only be used with 'struct'.`, - forbiddenInitialization: `Property '{{value}}' in the custom component '{{structName}}' cannot be initialized here (forbidden to specify).`, - }, - setup(context) { - // Used to record the names of the corresponding structs and member variables that are @consumer modified - let componentv2WithConsumer: MultiMap = new MultiMap(); - return { - parsed: (node): void => { - collectStructsWithConsumer(node, componentv2WithConsumer); - validateStructDecoratorsAndMembers(node, context); - validateProviderInClasses(node, context); + + public parsed(node: arkts.AstNode): void { + this.collectStructsWithConsumerAndProvider(node); + this.validateDecoratorsOnMember(node); + this.validateOnlyInStruct(node); + if (arkts.isCallExpression(node)) { - validateConsumerInitialization(node, componentv2WithConsumer, context); + this.validateConsumerInitialization(node); + this.validateProviderInitialization(node); } - }, - }; - }, -}; + } + + private collectStructsWithConsumerAndProvider(node: arkts.AstNode): void { + if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + // Breadth traversal is done through while and queues + const queue: Array = [node]; + while (queue.length > 0) { + const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; + // Filter and record the nodes of the tree + this.rememberStructName(currentNode); + const children = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } + } + } + + private rememberStructName(node: arkts.AstNode): void { + if (arkts.isStructDeclaration(node)) { + node.definition.annotations.forEach((anno) => { + if (!anno.expr) { + return; + } + const annoName = getIdentifierName(anno.expr); + // Second, it must be decorated with a @component v2 decorator + if (annoName === PresetDecorators.COMPONENT_V2) { + const structName = node.definition.ident?.name ?? ''; + this.processStructMembers(node, structName); + } + }); + } + } + + private processStructMembers(node: arkts.StructDeclaration, structName: string): void { + node.definition.body.forEach((member) => { + // When a member variable is @consumer modified, it is stored to mark fields that cannot be initialized + if (arkts.isClassProperty(member)) { + const consumerDecorator = member.annotations.some(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.CONSUMER + ); + const providerDecorator = member.annotations.some(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.PROVIDER + ); + if (!member.key) { + return; + } + const memberName = getIdentifierName(member.key); + if (consumerDecorator && structName && memberName) { + this.componentV2WithConsumer.add(structName, memberName); + } + + if (providerDecorator && structName && memberName) { + this.componentV2WithProvider.add(structName, memberName); + } + } + }); + } + + private validateDecoratorsOnMember(node: arkts.AstNode): void { + if (arkts.isScriptFunction(node) || arkts.isVariableDeclaration(node) || arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node)) { + this.validateDecorator(node, this.messages.providerAndConsumerOnlyOnProperty, PresetDecorators.CONSUMER); + this.validateDecorator(node, this.messages.providerAndConsumerOnlyOnProperty, PresetDecorators.PROVIDER); + } + + if (arkts.isStructDeclaration(node)) { + node.definition.body.forEach(member => { + if (arkts.isClassProperty(member)) { + this.validateMemberDecorators(member); + } + }); + } + } + + private validateMemberDecorators( + member: arkts.ClassProperty, + ): void { + // Check that the @Consumer is not mixed with other decorators + this.validateMultipleBuiltInDecorators(member, PresetDecorators.CONSUMER); + + // Check that the @Provider is mixed with other decorators + this.validateMultipleBuiltInDecorators(member, PresetDecorators.PROVIDER); + } + + private validateMultipleBuiltInDecorators(member: arkts.ClassProperty, decoratorName: string): void { + const decorator = this.findDecorator(member, decoratorName); + const otherDecorators = this.findOtherDecorator(member, decoratorName); + if (!decorator || !otherDecorators) { + return; + } + this.report({ + node: decorator, + message: this.messages.multipleBuiltInDecorators, + data: { + decorator: getAnnotationName(decorator) + }, + fix: () => { + let startPosition = otherDecorators.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = otherDecorators.endPosition; + return { + title: 'Remove other annotations', + range: [startPosition, endPosition], + code: '', + }; + } + }); + } + + private findDecorator( + member: arkts.ClassProperty | arkts.VariableDeclaration | arkts.FunctionDeclaration | + arkts.ScriptFunction | arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration, + decoratorName: string + ): arkts.AnnotationUsage | undefined { + return member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === decoratorName + ); + } + + private findOtherDecorator(member: arkts.ClassProperty, decoratorName: string): arkts.AnnotationUsage | undefined { + return member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name !== decoratorName + ); + } + + private validateOnlyInStruct(node: arkts.AstNode): void { + if (arkts.isClassDeclaration(node)) { + node.definition?.body.forEach(member => { + if (arkts.isClassProperty(member)) { + this.validateDecorator(member, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.CONSUMER); + this.validateDecorator(member, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.PROVIDER); + } + if (arkts.isMethodDefinition(member)) { + this.validateDecorator( + member.scriptFunction, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.CONSUMER); + this.validateDecorator( + member.scriptFunction, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.PROVIDER); + } + }); + return; + } + + // function/ variable/ interface/ type alias declaration + if (arkts.isFunctionDeclaration(node) || + arkts.isVariableDeclaration(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node) + ) { + this.validateDecorator(node, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.CONSUMER); + this.validateDecorator(node, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.PROVIDER); + return; + } + } + + private validateDecorator( + node: arkts.ClassProperty | arkts.VariableDeclaration | arkts.FunctionDeclaration | + arkts.ScriptFunction | arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration, + message: string, + decoratorName: string + ): void { + const decorator = this.findDecorator(node, decoratorName); + if (!decorator) { + return; + } + + this.report({ + node: decorator, + message: message, + data: { + decorator: getAnnotationName(decorator), + }, + fix: (decorator) => { + let startPosition = decorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = decorator.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + } + }); + } + + private validateConsumerInitialization(node: arkts.CallExpression): void { + if (!arkts.isIdentifier(node.expression)) { + return; + } + const callExpName: string = node.expression.name; + if (this.componentV2WithConsumer.has(callExpName)) { + const queue: Array = [node]; + while (queue.length > 0) { + const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; + if (arkts.isIdentifier(currentNode)) { + this.checkInvalidConsumerUsage(currentNode, callExpName); + } + const children = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } + } + } + + private validateProviderInitialization(node: arkts.CallExpression): void { + if (!arkts.isIdentifier(node.expression)) { + return; + } + const callExpName: string = node.expression.name; + if (this.componentV2WithProvider.has(callExpName)) { + const queue: Array = [node]; + while (queue.length > 0) { + const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; + if (arkts.isIdentifier(currentNode)) { + this.checkInvalidProviderUsage(currentNode, callExpName); + } + const children = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } + } + } + + private checkInvalidConsumerUsage(currentNode: arkts.Identifier, callExpName: string): void { + const parent = currentNode.parent; + if (parent && this.componentV2WithConsumer.get(callExpName).includes(getIdentifierName(currentNode))) { + this.report({ + node: parent, + message: this.messages.forbiddenInitialization, + data: { + decorator: PresetDecorators.CONSUMER, + value: getIdentifierName(currentNode), + structName: callExpName + }, + fix: () => { + const startPosition = parent.startPosition; + const endPosition = parent.endPosition; + return { + title: 'Remove the property', + range: [startPosition, endPosition], + code: '', + }; + } + }); + } + } + + private checkInvalidProviderUsage(currentNode: arkts.Identifier, callExpName: string): void { + const parent = currentNode.parent; + if (parent && this.componentV2WithProvider.get(callExpName)?.includes(getIdentifierName(currentNode))) { + this.report({ + node: parent, + message: this.messages.forbiddenInitialization, + data: { + decorator: PresetDecorators.PROVIDER, + value: getIdentifierName(currentNode), + structName: callExpName + }, + fix: () => { + const startPosition = parent.startPosition; + const endPosition = parent.endPosition; + return { + title: 'Remove the property', + range: [startPosition, endPosition], + code: '', + }; + } + }); + } + } +} -export default rule; +export default ConsumerProviderDecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/custom-dialog-missing-controller.ts b/arkui-plugins/ui-syntax-plugins/rules/custom-dialog-missing-controller.ts index f7721812c..d35cf99f2 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/custom-dialog-missing-controller.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/custom-dialog-missing-controller.ts @@ -14,55 +14,91 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getClassPropertyType, PresetDecorators, getAnnotationUsage } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getClassPropertyType, PresetDecorators, getAnnotationUsage, isClassPropertyOptional } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; const CUSTOM_DIALOG_CONTROLLER: string = 'CustomDialogController'; -function missingController( - node: arkts.StructDeclaration, - context: UISyntaxRuleContext -): void { - // Check for the @CustomDialog decorator - const hasCustomDialogDecorator = getAnnotationUsage(node, PresetDecorators.CUSTOM_DIALOG); - const structName = node.definition.ident; - if (!structName) { - return; +class CustomDialogMissingControllerRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + missingController: `The @CustomDialog decorated custom component must contain a property of the CustomDialogController type.`, + }; } - // Check if there is an attribute of type CustomDialogController in the class - let hasControllerProperty = false; - node.definition.body.forEach((property) => { - if (arkts.isClassProperty(property)) { - const propertyType = getClassPropertyType(property); - if (propertyType === CUSTOM_DIALOG_CONTROLLER) { - hasControllerProperty = true; - } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; } - }); - if (!hasControllerProperty && hasCustomDialogDecorator) { - context.report({ - node: structName, - message: rule.messages.missingController, - }); + this.checkMissingController(node); } -} -const rule: UISyntaxRule = { - name: 'custom-dialog-missing-controller', - messages: { - missingController: `The @CustomDialog decorated custom component must contain a property of the CustomDialogController type.`, - }, - setup(context) { - return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { + // Check if the @CustomDialog-decorated struct contains a property of type CustomDialogController + private checkMissingController(node: arkts.StructDeclaration): void { + const customDialogDecorator = getAnnotationUsage(node, PresetDecorators.CUSTOM_DIALOG); + + if (!customDialogDecorator) { + return; + } + + const structName = node.definition.ident; + if (!structName) { + return; + } + + let hasControllerProperty = false; + + node.definition.body.forEach((property) => { + if (arkts.isClassProperty(property)) { + // Check if it's a union type, such as CustomDialogController | undefined + if (this.hasCustomDialogControllerInUnion(property)) { + hasControllerProperty = true; return; } - missingController(node, context); - }, - }; - }, -}; -export default rule; + // Check if it's directly of the CustomDialogController type + const propertyType = getClassPropertyType(property); + if (propertyType === CUSTOM_DIALOG_CONTROLLER) { + hasControllerProperty = true; + } + } + }); + + if (!hasControllerProperty) { + this.report({ + node: structName, + message: this.messages.missingController, + }); + } + } + + // Check that the property is of a form that contains a CustomDialogController in the union type (for example: CustomDialogController | undefined) + private hasCustomDialogControllerInUnion(property: arkts.ClassProperty): boolean { + if (!isClassPropertyOptional(property)) { + return false; + } + + if (!property.typeAnnotation || !arkts.isETSUnionType(property.typeAnnotation)) { + return false; + } + + for (const type of property.typeAnnotation.types) { + if (!arkts.isETSTypeReference(type)) { + continue; + } + + const part = type.part; + if (!part || !arkts.isETSTypeReferencePart(part)) { + continue; + } + + const name = part.name; + if (name && arkts.isIdentifier(name) && name.name === CUSTOM_DIALOG_CONTROLLER) { + return true; + } + } + return false; + } +} +export default CustomDialogMissingControllerRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/decorators-in-ui-component-only.ts b/arkui-plugins/ui-syntax-plugins/rules/decorators-in-ui-component-only.ts deleted file mode 100644 index b01e0d40a..000000000 --- a/arkui-plugins/ui-syntax-plugins/rules/decorators-in-ui-component-only.ts +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2025 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; -import { PresetDecorators } from '../utils/index'; - -// Helper function to find the '@Component' decorator in a ClassDeclaration and report errors. -function findComponentDecorator(context: UISyntaxRuleContext, node: arkts.ClassDeclaration): void { - const componentDecorator = node.definition?.annotations?.find( - (annotation) => - annotation.expr && - arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.COMPONENT_V1 - ); - if (componentDecorator) { - reportDecoratorError(context, componentDecorator, rule.messages.invalidComponentDecorator); - } -} - -// Helper function to find the '@Prop' decorator in a MethodDefinition or ClassProperty. -const findPropDecorator = (node: arkts.MethodDefinition | arkts.ClassProperty): arkts.AnnotationUsage | undefined => { - const annotations = 'scriptFunction' in node ? node.scriptFunction.annotations : node.annotations; - return annotations?.find( - (annotation) => - annotation.expr && annotation.expr.dumpSrc() === PresetDecorators.PROP - ); -}; - -// Rule 2: Check for '@Prop' on MethodDefinition -function checkPropOnMethod(context: UISyntaxRuleContext, node: arkts.MethodDefinition): void { - const propDecorator = findPropDecorator(node); - if (propDecorator) { - reportDecoratorError(context, propDecorator, rule.messages.propOnMethod); - } -}; - -// Rule 3: Check for '@Prop' on ClassProperty within a ClassDeclaration -function checkPropOnClassProperty(context: UISyntaxRuleContext, node: arkts.ClassProperty, currentNode: arkts.AstNode) - : void { - const propDecorator = findPropDecorator(node); - while (arkts.nodeType(currentNode) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - currentNode = currentNode.parent; - if (propDecorator && arkts.isClassDeclaration(currentNode)) { - reportDecoratorError(context, propDecorator, rule.messages.propOnMethod); - } - } -}; - -function reportDecoratorError(context: UISyntaxRuleContext, Decorator: arkts.AnnotationUsage, message: string -): void { - context.report({ - node: Decorator, - message: message, - fix: () => { - const startPosition = arkts.getStartPosition(Decorator); - const endPosition = arkts.getEndPosition(Decorator); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); -} - -const rule: UISyntaxRule = { - name: 'no-prop-on-method', - messages: { - invalidComponentDecorator: `'@Component' can decorate only custom components.`, - propOnMethod: `'@Prop' can decorate only member variables of custom components.`, - }, - setup(context) { - return { - parsed: (node: arkts.AstNode): void => { - if (arkts.isClassDeclaration(node)) { - findComponentDecorator(context, node); - } - if (arkts.isMethodDefinition(node)) { - checkPropOnMethod(context, node); - } - let currentNode = node; - if (arkts.isClassProperty(node)) { - checkPropOnClassProperty(context, node, currentNode); - } - }, - }; - }, -}; - -export default rule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/entry-localstorage-check.ts b/arkui-plugins/ui-syntax-plugins/rules/entry-localstorage-check.ts index aa82f3104..7fb289253 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/entry-localstorage-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/entry-localstorage-check.ts @@ -15,47 +15,46 @@ import * as arkts from '@koalaui/libarkts'; import { getAnnotationUsage, getClassPropertyAnnotationNames, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function checkLocalStorageLink(node: arkts.StructDeclaration, context: UISyntaxRuleContext): void { - // Check if @Entry decorator exists with parameter - const entryDecorator = getAnnotationUsage(node, PresetDecorators.ENTRY); - const isStorageUsed = entryDecorator && node.definition?.annotations[0].properties[0]; - // Check if @LocalStorageLink exists - let localStorageLinkUsed = false; - node.definition.body.forEach(body => { - if (!arkts.isClassProperty(body)) { - return; +class EntryLocalStorageCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + entryLocalStorageCheck: `'@Entry' should have a parameter, like '@Entry ({ storage: "__get_local_storage__" })'.`, + }; } - const propertyDecorators = getClassPropertyAnnotationNames(body); - localStorageLinkUsed = propertyDecorators.some( - decorator => decorator === PresetDecorators.LOCAL_STORAGE_LINK); - }); - // If @LocalStorageLink is used but @Entry(storage) is missing, report error - if (entryDecorator && localStorageLinkUsed && !isStorageUsed) { - context.report({ - node: entryDecorator, - message: rule.messages.invalidUsage - }); - } -} - -const rule: UISyntaxRule = { - name: 'entry-localstorage-check', - messages: { - invalidUsage: `@LocalStorageLink requires @Entry(storage) on the struct.`, - }, - setup(context) { - return { - parsed: (node): void => { + public parsed(node: arkts.AstNode): void { if (!arkts.isStructDeclaration(node)) { - return; + return; + } + this.checkLocalStorageLink(node); + } + + private checkLocalStorageLink(node: arkts.StructDeclaration): void { + // Check if @Entry decorator exists with parameter + const entryDecorator = getAnnotationUsage(node, PresetDecorators.ENTRY); + const isStorageUsed = entryDecorator && entryDecorator.properties[0]; + // Check if @LocalStorageLink exists + let localStorageLinkUsed = false; + node.definition.body.forEach(body => { + if (!arkts.isClassProperty(body)) { + return; + } + const propertyDecorators = getClassPropertyAnnotationNames(body); + localStorageLinkUsed = propertyDecorators.some( + decorator => decorator === PresetDecorators.LOCAL_STORAGE_LINK || + decorator === PresetDecorators.STORAGE_PROP_REF); + }); + + // If @LocalStorageLink is used but @Entry(storage) is missing, report error + if (entryDecorator && localStorageLinkUsed && !isStorageUsed) { + this.report({ + node: entryDecorator, + message: this.messages.entryLocalStorageCheck + }); } - checkLocalStorageLink(node, context); - }, - }; - }, -}; + } +} -export default rule; +export default EntryLocalStorageCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/entry-struct-no-export.ts b/arkui-plugins/ui-syntax-plugins/rules/entry-struct-no-export.ts index 7017ab54f..0bd71b7c7 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/entry-struct-no-export.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/entry-struct-no-export.ts @@ -15,39 +15,38 @@ import * as arkts from '@koalaui/libarkts'; import { getAnnotationUsage, PresetDecorators } from '../utils'; -import { UISyntaxRule } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const rule: UISyntaxRule = { - name: 'entry-struct-no-export', - messages: { - noExportWithEntry: `It's not a recommended way to export struct with @Entry decorator, which may cause ACE Engine error in component preview mode.`, - }, - setup(context) { - return { - parsed: (node): void => { +class EntryStructNoExportRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + noExportWithEntry: `It's not a recommended way to export struct with '@Entry' annotation, which may cause ACE Engine error in component preview mode.`, + }; + } + + public parsed(node: arkts.AstNode): void { // Check if the current node is a schema declaration if (!arkts.isStructDeclaration(node)) { - return; + return; } // Get the usage of the @Entry decorator const entryDecoratorUsage = getAnnotationUsage( - node, - PresetDecorators.ENTRY, + node, + PresetDecorators.ENTRY, ); //Determines whether the struct is exported - const isExported = node.dumpSrc().includes('export struct'); + const isStructExport = node.isExport; + const isStructDefaultExport = node.isDefaultExport; // If a @Entry decorator is present and the struct is exported - if (entryDecoratorUsage && isExported) { - context.report({ - node: entryDecoratorUsage, - message: this.messages.noExportWithEntry, - }); + if (entryDecoratorUsage && (isStructExport || isStructDefaultExport)) { + this.report({ + node: entryDecoratorUsage, + message: this.messages.noExportWithEntry, + }); } - }, - }; - }, + } }; -export default rule; \ No newline at end of file +export default EntryStructNoExportRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/index.ts b/arkui-plugins/ui-syntax-plugins/rules/index.ts index 13b008b62..6fdab3812 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/index.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/index.ts @@ -13,87 +13,115 @@ * limitations under the License. */ -import { UISyntaxRule } from './ui-syntax-rule'; -import BuildRootNode from './build-root-node'; -import CheckConstructPrivateParameter from './check-construct-private-parameter'; -import CheckDecoratedPropertyType from './check-decorated-property-type'; -import ComponentComponentV2MixUseCheck from './component-componentV2-mix-use-check'; -import ComponentV2MixCheck from './componentV2-mix-check'; -import ConsumerProviderDecoratorCheck from './consumer-provider-decorator-check'; -import ComponentV2StateUsageValidation from './componentV2-state-usage-validation'; -import ConstructParameterLiteral from './construct-parameter-literal'; -import CustomDialogMissingController from './custom-dialog-missing-controller'; -import DecoratorsInUIComponentOnly from './decorators-in-ui-component-only'; -import EntryLoacalStorageCheck from './entry-localstorage-check'; -import EntryStructNoExport from './entry-struct-no-export'; -import LocalBuilderCheck from './local-builder-check'; -import MonitorDecoratorCheck from './monitor-decorator-check'; -import NestedRelationship from './nested-relationship'; -import NoChildInButton from './no-child-in-button'; -import NoDuplicateDecorators from './no-duplicate-decorators'; -import NoDuplicateEntry from './no-duplicate-entry'; -import NoDuplicatePreview from './no-duplicate-preview'; -import NoDuplicateStateManager from './no-duplicate-state-manager'; -import NoPropLinkObjectlinkInEntry from './no-prop-link-objectlink-in-entry'; -import NoSameAsBuiltInAttribute from './no-same-as-built-in-attribute'; -import ReuseAttributeCheck from './reuse-attribute-check'; -import StructMissingDecorator from './struct-missing-decorator'; -import StructPropertyDecorator from './struct-property-decorator'; -import StructVariableInitialization from './struct-variable-initialization'; -import TrackDecoratorCheck from './track-decorator-check'; -import TypeDecoratorCheck from './type-decorator-check'; -import ValidateBuildInStruct from './validate-build-in-struct'; -import VariableInitializationViaComponentCons from './variable-initialization-via-component-cons'; -import WatchDecoratorFunction from './watch-decorator-function'; -import WatchDecoratorRegular from './watch-decorator-regular'; -import WrapBuilderCheck from './wrap-builder-check'; -import ObservedHeritageCompatibleCheck from './observed-heritage-compatible-check'; -import ObservedObservedV2 from './observed-observedV2-check'; -import ObservedV2TraceUsageValidation from './observedV2-trace-usage-validation'; -import OnceDecoratorCheck from './once-decorator-check'; -import OneDecoratorOnFunctionMethod from './one-decorator-on-function-method'; -import OldNewDecoratorMixUseCheck from './old-new-decorator-mix-use-check'; +import { UISyntaxRule, UISyntaxRuleConfig } from './ui-syntax-rule'; +import BuilderParamDecoratorCheckRule from './builderparam-decorator-check'; +import AttributeNoInvokeRule from './attribute-no-invoke'; +import BuildRootNodeRule from './build-root-node'; +import CheckConstructPrivateParameterRule from './check-construct-private-parameter'; +import CheckDecoratedPropertyTypeRule from './check-decorated-property-type'; +import CheckPropertyModifiersRule from './check-property-modifiers'; +import ComponentComponentV2MixUseCheckRule from './component-componentV2-mix-use-check'; +import ComponentV2MixCheckRule from './componentV2-mix-check'; +import ConstructParameterLiteralRule from './construct-parameter-literal'; +import ConstructParameterRule from './construct-parameter'; +import ConsumerProviderDecoratorCheckRule from './consumer-provider-decorator-check'; +import ComputedDecoratorCheckRule from './computed-decorator-check'; +import ComponentV2StateUsageValidationRule from './componentV2-state-usage-validation'; +import CustomDialogMissingControllerRule from './custom-dialog-missing-controller'; +import EntryLocalStorageCheckRule from './entry-localstorage-check'; +import EntryStructNoExportRule from './entry-struct-no-export'; +import MainPagesEntryCheckRule from './main-pages-entry-check'; +import MonitorDecoratorCheckRule from './monitor-decorator-check'; +import NestedRelationshipRule from './nested-relationship'; +import NestedReuseComponentCheckRule from './nested-reuse-component-check'; +import NoChildInButtonRule from './no-child-in-button'; +import NoDuplicateEntryRule from './no-duplicate-entry'; +import NoDuplicateIdRule from './no-duplicate-id'; +import NoDuplicatePreviewRule from './no-duplicate-preview'; +import NoPropLinkObjectLinkInEntryRule from './no-prop-link-objectlink-in-entry'; +import NoSameAsBuiltInAttributeRule from './no-same-as-built-in-attribute'; +import ReuseAttributeCheckRule from './reuse-attribute-check'; +import StaticParamRequireRule from './static-param-require'; +import StructMissingDecoratorRule from './struct-missing-decorator'; +import StructPropertyDecoratorRule from './struct-property-decorator'; +import TrackDecoratorCheckRule from './track-decorator-check'; +import ValidateBuildInStructRule from './validate-build-in-struct'; +import WrapBuilderCheckRule from './wrap-builder-check'; +import StructPropertyOptionalRule from './struct-property-optional'; +import StructVariableInitializationRule from './struct-variable-initialization'; +import UiConsistentCheckRule from './ui-consistent-check'; +import ValidateDecoratorTargetRule from './validate-decorator-target'; +import WatchDecoratorFunctionRule from './watch-decorator-function'; +import WatchDecoratorRegularRule from './watch-decorator-regular'; +import ObservedHeritageCompatibleCheckRule from './observed-heritage-compatible-check'; +import ObservedObservedV2Rule from './observed-observedV2-check'; +import ObservedV2TraceUsageValidationRule from './observedV2-trace-usage-validation'; +import OnceDecoratorCheckRule from './once-decorator-check'; +import OneDecoratorOnFunctionMethodRule from './one-decorator-on-function-method'; +import PropertyTypeRule from './property-type'; +import ReusableV2DecoratorCheckRule from './reusableV2-decorator-check'; +import VariableInitializationViaComponentConstructorRule from './variable-initialization-via-component-constructor'; +import ComponentComponentV2InitCheckRule from './component-componentV2-init-check'; +import StructNoExtendsRule from './struct-no-extends'; +import OldNewDecoratorMixUseCheckRule from './old-new-decorator-mix-use-check'; +import RequireDecoratorRegularRule from './require-decorator-regular'; +import ReusableComponentInV2CheckRule from './reusable-component-in-V2-check'; +import SpecificComponentChildrenRule from './specific-component-children'; -const rules: UISyntaxRule[] = [ - BuildRootNode, - CheckConstructPrivateParameter, - CheckDecoratedPropertyType, - ComponentComponentV2MixUseCheck, - ComponentV2MixCheck, - ConsumerProviderDecoratorCheck, - ComponentV2StateUsageValidation, - ConstructParameterLiteral, - CustomDialogMissingController, - DecoratorsInUIComponentOnly, - EntryLoacalStorageCheck, - EntryStructNoExport, - LocalBuilderCheck, - MonitorDecoratorCheck, - NestedRelationship, - NoChildInButton, - NoDuplicateDecorators, - NoDuplicateEntry, - NoDuplicatePreview, - NoDuplicateStateManager, - NoPropLinkObjectlinkInEntry, - NoSameAsBuiltInAttribute, - ReuseAttributeCheck, - StructMissingDecorator, - StructPropertyDecorator, - StructVariableInitialization, - TrackDecoratorCheck, - TypeDecoratorCheck, - ValidateBuildInStruct, - VariableInitializationViaComponentCons, - WatchDecoratorFunction, - WatchDecoratorRegular, - WrapBuilderCheck, - ObservedHeritageCompatibleCheck, - ObservedObservedV2, - ObservedV2TraceUsageValidation, - OnceDecoratorCheck, - OneDecoratorOnFunctionMethod, - OldNewDecoratorMixUseCheck, +const rules: Array = [ + [AttributeNoInvokeRule, 'warn'], + [BuildRootNodeRule, 'error'], + [BuilderParamDecoratorCheckRule, 'error'], + [CheckConstructPrivateParameterRule, 'warn'], + [CheckDecoratedPropertyTypeRule, 'error'], + [CheckPropertyModifiersRule, 'warn'], + [ComponentComponentV2MixUseCheckRule, 'error'], + [ComponentV2MixCheckRule, 'error'], + [ConstructParameterLiteralRule, 'warn'], + [ConstructParameterRule, 'error'], + [ConsumerProviderDecoratorCheckRule, 'error'], + [ComponentV2StateUsageValidationRule, 'error'], + [CustomDialogMissingControllerRule, 'error'], + [EntryLocalStorageCheckRule, 'warn'], + [EntryStructNoExportRule, 'warn'], + [MainPagesEntryCheckRule, 'error'], + [MonitorDecoratorCheckRule, 'error'], + [NestedRelationshipRule, 'error'], + [NestedReuseComponentCheckRule, 'error'], + [NoChildInButtonRule, 'error'], + [NoDuplicateEntryRule, 'error'], + [NoDuplicateIdRule, 'warn'], + [NoDuplicatePreviewRule, 'error'], + [NoPropLinkObjectLinkInEntryRule, 'warn'], + [NoSameAsBuiltInAttributeRule, 'error'], + [ReuseAttributeCheckRule, 'error'], + [StaticParamRequireRule, 'warn'], + [StructMissingDecoratorRule, 'error'], + [StructPropertyDecoratorRule, 'error'], + [TrackDecoratorCheckRule, 'error'], + [ValidateBuildInStructRule, 'error'], + [WrapBuilderCheckRule, 'error'], + [StructPropertyOptionalRule, 'warn'], + [StructVariableInitializationRule, 'error'], + [ObservedHeritageCompatibleCheckRule, 'error'], + [ObservedObservedV2Rule, 'error'], + [ObservedV2TraceUsageValidationRule, 'error'], + [OnceDecoratorCheckRule, 'error'], + [OneDecoratorOnFunctionMethodRule, 'error'], + [PropertyTypeRule, 'error'], + [ComputedDecoratorCheckRule, 'error'], + [ComponentComponentV2InitCheckRule, 'error'], + [ReusableV2DecoratorCheckRule, 'error'], + [VariableInitializationViaComponentConstructorRule, 'error'], + [StructNoExtendsRule, 'error'], + [UiConsistentCheckRule, 'warn'], + [ValidateDecoratorTargetRule, 'error'], + [WatchDecoratorFunctionRule, 'error'], + [WatchDecoratorRegularRule, 'error'], + [OldNewDecoratorMixUseCheckRule, 'error'], + [RequireDecoratorRegularRule, 'warn'], + [ReusableComponentInV2CheckRule, 'warn'], + [SpecificComponentChildrenRule, 'error'], ]; -export default rules; +export default rules; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/main-pages-entry-check.ts b/arkui-plugins/ui-syntax-plugins/rules/main-pages-entry-check.ts new file mode 100644 index 000000000..c0b9181e4 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/main-pages-entry-check.ts @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { getAnnotationUsage, getCurrentFilePath, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class MainPagesEntryCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + mainPagesEntryCheck: `A page configured in 'main_pages. json or build-profile. json5' must have one and only one '@Entry' annotation. ` + }; + } + public parsed(node: arkts.AstNode): void { + if (!arkts.isEtsScript(node) || node.isNamespace) { + return; + } + const currentFilePath = getCurrentFilePath(node); + if (!currentFilePath) { + return; + } + if (!this.context.getMainPages().includes(currentFilePath)) { + return; + } + let entryDecoratorCount = 0; + // Store the first StructDeclaration + let firstStructDeclaration: arkts.AstNode | undefined = undefined; + // Traverse all child nodes of the Program + for (const child of node.getChildren()) { + // Check if it's of type StructDeclaration + if (arkts.isStructDeclaration(child)) { + if (!firstStructDeclaration) { + firstStructDeclaration = child.definition.ident; + } + const entryDocoratorUsage = getAnnotationUsage( + child, + PresetDecorators.ENTRY, + ); + if (entryDocoratorUsage) { + ++entryDecoratorCount; + } + } + } + if (entryDecoratorCount === 0) { + this.report({ + node: firstStructDeclaration ? firstStructDeclaration : node, + message: this.messages.mainPagesEntryCheck, + }); + } + } +} + +export default MainPagesEntryCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts index e0696de48..e845cac44 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts @@ -14,161 +14,216 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -// Function declarations moved to the top with explicit return types -function getLocalMonitorUsed(body: arkts.MethodDefinition): arkts.AnnotationUsage | undefined { - const localMonitorUsed = body.scriptFunction.annotations?.find( - annotation => annotation.expr && - annotation.expr.dumpSrc() === PresetDecorators.MONITOR - ); - return localMonitorUsed; -} +import { getAnnotationUsage, PresetDecorators, getClassAnnotationUsage } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function checkConflictingDecorators(context: UISyntaxRuleContext, body: arkts.MethodDefinition, - localMonitorUsed: arkts.AnnotationUsage): boolean { - const conflictingDecorators = body.scriptFunction.annotations?.filter( - annotation => annotation.expr && - annotation.expr.dumpSrc() !== PresetDecorators.MONITOR - ); - if (conflictingDecorators?.length > 0) { - reportConflictingDecorators(context, localMonitorUsed, conflictingDecorators); - return true; - } - return false; -} +class MonitorDecoratorCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + monitorUsedAlone: + `The member property or method can not be decorated by multiple built-in annotations.`, + monitorUsedInObservedV2Class: + `The '@Monitor' can decorate only member method within a 'class' decorated with @ObservedV2.`, + monitorUsedInComponentV2Struct: + `The '@Monitor' annotation can only be used in a 'struct' decorated with '@ComponentV2'.`, + monitorDecorateMethod: + `@Monitor can only decorate method.` + }; + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isClassDeclaration(node) && !arkts.isStructDeclaration(node)) { + return; + } + + const monitorDecorator = this.checkMonitorUsage(node); + if (monitorDecorator && arkts.isClassDeclaration(node)) { + this.checkMonitorInClass(node, monitorDecorator); + } -function reportConflictingDecorators(context: UISyntaxRuleContext, localMonitorUsed: arkts.AstNode, - conflictingDecorators: arkts.AnnotationUsage[]): void { - context.report({ - node: localMonitorUsed, - message: rule.messages.invalidUsage1, - fix: () => { - const startPositions = conflictingDecorators.map(annotation => - arkts.getStartPosition(annotation)); - const endPositions = conflictingDecorators.map(annotation => arkts.getEndPosition(annotation)); - const startPosition = startPositions[0]; - const endPosition = endPositions[endPositions.length - 1]; - return { - range: [startPosition, endPosition], - code: '' - }; + if (monitorDecorator && arkts.isStructDeclaration(node)) { + this.checkMonitorInStruct(node, monitorDecorator); + } + this.checkDecorateMethod(node); } - }); -} -function checkIfClassIsObservedV2(node: arkts.ClassDeclaration): boolean { - return node.definition?.annotations?.some( - observedV2 => observedV2.expr?.dumpSrc() === PresetDecorators.OBSERVED_V2 - ) ?? false; -} -function checkIfStructIsComponentV2(node: arkts.StructDeclaration): boolean { - return node.definition?.annotations?.some( - componentV2 => componentV2.expr?.dumpSrc() === PresetDecorators.COMPONENT_V2 - ) ?? false; -} + private checkMonitorInClass( + node: arkts.ClassDeclaration, + monitorDecorator: arkts.AnnotationUsage | undefined, + ): void { + if (!monitorDecorator) { + return; + } + const isObservedV2 = this.checkDecorator(node, PresetDecorators.OBSERVED_V2); + const observedV1Decorator = getClassAnnotationUsage(node, PresetDecorators.OBSERVED_V1); -function reportInvalidUsage(context: UISyntaxRuleContext, node: arkts.AstNode, message: string, fixCode: string) - : void { - const startPosition = arkts.getStartPosition(node); - context.report({ - node, - message, - fix: () => ({ - range: [startPosition, startPosition], - code: fixCode, - }), - }); -} + if (!isObservedV2 && !observedV1Decorator) { + this.report({ + node: monitorDecorator, + message: this.messages.monitorUsedInObservedV2Class, + fix: () => { + return { + title: 'Add @ObservedV2 annotation', + range: [node.startPosition, node.startPosition], + code: `@${PresetDecorators.OBSERVED_V2}\n` + }; + } + }); + return; + } + if (!isObservedV2 && observedV1Decorator) { + this.report({ + node: monitorDecorator, + message: this.messages.monitorUsedInObservedV2Class, + fix: () => { + return { + title: 'Change @Observed to @ObservedV2', + range: [observedV1Decorator.startPosition, observedV1Decorator.endPosition], + code: `${PresetDecorators.OBSERVED_V2}` + }; + } + }); + } + } + + private checkMonitorInStruct( + node: arkts.StructDeclaration, + monitorDecorator: arkts.AnnotationUsage | undefined, + ): void { + if (!monitorDecorator) { + return; + } + const componentV1Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); + const isComponentV2 = this.checkDecorator(node, PresetDecorators.COMPONENT_V2); + if (!isComponentV2 && !componentV1Decorator) { + this.report({ + node: monitorDecorator, + message: this.messages.monitorUsedInComponentV2Struct, + fix: () => ({ + title: 'Add @ComponentV2 annotation', + range: [node.startPosition, node.startPosition], + code: `@${PresetDecorators.COMPONENT_V2}\n` + }) + }); + return; + } -function checkMultipleDecorators( - node: arkts.ClassDeclaration | arkts.StructDeclaration, - context: UISyntaxRuleContext -): boolean { - // Traverse body of the class to check for @Monitor usage - let monitorUsed: boolean = false; - node.definition?.body.forEach(body => { - if (arkts.isMethodDefinition(body)) { - const localMonitorUsed = getLocalMonitorUsed(body); - if (localMonitorUsed) { - monitorUsed = true; - checkConflictingDecorators(context, body, localMonitorUsed); - return; // Stop further checks for this method - } + if (!isComponentV2 && componentV1Decorator) { + this.report({ + node: monitorDecorator, + message: this.messages.monitorUsedInComponentV2Struct, + fix: () => { + return { + title: 'Change @Component to @ComponentV2', + range: [componentV1Decorator.startPosition, componentV1Decorator.endPosition], + code: `${PresetDecorators.COMPONENT_V2}` + }; + } + }); + } } - }); - return monitorUsed; -} -function checkDecorateMethod( - node: arkts.ClassDeclaration | arkts.StructDeclaration, - context: UISyntaxRuleContext -): void { - // Check if @Monitor is used on a property (which is not allowed) - node.definition?.body.forEach(body => { - if (!arkts.isClassProperty(body)) { - return; + private checkDecorator(node: arkts.ClassDeclaration | arkts.StructDeclaration, decoratorName: string): boolean { + return node.definition?.annotations?.some( + annotation => annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr?.name === decoratorName + ) ?? false; } - const monitorDecorator = body.annotations?.find( - annotation => annotation.expr?.dumpSrc() === PresetDecorators.MONITOR); - if (monitorDecorator === undefined) { - return; + + private checkMonitorUsage( + node: arkts.ClassDeclaration | arkts.StructDeclaration + ): arkts.AnnotationUsage | undefined { + let monitorUsage: arkts.AnnotationUsage | undefined; + + for (const body of node.definition?.body ?? []) { + if (!arkts.isMethodDefinition(body)) { + continue; + } + const currentMonitor = this.getLocalMonitorUsed(body); + + if (currentMonitor) { + monitorUsage = currentMonitor; + this.checkConflictingDecorators(body, currentMonitor); + break; + } + } + return monitorUsage; } - context.report({ - node: monitorDecorator, - message: rule.messages.invalidUsage4, - fix: () => { - const startPosition = arkts.getStartPosition(monitorDecorator); - const endPosition = arkts.getEndPosition(monitorDecorator); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); - }); -} -// The rule object with its setup method -const rule: UISyntaxRule = { - name: 'monitor-decorator-check', - messages: { - invalidUsage1: - `The member property or method can not be decorated by multiple built-in decorators.`, - invalidUsage2: - `The '@Monitor' can decorate only member method within a 'class' decorated with @ObservedV2.`, - invalidUsage3: - `The '@Monitor' decorator can only be used in a 'struct' decorated with '@ComponentV2'.`, - invalidUsage4: - `@Monitor can only decorate method`, - }, - setup(context) { - return { - parsed: (node: arkts.AstNode): void => { - if (!arkts.isClassDeclaration(node) && !arkts.isStructDeclaration(node)) { - return; + private getLocalMonitorUsed(body: arkts.MethodDefinition): arkts.AnnotationUsage | undefined { + const localMonitorUsed = body.scriptFunction.annotations?.find( + annotation => annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.MONITOR + ); + return localMonitorUsed; + } + + private checkConflictingDecorators(body: arkts.MethodDefinition, localMonitorUsed: arkts.AnnotationUsage): boolean { + const conflictingDecorators = body.scriptFunction.annotations?.filter( + annotation => annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name !== PresetDecorators.MONITOR + ); + if (conflictingDecorators?.length > 0) { + this.reportConflictingDecorators(localMonitorUsed, conflictingDecorators); + return true; } - let monitorUsed = false; + return false; + } - const isObservedV2 = arkts.isClassDeclaration(node) && checkIfClassIsObservedV2(node); - const isComponentV2 = arkts.isStructDeclaration(node) && checkIfStructIsComponentV2(node); + private reportConflictingDecorators(localMonitorUsed: arkts.AstNode, conflictingDecorators: arkts.AnnotationUsage[]): void { + this.report({ + node: localMonitorUsed, + message: this.messages.monitorUsedAlone, + fix: () => { + const startPositions = conflictingDecorators.map(annotation => + annotation.startPosition); + const endPositions = conflictingDecorators.map(annotation => annotation.endPosition); + let startPosition = startPositions[0]; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = endPositions[endPositions.length - 1]; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '' + }; + } + }); + } - monitorUsed = checkMultipleDecorators(node, context); + private checkDecorateMethod(node: arkts.ClassDeclaration | arkts.StructDeclaration): void { + // Check if @Monitor is used on a property (which is not allowed) + node.definition?.body.forEach((body) => { + if (!arkts.isClassProperty(body)) { + return; + } - // Check for errors related to @Monitor usage - if (monitorUsed && !isObservedV2 && arkts.isClassDeclaration(node)) { - reportInvalidUsage(context, node, rule.messages.invalidUsage2, `@${PresetDecorators.OBSERVED_V2}\n`); - } - if (monitorUsed && !isComponentV2 && arkts.isStructDeclaration(node)) { - reportInvalidUsage(context, node, rule.messages.invalidUsage3, `@${PresetDecorators.COMPONENT_V2}\n`); - } + const monitorDecorator = body.annotations?.find( + (annotation) => + annotation.expr && + arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.MONITOR + ); - checkDecorateMethod(node, context); - }, - }; - }, -}; + if (monitorDecorator === undefined) { + return; + } + this.report({ + node: monitorDecorator, + message: this.messages.monitorDecorateMethod, + fix: () => { + let startPosition = monitorDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = monitorDecorator.endPosition; + return { + title: 'Remove the @Monitor annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + }); + } +} -export default rule; +export default MonitorDecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/nested-relationship.ts b/arkui-plugins/ui-syntax-plugins/rules/nested-relationship.ts index fd18100e9..2f4ee91e0 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/nested-relationship.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/nested-relationship.ts @@ -14,178 +14,185 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getIdentifierName } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { + getIdentifierName, + isAtomicComponent, + isBuildInComponent, + isSingleChildComponent, + listToString, + SINGLE_CHILD_COMPONENT +} from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function checkInvalidChildInText(node: arkts.AstNode, context: UISyntaxRuleContext, textChild: string[]): void { - // Check if the current node is an identifier, and name is 'Text' - if (!arkts.isIdentifier(node)) { - return; - } - if (getIdentifierName(node) !== 'Text') { - return; - } - if (!node.parent) { - return; - } - const parentNode = node.parent; - if (!arkts.isCallExpression(parentNode)) { - return; - } - // If the BlockStatement contains a child component that should not exist under the text, an error will be reported - parentNode.getChildren().forEach(member => { - if (!arkts.isBlockStatement(member)) { - return; +class NestedRelationshipRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + noChildComponent: `The component '{{componentName}}' can't have any child.`, + singleChildComponent: `The '{{componentName}}' component can have only one child component.`, + delegateChildrenComponentChildren: `The '{{childComponentName}}' component cannot be a child component of the {{componentName}} component.`, + delegateChildrenComponentParent: `The component '{{parentComponentName}}' can only have the child component '{{childComponentList}}'.`, + delegateParentComponent: `The '{{componentName}}' component can only be nested in the '{{parentComponentList}}' parent component.`, + }; } - member.getChildren().forEach(sibling => { - if (!arkts.isExpressionStatement(sibling) || !arkts.isCallExpression(sibling.expression)) { - return; - } - const childComponentName = sibling.expression.expression.dumpSrc(); - if (!textChild.includes(childComponentName)) { - context.report({ - node: node, - message: rule.messages.invalidChildInText - }); - return; - } - }); - }); -} -function checkOneChildInButton(node: arkts.AstNode, context: UISyntaxRuleContext): void { - // Check if the current node is an identifier, and name is 'Button' - if (!arkts.isIdentifier(node)) { - return; - } - if (getIdentifierName(node) !== 'Button') { - return; - } - if (!node.parent) { - return; - } - const parentNode = node.parent; - if (!arkts.isCallExpression(parentNode)) { - return; - } - // If there is more than one subcomponent in the BlockStatement, an error is reported - parentNode.getChildren().forEach(member => { - if (!arkts.isBlockStatement(member)) { - return; + public parsed(node: arkts.StructDeclaration): void { + this.checkValidParentComponent(node); + this.checkValidChildComponent(node); + this.checkSingleChildComponent(node); + this.checkNoChildComponent(node); } - if (member.statements.length > 1) { - context.report({ - node: node, - message: rule.messages.oneChildInButton - }); - } - }); -} -function checkListItem(node: arkts.AstNode, context: UISyntaxRuleContext): void { - // Check if the current node is an identifier, and name is 'ListItem' - if (!arkts.isIdentifier(node)) { - return; - } - if (getIdentifierName(node) !== 'ListItem') { - return; - } - if (!node.parent || !node.parent.parent) { - return; - } - let curNode: arkts.AstNode = node.parent.parent; - do { - while (!arkts.isCallExpression(curNode)) { - if (!curNode.parent) { - return; - } - curNode = curNode.parent; + private checkValidParentComponent(node: arkts.AstNode): void { + // Check if the node is an identifier and if there are any restrictions on the parent component + if (!arkts.isIdentifier(node) || !this.context.componentsInfo) { + return; + } + const componentName: string = getIdentifierName(node); + if (!this.context.componentsInfo.validParentComponent.has(componentName) || !node.parent || !node.parent.parent) { + return; + } + let curNode = node.parent.parent; + while (!arkts.isCallExpression(curNode) || !arkts.isIdentifier(curNode.expression) || + !isBuildInComponent(this.context, curNode.expression.name)) { + if (!curNode.parent) { + return; + } + curNode = curNode.parent; + } + // If the parent component of the current component is not within the valid range, an error is reported + const parentComponentName = curNode.expression.name; + if (!this.context.componentsInfo.validParentComponent.get(componentName)!.includes(parentComponentName)) { + const parentComponentListArray: string[] = this.context.componentsInfo.validParentComponent.get(componentName)!; + this.report({ + node: node, + message: this.messages.delegateParentComponent, + data: { + componentName: componentName, + parentComponentList: listToString(parentComponentListArray), + }, + }); + } } - const parentName: string = curNode.expression.dumpSrc(); - if (parentName === 'List') { // If the parent component's name is 'List', exit directly - break; - } else if (parentName !== 'ForEach') { // If the parent component's name is not 'List' or 'ForEach', throw an error - context.report({ - node: node, - message: rule.messages.listItemCannotInOther, - data: { parentName: parentName } - }); - context.report({ - node: node, - message: rule.messages.listItemMustInList - }); - break; + + private checkValidChildComponent(node: arkts.AstNode): void { + // Check whether the node is an identifier and whether there are restrictions on subcomponents + if (!arkts.isIdentifier(node) || !this.context.componentsInfo) { + return; + } + const componentName: string = getIdentifierName(node); + if (!this.context.componentsInfo.validChildComponent.has(componentName) || !node.parent || + !arkts.isCallExpression(node.parent) || !arkts.isIdentifier(node.parent.expression)) { + return; + } + let parentNode = node.parent; + const childComponentListArray = this.context.componentsInfo.validChildComponent.get(componentName); + if (!childComponentListArray) { + return; + } + let reportFlag: boolean = false; + // If the BlockStatement contains a child component that should not exist under the component, an error will be reported + parentNode.getChildren().forEach(member => { + if (!arkts.isBlockStatement(member)) { + return; + } + member.statements.forEach(statement => { + if (!arkts.isExpressionStatement(statement) || !statement.expression || + !arkts.isCallExpression(statement.expression) || !statement.expression.expression || + !arkts.isIdentifier(statement.expression.expression)) { + return; + } + const childComponentNode = statement.expression.expression; + const childComponentName = getIdentifierName(childComponentNode); + if (childComponentListArray.includes(childComponentName) || + !isBuildInComponent(this.context, childComponentName)) { + return; + } + reportFlag = true; + this.reportDelegateChildrenComponentChildren(childComponentNode, childComponentName, componentName); + }); + }); + if (!reportFlag) { + return; + } + this.report({ + node: node, + message: this.messages.delegateChildrenComponentParent, + data: { + parentComponentName: componentName, + childComponentList: listToString(childComponentListArray), + }, + }); } - // In the remaining case, the parent component is 'ForEach', continue traversing upwards for further checks - if (!curNode.parent) { - return; + + private reportDelegateChildrenComponentChildren( + parentNode: arkts.Identifier, + childComponentName: string, + componentName: string + ): void { + this.report({ + node: parentNode, + message: this.messages.delegateChildrenComponentChildren, + data: { + childComponentName: childComponentName, + componentName: componentName, + }, + }); } - curNode = curNode.parent; - } while (true); -} -function checkSpan(node: arkts.AstNode, context: UISyntaxRuleContext): void { - // Check if the current node is an identifier, and name is 'Span' - if (!arkts.isIdentifier(node)) { - return; - } - if (getIdentifierName(node) !== 'Span') { - return; - } - let parentNode = node.parent; - if (!arkts.isCallExpression(parentNode)) { - return; - } - // If there are subcomponents in the BlockStatement, an error is reported - parentNode.getChildren().forEach(sibling => { - if (!arkts.isBlockStatement(sibling)) { - return; + private checkSingleChildComponent(node: arkts.AstNode): void { + // Check whether the current node is an identifier and a single subcomponent container + if (!arkts.isIdentifier(node)) { + return; + } + const componentName: string = getIdentifierName(node); + if (!isSingleChildComponent(this.context, componentName) || !node.parent) { + return; + } + const parentNode = node.parent; + if (!arkts.isCallExpression(parentNode)) { + return; + } + // If there is more than one subcomponent in the BlockStatement, an error is reported + parentNode.getChildren().forEach(member => { + if (!arkts.isBlockStatement(member)) { + return; + } + if (member.statements.length > SINGLE_CHILD_COMPONENT) { + this.report({ + node: node, + message: this.messages.singleChildComponent, + data: { componentName: componentName } + }); + } + }); } - if (sibling.statements.length > 0) { - context.report({ - node: node, - message: rule.messages.noChildInSpan - }); + + private checkNoChildComponent(node: arkts.AstNode): void { + // Check whether the current node is an identifier and an atomic component + if (!arkts.isIdentifier(node)) { + return; + } + const componentName: string = getIdentifierName(node); + if (!isAtomicComponent(this.context, componentName) || !node.parent) { + return; + } + let parentNode = node.parent; + if (!arkts.isCallExpression(parentNode)) { + return; + } + // If there are child components in arguments, an error will be reported + parentNode.getChildren().forEach(member => { + if (!arkts.isBlockStatement(member)) { + return; + } + if (member.statements.length > 0) { + this.report({ + node: node, + message: this.messages.noChildComponent, + data: { componentName: componentName } + }); + } + }); } - }); - if (!node.parent || !node.parent.parent) { - return; - } - parentNode = parentNode.parent; - while (!arkts.isCallExpression(parentNode)) { - parentNode = parentNode.parent; - } - const parentName: string = parentNode.expression.dumpSrc(); - // If the name of the parent component is not 'Text', an error is reported - if (parentName !== 'Text' && parentName !== 'RichEditor' && parentName !== 'ContainerSpan') { - context.report({ - node: node, - message: rule.messages.spanMustInText - }); - } } -// The 'Text' component can have only the Span, ImageSpan, ContainerSpan and SymbolSpan child component. - -const rule: UISyntaxRule = { - name: 'nested-relationship', - messages: { - invalidChildInText: `The 'Text' component can have only the Span, ImageSpan, ContainerSpan and SymbolSpan child component.`, - oneChildInButton: `The 'Button' component can have only one child component.`, - listItemMustInList: `The 'ListItem' component can only be nested in the List and ListItemGroup parent component.`, - listItemCannotInOther: `The 'ListItem' component cannot be a child component of the '{{parentName}}' component.`, - noChildInSpan: `No child component is allowed in the 'Span' component. `, - spanMustInText: `The 'Span' component can only be nested in the Text, RichEditor and ContainerSpan parent component. `, - }, - setup(context) { - const textChild: string[] = ['Span', 'ImageSpan', 'ContainerSpan', 'SymbolSpan']; - return { - parsed: (node): void => { - checkInvalidChildInText(node, context, textChild); - checkOneChildInButton(node, context); - checkListItem(node, context); - checkSpan(node, context); - }, - }; - }, -}; -export default rule; \ No newline at end of file +export default NestedRelationshipRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/nested-reuse-component-check.ts b/arkui-plugins/ui-syntax-plugins/rules/nested-reuse-component-check.ts new file mode 100644 index 000000000..c1206b1ab --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/nested-reuse-component-check.ts @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { COMPONENT_REPEAT, getIdentifierName, PresetDecorators, TEMPLATE } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class NestedReuseComponentCheckRule extends AbstractUISyntaxRule { + private reusableV2StructName: string[] = []; + private reusableStructName: string[] = []; + + public setup(): Record { + return { + noReusableV2InComponentV1: `A custom component decorated with @Component cannot contain child components decorated with @ReusableV2.`, + noReusableV2InReusableV1: `A custom component decorated with @Reusable cannot contain child components decorated with @ReusableV2.`, + noReusableV1InReusableV2: `A custom component decorated with @ReusableV2 cannot contain child components decorated with @Reusable.`, + noReusableV2InRepeatTemplate: `The template attribute of the Repeat component cannot contain any custom component decorated with @ReusableV2.`, + }; + } + + public beforeTransform(): void { + this.reusableV2StructName = []; + this.reusableStructName = []; + } + + public parsed(node: arkts.StructDeclaration): void { + this.initStructName(node); + this.checkNestedReuseComponent(node); + this.checkNoReusableV1InReusableV2(node); + } + + private initStructName(node: arkts.AstNode): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + //Go through all the children of Program + for (const childNode of node.getChildren()) { + // Check whether the type is struct + if (!arkts.isStructDeclaration(childNode)) { + continue; + } + // Get a list of annotations + const annotationsList = childNode.definition.annotations; + // Check that the current component has @Reusable and @ReusableV2 decorators + if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V2)) { + const struceName = childNode.definition?.ident?.name || ''; + this.reusableV2StructName.push(struceName); + } else if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V1)) { + const struceName = childNode.definition?.ident?.name || ''; + this.reusableStructName.push(struceName); + } + } + } + + private reportNoReusableV2InRepeatTemplate(errorNode: arkts.AstNode): void { + this.report({ + node: errorNode, + message: this.messages.noReusableV2InRepeatTemplate, + fix: (errorNode) => { + return { + title: 'Remove the component', + range: [errorNode.startPosition, errorNode.endPosition], + code: '', + }; + } + }); + } + + private checkHasRepeatOrTemplate(node: arkts.CallExpression): { hasRepeat: boolean, hasTemplate: boolean } { + let hasRepeat: boolean = false; + let hasTemplate: boolean = false; + while (arkts.isCallExpression(node) && + node.expression && arkts.isMemberExpression(node.expression) && + node.expression.object && arkts.isCallExpression(node.expression.object)) { + if (arkts.isIdentifier(node.expression.property) && getIdentifierName(node.expression.property) === TEMPLATE) { + hasTemplate = true; + } + node = node.expression.object; + } + if (arkts.isCallExpression(node) && arkts.isIdentifier(node.expression)) { + hasRepeat = getIdentifierName(node.expression) === COMPONENT_REPEAT; + } + return { hasRepeat, hasTemplate }; + } + + private checkNoReusableV2InRepeatTemplate(node: arkts.AstNode, errorNode: arkts.AstNode): boolean { + if (!arkts.isCallExpression(node)) { + return false; + } + const flag = this.checkHasRepeatOrTemplate(node); + if (!flag.hasRepeat || !flag.hasTemplate) { + return false; + } + this.reportNoReusableV2InRepeatTemplate(errorNode); + return true; + } + + private reportNoReusableV1InReusableV2(node: arkts.AstNode): void { + this.report({ + node: node, + message: this.messages.noReusableV1InReusableV2, + fix: (node) => { + return { + title: 'Remove the component', + range: [node.startPosition, node.endPosition], + code: '', + }; + } + }); + } + + private checkNoReusableV1InReusableV2(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; + } + if (this.reusableStructName.includes(node.expression.name)) { + // Traverse upwards to find the custom component. + let struceNode: arkts.AstNode = node; + while (!arkts.isStructDeclaration(struceNode)) { + if (!struceNode.parent) { + return; + } + struceNode = struceNode.parent; + } + const annotationsList = struceNode.definition.annotations; + // Check that the current component is decorated by the @ComponentV2 decorator + if (annotationsList.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V2)) { + this.reportNoReusableV1InReusableV2(node); + } + } + } + + private reportNoReusableV2InReusableV1(node: arkts.AstNode): void { + this.report({ + node: node, + message: this.messages.noReusableV2InReusableV1, + fix: (node) => { + return { + title: 'Remove the component', + range: [node.startPosition, node.endPosition], + code: '', + }; + } + }); + } + + private reportNoReusableV2InComponentV1(node: arkts.AstNode): void { + this.report({ + node: node, + message: this.messages.noReusableV2InComponentV1, + fix: (node) => { + return { + title: 'Remove the component', + range: [node.startPosition, node.endPosition], + code: '', + }; + } + }); + } + + private checkNestedReuseComponent(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; + } + if (this.reusableV2StructName.includes(node.expression.name)) { + // Traverse upwards to find the custom component. + let struceNode: arkts.AstNode = node; + let hasReportedError = false; + while (!arkts.isStructDeclaration(struceNode)) { + if (!struceNode.parent) { + return; + } + struceNode = struceNode.parent; + if (!hasReportedError) { + hasReportedError = this.checkNoReusableV2InRepeatTemplate(struceNode, node); + } + } + // Gets a list of decorators for the current Struct + const annotationsList = struceNode.definition.annotations; + if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V1)) { + this.reportNoReusableV2InReusableV1(node); + } else if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.COMPONENT_V1)) { + this.reportNoReusableV2InComponentV1(node); + } + } + } +} + +export default NestedReuseComponentCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts b/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts index 6cf999a27..74028790c 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts @@ -14,76 +14,86 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; import { getIdentifierName } from '../utils'; -function isInsideStructAndBuild(node: arkts.AstNode): boolean { - let parentNode = node.parent; - let isInStruct = false; - let isInBuild = false; - while (arkts.nodeType(parentNode) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - if (arkts.isStructDeclaration(parentNode)) { - isInStruct = true; - } - if (arkts.isScriptFunction(parentNode) && parentNode.id?.name === 'build') { - isInBuild = true; - } - parentNode = parentNode.parent; - } - return isInStruct && isInBuild; -} -function reportNoChildInButtonError(parentNode: arkts.AstNode, context: UISyntaxRuleContext): void { - const siblings = parentNode.getChildren(); - if (!Array.isArray(siblings) || siblings.length < 3) { - return; - } - if (arkts.isStringLiteral(siblings[1]) && arkts.isBlockStatement(siblings[2])) { - context.report({ - node: parentNode, - message: rule.messages.noChildInButton, - fix: (parentNode) => { - const startPosition = arkts.getStartPosition(siblings[2]); - const endPosition = arkts.getEndPosition(siblings[2]); +class NoChildInButtonRule extends AbstractUISyntaxRule { + public setup(): Record { return { - range: [startPosition, endPosition], - code: '', + noChildInButton: `The Button component with a label parameter can not have any child.`, }; - }, - }); - } -} + } -const rule: UISyntaxRule = { - name: 'no-child-in-button', - messages: { - noChildInButton: `The Button component with a label parameter can not have any child.`, - }, - setup(context: UISyntaxRuleContext) { - return { - parsed: (node): void => { + public parsed(node: arkts.AstNode): void { // Check if the current node is an identifier if (!arkts.isIdentifier(node)) { - return; + return; } const componentName = getIdentifierName(node); // If the current node is 'Button' if (componentName !== 'Button') { - return; + return; + } + if (!this.isInsideStructAndBuild(node)) { + return; } - if (!isInsideStructAndBuild(node)) { - return; + if (!node.parent) { + return; } // Obtain the information of the parent node of the current node let parentNode = node.parent; if (!arkts.isCallExpression(parentNode)) { - return; + return; }; // Gets and traverses all the children of the parent node - reportNoChildInButtonError(parentNode, context); - } - }; - }, + this.reportNoChildInButtonError(parentNode); + } + + private isInsideStructAndBuild(node: arkts.AstNode): boolean { + if (!node.parent) { + return false; + } + let parentNode = node.parent; + let isInStruct = false; + let isInBuild = false; + while (arkts.nodeType(parentNode) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + if (arkts.isStructDeclaration(parentNode)) { + isInStruct = true; + } + if (arkts.isScriptFunction(parentNode) && parentNode.id?.name === 'build') { + isInBuild = true; + } + if (!parentNode.parent) { + return false; + } + parentNode = parentNode.parent; + } + return isInStruct && isInBuild; + } + + private reportNoChildInButtonError(parentNode: arkts.AstNode): void { + const siblings = parentNode.getChildren(); + if (!Array.isArray(siblings) || siblings.length < 3) { + return; + } + if (arkts.isStringLiteral(siblings[1]) && arkts.isBlockStatement(siblings[2])) { + this.report({ + node: parentNode, + message: this.messages.noChildInButton, + fix: () => { + const startPosition = siblings[2].startPosition; + const endPosition = siblings[2].endPosition; + return { + title: 'Remove child components', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + } + }; -export default rule; \ No newline at end of file +export default NoChildInButtonRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-decorators.ts b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-decorators.ts deleted file mode 100644 index ff42b2180..000000000 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-decorators.ts +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2025 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as arkts from '@koalaui/libarkts'; -import { getAnnotationName, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -// Decorators that cannot be repeated -const validDecorators = [ - PresetDecorators.ENTRY, - PresetDecorators.COMPONENT_V1, - PresetDecorators.COMPONENT_V2, - PresetDecorators.REUSABLE_V1, - PresetDecorators.PREVIEW, - PresetDecorators.REUSABLE_V2, - PresetDecorators.CUSTOM_DIALOG, -]; - -function checkForDuplicateDecorators(context: UISyntaxRuleContext, node: arkts.StructDeclaration): void { - // Initialize a map to record decorators and their occurrences - const decoratorCounts: Map = new Map(); - if (!node.definition || !node.definition.annotations) { - return; - } - // Record all decorators and their counts - node.definition.annotations.forEach((annotation) => { - const decoratorName = getAnnotationName(annotation); - if (!validDecorators.includes(decoratorName)) { - return; - } - - if (decoratorCounts.has(decoratorName)) { - const decoratorInfo = decoratorCounts.get(decoratorName)!; - decoratorInfo.count += 1; - decoratorInfo.annotations.push(annotation); - } else { - decoratorCounts.set(decoratorName, { count: 1, annotations: [annotation] }); - } - }); - - // Process decorators with more than one occurrence - decoratorCounts.forEach(({ count, annotations }, decoratorName) => { - if (count <= 1) { - return; - } - // Report errors for all occurrences except the last one - for (let i = 0; i < annotations.length - 1; i++) { - const prevAnnotation = annotations[i]; - reportDuplicateDecorator(context, prevAnnotation); - } - // For the last occurrence, report an error but do not provide a fix - const lastAnnotation = annotations[annotations.length - 1]; - context.report({ - node: lastAnnotation, - message: rule.messages.duplicateDecorator, - }); - }); -} - -function reportDuplicateDecorator(context: UISyntaxRuleContext, annotation: arkts.AnnotationUsage): void { - context.report({ - node: annotation, - message: rule.messages.duplicateDecorator, - fix: () => { - const startPosition = arkts.getStartPosition(annotation); - const endPosition = arkts.getEndPosition(annotation); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); -} - -const rule: UISyntaxRule = { - name: 'no-duplicate-decorators', - messages: { - duplicateDecorator: `Duplicate decorators for struct are not allowed.`, - }, - setup(context) { - return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; - } - checkForDuplicateDecorators(context, node); - }, - }; - }, -}; - -export default rule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts index 60c810180..51043718d 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts @@ -14,59 +14,74 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getAnnotationUsage, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getAnnotationUsage, MAX_ENTRY_DECORATOR_COUNT, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const MAX_ENTRY_DECORATOR_COUNT = 1; -function checkDuplicateEntry(node: arkts.AstNode, context: UISyntaxRuleContext): void { - if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - return; - } - let entryDecoratorUsages: arkts.AnnotationUsage[] = []; - node.getChildren().forEach((child) => { - if (!arkts.isStructDeclaration(child)) { - return; - } - const entryDecoratorUsage = getAnnotationUsage( - child, - PresetDecorators.ENTRY, - ); - if (entryDecoratorUsage) { - entryDecoratorUsages.push(entryDecoratorUsage); - } - }); - // If more than one entry decorator is recorded, an error is reported - if (entryDecoratorUsages.length <= MAX_ENTRY_DECORATOR_COUNT) { - return; - } - entryDecoratorUsages.forEach((entryDocoratorUsage) => { - context.report({ - node: entryDocoratorUsage, - message: rule.messages.duplicateEntry, - fix: (entryDocoratorUsage) => { - const startPosition = arkts.getStartPosition(entryDocoratorUsage); - const endPosition = arkts.getEndPosition(entryDocoratorUsage); +class NoDuplicateEntryRule extends AbstractUISyntaxRule { + private entryDecoratorUsages: arkts.AnnotationUsage[] = []; + private entryDecoratorUsageIndex = 1; + + public setup(): Record { return { - range: [startPosition, endPosition], - code: '', + duplicateEntry: `A page can't contain more then one '@Entry' annotation.`, }; - } - }); - }); -} + } -const rule: UISyntaxRule = { - name: 'no-duplicate-entry', - messages: { - duplicateEntry: `An ArkTS file can contain only one '@Entry' decorator.`, - }, - setup(context) { - return { - parsed: (node): void => { - checkDuplicateEntry(node, context); - }, - }; - }, -}; + public beforeTransform(): void { + this.entryDecoratorUsages = []; + this.entryDecoratorUsageIndex = 1; + } + + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + let entryDecoratorUsage = getAnnotationUsage(node, PresetDecorators.ENTRY); + if (entryDecoratorUsage) { + this.entryDecoratorUsages.push(entryDecoratorUsage); + } + // If more than one entry decorator is recorded, an error is reported + if (this.entryDecoratorUsages.length <= MAX_ENTRY_DECORATOR_COUNT) { + return; + } + if (this.entryDecoratorUsageIndex === MAX_ENTRY_DECORATOR_COUNT) { + const entryDecoratorUsage = this.entryDecoratorUsages.at(0)!; + if (!entryDecoratorUsage) { + return; + } + let startPosition = entryDecoratorUsage.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + this.report({ + node: entryDecoratorUsage, + message: this.messages.duplicateEntry, + fix: () => { + return { + title: 'Remove the duplicate \'Entry\' annotation', + range: [startPosition, entryDecoratorUsage.endPosition], + code: '', + }; + }, + }); + } + entryDecoratorUsage = this.entryDecoratorUsages.at(this.entryDecoratorUsageIndex)!; + if (!entryDecoratorUsage) { + return; + } + let startPosition = entryDecoratorUsage.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + this.report({ + node: entryDecoratorUsage, + message: this.messages.duplicateEntry, + fix: () => { + return { + title: 'Remove the duplicate \'Entry\' annotation', + range: [startPosition, entryDecoratorUsage.endPosition], + code: '', + }; + }, + }); + this.entryDecoratorUsageIndex++; + } +} -export default rule; \ No newline at end of file +export default NoDuplicateEntryRule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-id.ts b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-id.ts new file mode 100644 index 000000000..32f2b369d --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-id.ts @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { getCurrentFilePath } from '../utils'; + +interface IdInfo { + value: string; + node: arkts.AstNode; +} + +const ID_NAME: string = 'id'; + +class NoDuplicateIdRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + duplicateId: `The current component id "{{id}}" is duplicate with {{path}}:{{line}}:{{index}}.`, + }; + } + + public parsed(node: arkts.AstNode): void { + const usedIds = new Map(); + if (arkts.isBlockStatement(node)) { + this.findAndValidateIds(node, usedIds); + } + } + + private findAndValidateIds( + node: arkts.BlockStatement, + usedIds: Map + ): void { + node.statements.forEach((statement) => { + if ( + arkts.isExpressionStatement(statement) && + arkts.isCallExpression(statement.expression) + ) { + this.findDuplicateComponentIds(statement.expression, usedIds); + } + }); + } + + private findDuplicateComponentIds(node: arkts.CallExpression, usedIds: Map): void { + const idInfo = this.getIdInfo(node); + if (!idInfo) { + return; + } + if (usedIds.has(idInfo.value)) { + this.report({ + node: idInfo.node, + message: this.messages.duplicateId, + data: { + id: idInfo.value, + path: getCurrentFilePath(node) ?? '', + line: idInfo.node.startPosition.line().toString(), + index: idInfo.node.startPosition.index().toString() + } + }); + } else { + // Otherwise, record it + usedIds.set(idInfo.value, idInfo); + } + } + + private getIdInfo(node: arkts.CallExpression): IdInfo | undefined { + const callee = node.expression; + + if (!arkts.isMemberExpression(callee)) { + return undefined; + } + const property = callee.property; + if (!arkts.isIdentifier(property) || property.name !== ID_NAME) { + return undefined; + } + + const strArg = node.arguments.find(arkts.isStringLiteral); + const value = strArg?.str; + if (!value) { + return undefined; + } + return { value, node: property }; + } +} + +export default NoDuplicateIdRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts index fe57d8f11..35d1b31e5 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts @@ -14,60 +14,68 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getAnnotationUsage, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getAnnotationUsage, MAX_PREVIEW_DECORATOR_COUNT, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const MAX_PREVIEW_DECORATOR_COUNT = 10; +class NoDuplicatePreviewRule extends AbstractUISyntaxRule { + private previewDecoratorUsages: arkts.AnnotationUsage[] = []; + private previewDecoratorUsageIndex = 10; -function checkDuplicatePreview(node: arkts.AstNode, context: UISyntaxRuleContext): void { - if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - return; - } - let previewDecoratorUsages: arkts.AnnotationUsage[] = []; - node.getChildren().forEach((child) => { - if (!arkts.isStructDeclaration(child)) { - return; - } - const previewDecoratorUsage = getAnnotationUsage( - child, - PresetDecorators.PREVIEW, - ); - if (previewDecoratorUsage) { - previewDecoratorUsages.push(previewDecoratorUsage); - } - }); - // If the number of preview decorators is greater than 10, an error is reported - if (previewDecoratorUsages.length <= MAX_PREVIEW_DECORATOR_COUNT) { - return; - } - previewDecoratorUsages.forEach((previewDecoratorUsage) => { - context.report({ - node: previewDecoratorUsage, - message: rule.messages.duplicateEntry, - fix: (previewDecoratorUsage) => { - const startPosition = arkts.getStartPosition(previewDecoratorUsage); - const endPosition = arkts.getEndPosition(previewDecoratorUsage); + public setup(): Record { return { - range: [startPosition, endPosition], - code: '', + duplicateEntry: `A page can contain at most 10 '@Preview' annotations.`, }; - } - }); - }); -} + } + + public beforeTransform(): void { + this.previewDecoratorUsages = []; + this.previewDecoratorUsageIndex = 10; + } + + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + const previewDecoratorUsage = getAnnotationUsage( + node, + PresetDecorators.PREVIEW, + ); + if (previewDecoratorUsage) { + this.previewDecoratorUsages.push(previewDecoratorUsage); + } + // If the number of preview decorators is less than 10, no error is reported + if (this.previewDecoratorUsages.length <= MAX_PREVIEW_DECORATOR_COUNT) { + return; + } + if (this.previewDecoratorUsageIndex === MAX_PREVIEW_DECORATOR_COUNT) { + this.previewDecoratorUsages.forEach((previewDecoratorUsage) => { + this.reportError(previewDecoratorUsage); + }); + } else { + let previewDecoratorUsage = this.previewDecoratorUsages.at(this.previewDecoratorUsageIndex); + if (!previewDecoratorUsage) { + return; + } + this.reportError(previewDecoratorUsage); + } + this.previewDecoratorUsageIndex++; + } -const rule: UISyntaxRule = { - name: 'no-duplicate-preview', - messages: { - duplicateEntry: `An ArkTS file con contain at most 10 '@Preview' decorators.`, - }, - setup(context) { - return { - parsed: (node): void => { - checkDuplicatePreview(node, context); - }, - }; - }, -}; + private reportError(errorNode: arkts.AnnotationUsage): void { + let startPosition = errorNode.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + this.report({ + node: errorNode, + message: this.messages.duplicateEntry, + fix: () => { + return { + title: 'Remove the duplicate \'Preview\' annotation', + range: [startPosition, errorNode.endPosition], + code: '', + }; + } + }); + } +} -export default rule; \ No newline at end of file +export default NoDuplicatePreviewRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-state-manager.ts b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-state-manager.ts deleted file mode 100644 index df1468fd5..000000000 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-state-manager.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2025 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as arkts from '@koalaui/libarkts'; -import { getClassPropertyAnnotationNames } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -export const stateManagementDecorator = { - STATE: 'State', - PROP: 'Prop', - LINK: 'Link', - PROVIDE: 'Provide', - CONSUME: 'Consume' -}; - -const CLASS_PROPERTY_ANNOTATION_ONE: number = 1; -function duplicateState( - node: arkts.StructDeclaration, - context: UISyntaxRuleContext -): void { - node.definition.body.forEach(body => { - if (arkts.isClassProperty(body)) { - // Get the list of decorators - const propertyDecorators = getClassPropertyAnnotationNames(body); - // Filter the decorators to get those related to state management - const stateDecorators = propertyDecorators.filter(decorator => - Object.values(stateManagementDecorator).includes(decorator) - ); - const propertyNameNode = body.key; - const attributeName = body.key?.dumpSrc(); - if (!propertyNameNode || !attributeName) { - return; - } - if (stateDecorators.length > CLASS_PROPERTY_ANNOTATION_ONE) { - context.report({ - node: propertyNameNode, - message: rule.messages.duplicateState, - data: { attributeName }, - }); - } - } - }); -} - -const rule: UISyntaxRule = { - name: 'no-duplicate-state-manager', - messages: { - duplicateState: `This attribute '{{attributeName}}' cannot have mutilate state management decorators. `, - }, - setup(context) { - return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; - } - duplicateState(node, context); - }, - }; - }, -}; - -export default rule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-prop-link-objectlink-in-entry.ts b/arkui-plugins/ui-syntax-plugins/rules/no-prop-link-objectlink-in-entry.ts index 8815feff4..7c90f0c9d 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-prop-link-objectlink-in-entry.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-prop-link-objectlink-in-entry.ts @@ -15,61 +15,74 @@ import * as arkts from '@koalaui/libarkts'; import { getAnnotationUsage, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function checkNoPropLinkOrObjectLinkInEntry(context: UISyntaxRuleContext, node: arkts.StructDeclaration): void { - // Check if the struct has the @Entry decorator - const isEntryComponent = !!getAnnotationUsage(node, PresetDecorators.ENTRY); - if (!isEntryComponent) { - return; - } - node.definition.body.forEach(body => { - if (!arkts.isClassProperty(body)) { - return; - } - const invalidDecorators = [PresetDecorators.PROP, PresetDecorators.LINK, PresetDecorators.OBJECT_LINK]; - // Check if any invalid decorators are applied to the class property - body.annotations?.forEach(annotation => { - reportInvalidDecorator(context, annotation, invalidDecorators); - }); - }); -} +const invalidDecorators = [PresetDecorators.PROP_REF, PresetDecorators.LINK, PresetDecorators.OBJECT_LINK]; -function reportInvalidDecorator(context: UISyntaxRuleContext, annotation: arkts.AnnotationUsage, - invalidDecorators: string[],): void { - if (annotation.expr && invalidDecorators.includes(annotation.expr.dumpSrc())) { - const decorator = annotation.expr.dumpSrc(); - context.report({ - node: annotation, - message: rule.messages.invalidUsage, - data: { decorator }, - fix: (annotation) => { - const startPosition = arkts.getStartPosition(annotation); - const endPosition = arkts.getEndPosition(annotation); +class NoPropLinkObjectLinkInEntryRule extends AbstractUISyntaxRule { + public setup(): Record { return { - range: [startPosition, endPosition], - code: '', + disallowDecoratorInEntry: `The '@Entry' component '{{componentName}}' cannot have the '@{{decoratorName}}' property '{{propertyName}}'.`, }; - }, - }); - } -} - -const rule: UISyntaxRule = { - name: 'no-prop-link-objectlink-in-entry', - messages: { - invalidUsage: `@{{decorator}} decorator cannot be used for '@Entry' decorated components.`, - }, - setup(context) { - return { - parsed: (node): void => { + } + public parsed(node: arkts.AstNode): void { if (!arkts.isStructDeclaration(node)) { - return; + return; + } + this.checkNoPropLinkOrObjectLinkInEntry(node); + } + + private checkNoPropLinkOrObjectLinkInEntry(node: arkts.StructDeclaration): void { + // Check if the struct has the @Entry decorator + const isEntryComponent = !!getAnnotationUsage(node, PresetDecorators.ENTRY); + if (!node.definition.ident || !arkts.isIdentifier(node.definition.ident)) { + return; + } + const componentName = node.definition.ident.name; + if (!isEntryComponent) { + return; } - checkNoPropLinkOrObjectLinkInEntry(context, node); - }, - }; - }, -}; + node.definition.body.forEach(body => { + if (!arkts.isClassProperty(body)) { + return; + } + if (!body.key || !arkts.isIdentifier(body.key)) { + return; + } + const propertyName = body.key.name; + // Check if any invalid decorators are applied to the class property + body.annotations?.forEach(annotation => { + this.reportInvalidDecorator(annotation, invalidDecorators, componentName, propertyName); + }); + }); + } + + private reportInvalidDecorator(annotation: arkts.AnnotationUsage, + invalidDecorators: string[], componentName: string, propertyName: string): void { + if (annotation.expr && arkts.isIdentifier(annotation.expr) && + invalidDecorators.includes(annotation.expr.name)) { + const decoratorName = annotation.expr.name; + this.report({ + node: annotation, + message: this.messages.disallowDecoratorInEntry, + data: { + componentName, + decoratorName, + propertyName, + }, + fix: (annotation) => { + let startPosition = annotation.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = annotation.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + } +} -export default rule; +export default NoPropLinkObjectLinkInEntryRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts b/arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts index e61af0b56..5940191f6 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts @@ -14,34 +14,38 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { isBuiltInAttribute } from '../utils'; -const rule: UISyntaxRule = { - name: 'no-same-as-built-in-attribute', - messages: { - duplicateName: `The struct name '{{structName}}' should not have the same name as a built-in attribute.`, - }, - setup(context) { - const builtInAttributes = ['fontColor', 'width', 'height', 'size', 'border', 'backgroundColor', 'margin', - 'padding']; +class NoSameAsBuiltInAttributeRule extends AbstractUISyntaxRule { + public setup(): Record { return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; - } - const structName = node.definition.ident?.name ?? ' '; - const structIdent = node.definition.ident; - // If the struct name matches any built-in attribute, report an error - if (builtInAttributes.includes(structName) && structIdent) { - context.report({ - node: structIdent, - message: rule.messages.duplicateName, - data: { structName } - }); - } - }, + duplicateName: `The struct '{{structName}}' cannot have the same name as the built-in attribute '{{builtInName}}'.`, }; - }, + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + if (!node.definition) { + return; + } + if (!arkts.isClassDefinition(node.definition)) { + return; + } + const structIdent = node.definition.ident; + const structName = node.definition.ident?.name ?? ' '; + // If the struct name matches any built-in attribute, report an error + if (structIdent && isBuiltInAttribute(this.context, structName)) { + const builtInName = structName; + this.report({ + node: structIdent, + message: this.messages.duplicateName, + data: { structName, builtInName } + }); + } + } }; -export default rule; +export default NoSameAsBuiltInAttributeRule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/observed-heritage-compatible-check.ts b/arkui-plugins/ui-syntax-plugins/rules/observed-heritage-compatible-check.ts index 95b605f2c..b18d51a2f 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/observed-heritage-compatible-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/observed-heritage-compatible-check.ts @@ -14,93 +14,113 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; -import { PresetDecorators } from '../utils/index'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { getIdentifierName, PresetDecorators } from '../utils/index'; -function getObservedDecorator(node: arkts.AstNode, observedClasses: string[], observedV2Classes: string[]): void { - for (const child of node.getChildren()) { - // Check if it is of the ClassDeclaration type - if (arkts.isClassDeclaration(child)) { - // Get a list of annotations - const annotations = child.definition?.annotations ?? []; - // Check for @Observed decorators - const hasObservedDecorator = annotations.find((annotation: any) => - annotation.expr.name === PresetDecorators.OBSERVED_V1); - // Check if there is a @ObservedV2 decorator - const hasObservedV2Decorator = annotations.find((annotation: any) => - annotation.expr.name === PresetDecorators.OBSERVED_V2); - // If there is a @Observed decorator, record the class name - if (hasObservedDecorator) { - const className = child.definition?.ident?.name ?? ''; - observedClasses.push(className); - } - // If there is a @ObservedV2 decorator, record the class name - if (hasObservedV2Decorator) { - const className = child.definition?.ident?.name ?? ''; - observedV2Classes.push(className); - } - } - } -} +class ObservedHeritageCompatibleCheckRule extends AbstractUISyntaxRule { + // Record the class name decorated with @Observed and @ObservedV2 + private observedClasses: string[] = []; + private observedV2Classes: string[] = []; -function checkInheritanceCompatibility(context: UISyntaxRuleContext, node: arkts.ClassDeclaration, - observedClasses: string[], observedV2Classes: string[]): void { - const observedV1Decorator = node.definition?.annotations?.find(annotations => - annotations.expr && - arkts.isIdentifier(annotations.expr) && - annotations.expr.name === PresetDecorators.OBSERVED_V1 - ); - const observedV2Decorator = node.definition?.annotations?.find(annotations => - annotations.expr && - arkts.isIdentifier(annotations.expr) && - annotations.expr.name === PresetDecorators.OBSERVED_V2 - ); - - //Get the name of the superClass - const superClassName = node.definition?.super?.dumpSrc(); - if (!superClassName) { - return; + public setup(): Record { + return { + incompatibleHeritageObservedToObservedV2: `A class decorated by '@Observed' cannot inherit from a class decorated by '@ObservedV2'.`, + incompatibleHeritageObservedV2ToObserved: `A class decorated by '@ObservedV2' cannot inherit from a class decorated by '@Observed'.`, + }; } - // Verify that the inheritance relationship is compatible - if (observedV1Decorator && observedV2Classes.includes(superClassName)) { - context.report({ - node: observedV1Decorator, - message: rule.messages.incompatibleHeritageObservedToObservedV2, - }); + + public beforeTransform(): void { + this.observedClasses = []; + this.observedV2Classes = []; } - if (observedV2Decorator && observedClasses.includes(superClassName)) { - context.report({ - node: observedV2Decorator, - message: rule.messages.incompatibleHeritageObservedV2ToObserved, - }); + + public parsed(node: arkts.AstNode): void { + // Check if it's of type "Program". + if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + this.getObservedDecorator(node, this.observedClasses, this.observedV2Classes); + } + // Check if the current node is a class declaration + if (!arkts.isClassDeclaration(node)) { + return; + } + this.checkInheritanceCompatibility(node, this.observedClasses, this.observedV2Classes); } -} -const rule: UISyntaxRule = { - name: 'observed-heritage-compatible-check', - messages: { - incompatibleHeritageObservedToObservedV2: `The current class is decorated by '@Observed', it cannot inherit a class decorated by '@ObservedV2'.`, - incompatibleHeritageObservedV2ToObserved: `The current class is decorated by '@ObservedV2', it cannot inherit a class decorated by '@Observed'.`, - }, - setup(context) { - // Record the class name decorated with @Observed and @ObservedV2 - const observedClasses: string[] = []; - const observedV2Classes: string[] = []; - return { - parsed: (node): void => { - // Check if it's of type "Program". - if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - getObservedDecorator(node, observedClasses, observedV2Classes); + private getObservedDecorator(node: arkts.AstNode, observedClasses: string[], observedV2Classes: string[]): void { + for (const child of node.getChildren()) { + // Check if it is of the ClassDeclaration type + if (arkts.isClassDeclaration(child)) { + // Get a list of annotations + const annotations = child.definition?.annotations ?? []; + // Check for @Observed decorators + const hasObservedDecorator = annotations.find((annotation: any) => + annotation.expr.name === PresetDecorators.OBSERVED_V1); + // Check if there is a @ObservedV2 decorator + const hasObservedV2Decorator = annotations.find((annotation: any) => + annotation.expr.name === PresetDecorators.OBSERVED_V2); + // If there is a @Observed decorator, record the class name + if (hasObservedDecorator) { + const className = child.definition?.ident?.name ?? ''; + observedClasses.push(className); } - - // Check if the current node is a class declaration - if (!arkts.isClassDeclaration(node)) { - return; + // If there is a @ObservedV2 decorator, record the class name + if (hasObservedV2Decorator) { + const className = child.definition?.ident?.name ?? ''; + observedV2Classes.push(className); } - checkInheritanceCompatibility(context, node, observedClasses, observedV2Classes); - }, - }; - }, + } + } + } + + private checkInheritanceCompatibility( + node: arkts.ClassDeclaration, + observedClasses: string[], + observedV2Classes: string[]): void { + const observedV1Decorator = node.definition?.annotations?.find(annotations => + annotations.expr && + arkts.isIdentifier(annotations.expr) && + annotations.expr.name === PresetDecorators.OBSERVED_V1 + ); + const observedV2Decorator = node.definition?.annotations?.find(annotations => + annotations.expr && + arkts.isIdentifier(annotations.expr) && + annotations.expr.name === PresetDecorators.OBSERVED_V2 + ); + + //Get the name of the superClass + if (!node.definition?.super) { + return; + } + if (arkts.isETSTypeReference(node.definition?.super)) { + if (!node.definition.super.part) { + return; + } + if (!arkts.isETSTypeReferencePart(node.definition?.super.part)) { + return; + } + if (!node.definition?.super.part.name) { + return; + } + const superClassName = getIdentifierName(node.definition?.super.part.name); + + if (!superClassName) { + return; + } + // Verify that the inheritance relationship is compatible + if (observedV1Decorator && observedV2Classes.includes(superClassName)) { + this.report({ + node: observedV1Decorator, + message: this.messages.incompatibleHeritageObservedToObservedV2, + }); + } + if (observedV2Decorator && observedClasses.includes(superClassName)) { + this.report({ + node: observedV2Decorator, + message: this.messages.incompatibleHeritageObservedV2ToObserved, + }); + } + } + } }; -export default rule; \ No newline at end of file +export default ObservedHeritageCompatibleCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/observed-observedV2-check.ts b/arkui-plugins/ui-syntax-plugins/rules/observed-observedV2-check.ts index e02304b3c..79d42a4d6 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/observed-observedV2-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/observed-observedV2-check.ts @@ -15,33 +15,33 @@ import * as arkts from '@koalaui/libarkts'; import { PresetDecorators } from '../utils'; -import { UISyntaxRule } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const rule: UISyntaxRule = { - name: 'observed-observedV2-check', - messages: { - conflictingDecorators: `A class cannot be decorated by both '@Observed' and '@ObservedV2' at the same time.`, - }, - setup(context) { +class ObservedObservedV2Rule extends AbstractUISyntaxRule { + public setup(): Record { return { - parsed: (node): void => { - if (!arkts.isClassDeclaration(node)) { - return; - } - const hasObservedDecorator = node.definition?.annotations?.find(annotations => annotations.expr && - annotations.expr.dumpSrc() === PresetDecorators.OBSERVED_V1); - const hasObservedV2Decorator = node.definition?.annotations?.find(annotations => annotations.expr && - annotations.expr.dumpSrc() === PresetDecorators.OBSERVED_V2); - // If the current class is decorated by @Observed and @ObservedV2, an error is reported - if (hasObservedDecorator && hasObservedV2Decorator) { - context.report({ - node: hasObservedDecorator, - message: rule.messages.conflictingDecorators, - }); - } - }, + conflictingDecorators: `A class cannot be decorated by both '@Observed' and '@ObservedV2' at the same time.`, }; - }, + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isClassDeclaration(node)) { + return; + } + const hasObservedDecorator = node.definition?.annotations?.find(annotations => annotations.expr && + arkts.isIdentifier(annotations.expr) && + annotations.expr.name === PresetDecorators.OBSERVED_V1); + const hasObservedV2Decorator = node.definition?.annotations?.find(annotations => annotations.expr && + arkts.isIdentifier(annotations.expr) && + annotations.expr.name === PresetDecorators.OBSERVED_V2); + // If the current class is decorated by @Observed and @ObservedV2, an error is reported + if (hasObservedDecorator && hasObservedV2Decorator) { + this.report({ + node: hasObservedDecorator, + message: this.messages.conflictingDecorators, + }); + } + } }; -export default rule; +export default ObservedObservedV2Rule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts b/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts index 278a9bc73..fce5a72c8 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts @@ -14,141 +14,217 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PresetDecorators, getAnnotationUsage } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { + PresetDecorators, getAnnotationUsage, findDecorator, getClassDeclarationAnnotation +} from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const rule: UISyntaxRule = { - name: 'observedV2-trace-usage-validation', - messages: { - observedV2DecoratorError: `The '@ObservedV2' decorator can only be used in 'class'.`, - traceDecoratorError: `The '@Trace' decorator can only be used in 'class'.`, - traceInObservedV2Error: `The '@Trace' decorator can only be used in a 'class' decorated with '@ObservedV2'.`, - traceMemberVariableError: `The '@Trace' decorator can only decorate member variables within a 'class' decorated with '@ObservedV2'.`, - }, - setup(context) { - return { - parsed: (node): void => { - validateTraceDecoratorUsage(node, context); - }, - }; - }, -}; +class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + observedV2DecoratorError: `The '@ObservedV2' annotation can only be used in 'class'.`, + traceDecoratorError: `The '@Trace' annotation can only be used in 'class'.`, + traceMemberVariableError: `The '@Trace' annotation can only decorate member variables within a 'class' decorated with '@ObservedV2'.`, + //The repair logic is different, if there is v1, update to v2 + traceMustUsedWithObservedV2: `The '@Trace' annotation can only be used within a 'class' decorated with 'ObservedV2'.`, + traceMustUsedWithObservedV2Update: `The '@Trace' annotation can only be used within a 'class' decorated with 'ObservedV2'.`, + }; + } -function reportObservedV2DecoratorError(context: UISyntaxRuleContext, hasObservedV2Decorator: arkts.AnnotationUsage) - : void { - context.report({ - node: hasObservedV2Decorator, - message: rule.messages.observedV2DecoratorError, - fix: (hasObservedV2Decorator) => { - const startPosition = arkts.getStartPosition(hasObservedV2Decorator); - const endPosition = arkts.getEndPosition(hasObservedV2Decorator); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); -} + public parsed(node: arkts.AstNode): void { + this.validateTraceDecoratorUsage(node); + } -function reportTraceMemberVariableError(context: UISyntaxRuleContext, hasTraceDecorator: arkts.AnnotationUsage) - : void { - context.report({ - node: hasTraceDecorator, - message: rule.messages.traceMemberVariableError, - fix: (hasTraceDecorator) => { - const startPosition = arkts.getStartPosition(hasTraceDecorator); - const endPosition = arkts.getEndPosition(hasTraceDecorator); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); -} + private getObservedDecorator(node: arkts.ClassDeclaration): arkts.AnnotationUsage | undefined { + if (!node.definition) { + return undefined; + } + return getClassDeclarationAnnotation(node, PresetDecorators.OBSERVED_V1); + } -function tracePerportyRule( - context: UISyntaxRuleContext, - currentNode: arkts.AstNode, - hasTraceDecorator: arkts.AnnotationUsage -): void { - if (arkts.isStructDeclaration(currentNode)) { - reportTraceDecoratorError(context, hasTraceDecorator); - } else if (arkts.isClassDeclaration(currentNode)) { - // The '@Trace' decorator can only be used in a 'class' decorated with '@ObservedV2' - if ( - !currentNode.definition?.annotations?.some((annotation: arkts.AnnotationUsage) => { - return ( - !!annotation.expr && - arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.OBSERVED_V2 - ); - }) - ) { - reportTraceInObservedV2Error(context, hasTraceDecorator, currentNode); + private reportObservedV2DecoratorError(observedV2Decorator: arkts.AnnotationUsage) + : void { + this.report({ + node: observedV2Decorator, + message: this.messages.observedV2DecoratorError, + fix: (observedV2Decorator) => { + let startPosition = observedV2Decorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = observedV2Decorator.endPosition; + return { + title: 'Remove the @ObservedV2 annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private reportTraceMemberVariableError(traceDecorator: arkts.AnnotationUsage) + : void { + this.report({ + node: traceDecorator, + message: this.messages.traceMemberVariableError, + fix: (traceDecorator) => { + let startPosition = traceDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = traceDecorator.endPosition; + return { + title: 'Remove the @Trace annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private tracePropertyRule( + currentNode: arkts.AstNode, + traceDecorator: arkts.AnnotationUsage): void { + if (arkts.isStructDeclaration(currentNode)) { + this.reportTraceDecoratorError(traceDecorator); + } else if (arkts.isClassDeclaration(currentNode) && currentNode.definition) { + const observedDecorator = this.getObservedDecorator(currentNode); + const observedV2 = currentNode.definition.annotations.some(annotation => + annotation.expr && + arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.OBSERVED_V2 + ); + if (!observedV2 && !observedDecorator) { + this.reportTraceMustUsedWithObservedV2(traceDecorator, currentNode); + } else if (!observedV2 && observedDecorator) { + this.reportTraceMustUsedWithObservedV2Update(traceDecorator, observedDecorator); + } } } -} -function reportTraceDecoratorError(context: UISyntaxRuleContext, hasTraceDecorator: arkts.AnnotationUsage) - : void { - context.report({ - node: hasTraceDecorator, - message: rule.messages.traceDecoratorError, - fix: (hasTraceDecorator) => { - const startPosition = arkts.getStartPosition(hasTraceDecorator); - const endPosition = arkts.getEndPosition(hasTraceDecorator); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); -} + private reportTraceDecoratorError(traceDecorator: arkts.AnnotationUsage) + : void { + this.report({ + node: traceDecorator, + message: this.messages.traceDecoratorError, + fix: (traceDecorator) => { + let startPosition = traceDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = traceDecorator.endPosition; + return { + title: 'Remove the @Trace annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } -function reportTraceInObservedV2Error(context: UISyntaxRuleContext, hasTraceDecorator: arkts.AnnotationUsage, - currentNode: arkts.ClassDeclaration): void { - context.report({ - node: hasTraceDecorator, - message: rule.messages.traceInObservedV2Error, - fix: () => { - const startPosition = arkts.getStartPosition(currentNode); - return { - range: [startPosition, startPosition], - code: `@${PresetDecorators.OBSERVED_V2}\n`, - }; - }, - }); -} + private reportTraceMustUsedWithObservedV2(traceDecorator: arkts.AnnotationUsage, + currentNode: arkts.ClassDeclaration): void { + this.report({ + node: traceDecorator, + message: this.messages.traceMustUsedWithObservedV2, + fix: () => { + const startPosition = currentNode.startPosition; + return { + title: 'Add @ObservedV2 annotation', + range: [startPosition, startPosition], + code: `@${PresetDecorators.OBSERVED_V2}\n`, + }; + }, + }); + } -function validateTraceDecoratorUsage(node: arkts.AstNode, context: UISyntaxRuleContext): void { - let currentNode = node; - if (arkts.isStructDeclaration(node)) { - // Check whether the current custom component is decorated by the @ObservedV2 decorator - const hasObservedV2Decorator = getAnnotationUsage(node, PresetDecorators.OBSERVED_V2); - if (hasObservedV2Decorator) { - reportObservedV2DecoratorError(context, hasObservedV2Decorator); + private reportTraceMustUsedWithObservedV2Update(traceDecorator: arkts.AnnotationUsage, + observedDecorator: arkts.AnnotationUsage): void { + this.report({ + node: traceDecorator, + message: this.messages.traceMustUsedWithObservedV2Update, + fix: () => { + const startPosition = observedDecorator.startPosition; + const endPosition = observedDecorator.endPosition; + return { + title: 'Change @Observed to @ObservedV2', + range: [startPosition, endPosition], + code: `${PresetDecorators.OBSERVED_V2}`, + }; + }, + }); } - } - if (arkts.isClassProperty(node)) { - const hasTraceDecorator = node.annotations?.find(annotation => - annotation.expr && annotation.expr.dumpSrc() === PresetDecorators.TRACE); - if (hasTraceDecorator) { - // Iterate up the parent node to check whether it is a class or a custom component - while (!arkts.isStructDeclaration(currentNode) && !arkts.isClassDeclaration(currentNode)) { - currentNode = currentNode.parent; - } - // The '@Trace' decorator can only be used in 'class' - tracePerportyRule(context, currentNode, hasTraceDecorator); + + private isInClassDeclaration(node: arkts.AstNode): boolean { + while (!arkts.isClassDeclaration(node)) { + if (!node.parent) { + return false; + } + node = node.parent; + } + return true; + } + + private checkAndReportObservedV2Decorator( + node: arkts.FunctionDeclaration | arkts.VariableDeclaration | arkts.ScriptFunction | + arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration | arkts.ClassProperty): void { + const observedV2Decorator = findDecorator(node, PresetDecorators.OBSERVED_V2); + if (observedV2Decorator) { + this.reportObservedV2DecoratorError(observedV2Decorator); + } + } + + private validateTraceDecoratorUsage(node: arkts.AstNode): void { + let currentNode = node; + if (arkts.isStructDeclaration(node)) { + // Check whether the current custom component is decorated by the @ObservedV2 decorator + const observedV2Decorator = getAnnotationUsage(node, PresetDecorators.OBSERVED_V2); + const traceDecorator = getAnnotationUsage(node, PresetDecorators.TRACE); + if (observedV2Decorator) { + this.reportObservedV2DecoratorError(observedV2Decorator); + } + if (traceDecorator) { + this.reportTraceDecoratorError(traceDecorator); + } + } else if ( + arkts.isFunctionDeclaration(node) || + arkts.isVariableDeclaration(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node) + ) { + this.checkAndReportObservedV2Decorator(node); + const traceDecorator = findDecorator(node, PresetDecorators.TRACE); + if (traceDecorator) { + this.reportTraceDecoratorError(traceDecorator); + } + } else if (arkts.isClassProperty(node)) { + this.checkTraceDecoratorUsageInClassProperty(node, currentNode); + this.checkAndReportObservedV2Decorator(node); + } + if (arkts.isMethodDefinition(node) && this.isInClassDeclaration(currentNode)) { + // Check that @Trace is in the correct location + const traceDecorator = findDecorator(node.scriptFunction, PresetDecorators.TRACE); + if (traceDecorator) { + this.reportTraceMemberVariableError(traceDecorator); + } + } else if (arkts.isMethodDefinition(node) && !this.isInClassDeclaration(currentNode)) { + const traceDecorator = findDecorator(node.scriptFunction, PresetDecorators.TRACE); + if (traceDecorator) { + this.reportTraceDecoratorError(traceDecorator); + } + } } - } - if (arkts.isMethodDefinition(node)) { - // Check that @Trace is in the correct location - const hasTraceDecorator = node.scriptFunction.annotations?.find(annotation => - annotation.expr && annotation.expr.dumpSrc() === PresetDecorators.TRACE); - if (hasTraceDecorator) { - reportTraceMemberVariableError(context, hasTraceDecorator); + + private checkTraceDecoratorUsageInClassProperty( + node: arkts.ClassProperty, + currentNode: arkts.AstNode,): void { + const traceDecorator = findDecorator(node, PresetDecorators.TRACE); + if (traceDecorator) { + // Iterate up the parent node to check whether it is a class or a custom component + while (!arkts.isStructDeclaration(currentNode) && !arkts.isClassDeclaration(currentNode)) { + if (!currentNode.parent) { + return; + } + currentNode = currentNode.parent; + } + // The '@Trace' decorator can only be used in 'class' + this.tracePropertyRule(currentNode, traceDecorator); + } } - } -} +}; -export default rule; +export default ObservedV2TraceUsageValidationRule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/old-new-decorator-mix-use-check.ts b/arkui-plugins/ui-syntax-plugins/rules/old-new-decorator-mix-use-check.ts index 356a28098..ce916bb83 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/old-new-decorator-mix-use-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/old-new-decorator-mix-use-check.ts @@ -14,98 +14,123 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PresetDecorators, getAnnotationUsage } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { PresetDecorators, getAnnotationName, getAnnotationUsage, getIdentifierName } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function findPropertyDecorator( - node: arkts.ClassProperty, - decoratorName: string -): arkts.AnnotationUsage | undefined { - const annotation = node.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === decoratorName - ); - return annotation; -} +class OldNewDecoratorMixUseCheckRule extends AbstractUISyntaxRule { + private static readonly oldV1Decorators: string[] = [ + PresetDecorators.STATE, + PresetDecorators.PROP_REF, + PresetDecorators.LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.WATCH, + PresetDecorators.STORAGE_LINK, + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.LOCAL_STORAGE_LINK, + PresetDecorators.OBJECT_LINK, + ]; -function paramDecoratorError( - context: UISyntaxRuleContext, - hasParamDecorator: arkts.AnnotationUsage, - hasComponentDecorator: arkts.AnnotationUsage -): void { - context.report({ - node: hasParamDecorator, - message: rule.messages.paramDecoratorError, - fix: () => { - const startPosition = arkts.getStartPosition(hasComponentDecorator); - const endPosition = arkts.getEndPosition(hasComponentDecorator); - return { - range: [startPosition, endPosition], - code: `@${PresetDecorators.COMPONENT_V2}`, - }; - }, - }); -} + private static readonly newV2decorators: string[] = [ + PresetDecorators.LOCAL, + PresetDecorators.PARAM, + PresetDecorators.ONCE, + PresetDecorators.EVENT, + PresetDecorators.MONITOR, + PresetDecorators.PROVIDER, + PresetDecorators.CONSUMER, + PresetDecorators.COMPUTED, + ]; -function stateDecoratorError( - context: UISyntaxRuleContext, - hasStateDecorator: arkts.AnnotationUsage, - hasComponentV2Decorator: arkts.AnnotationUsage -): void { - context.report({ - node: hasStateDecorator, - message: rule.messages.stateDecoratorError, - fix: () => { - const startPosition = arkts.getStartPosition(hasComponentV2Decorator); - const endPosition = arkts.getEndPosition(hasComponentV2Decorator); - return { - range: [startPosition, endPosition], - code: `@${PresetDecorators.COMPONENT_V1}`, - }; - }, - }); -} + public setup(): Record { + return { + oldAndNewDecoratorsMixUse: `The '@{{decoratorName}}' annotation can only be used in a 'struct' decorated with '@{{component}}'.`, + }; + } + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + // Gets the decorator version of a custom component + const componentV2Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); + const componentDecorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); + node.definition.body.forEach((property) => { + if (!arkts.isClassProperty(property)) { + return; + } + const newDecorator = this.findPropertyDecorator(property, OldNewDecoratorMixUseCheckRule.newV2decorators); + const oldDecorator = this.findPropertyDecorator(property, OldNewDecoratorMixUseCheckRule.oldV1Decorators); + // Check that the new decorator is used for component v2 + if (newDecorator && !componentV2Decorator && componentDecorator) { + this.reportErrorAndChangeDecorator(newDecorator, componentDecorator, PresetDecorators.COMPONENT_V2); + } + if (newDecorator && !componentDecorator && !componentV2Decorator) { + this.reportErrorAndAddDecorator(node, newDecorator); + } + // Check that the old decorator is used for component v1 + if (oldDecorator && !componentDecorator && componentV2Decorator) { + this.reportErrorAndChangeDecorator(oldDecorator, componentV2Decorator, PresetDecorators.COMPONENT_V1); + } + }); + } -function findDecoratorError( - node: arkts.StructDeclaration, - context: UISyntaxRuleContext -): void { - const hasComponentV2Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); - const hasComponentDecorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); - // Check where @Param and @State are used - node.definition.body.forEach((property) => { - if (arkts.isClassProperty(property)) { - const hasParamDecorator = findPropertyDecorator(property, PresetDecorators.PARAM); - const hasStateDecorator = findPropertyDecorator(property, PresetDecorators.STATE); - // Check that @Param is in the correct location - if (hasParamDecorator && !hasComponentV2Decorator && hasComponentDecorator) { - paramDecoratorError(context, hasParamDecorator, hasComponentDecorator); - } - // Check that @State is in the correct location - if (hasStateDecorator && !hasComponentDecorator && hasComponentV2Decorator) { - stateDecoratorError(context, hasStateDecorator, hasComponentV2Decorator); - } + private findPropertyDecorator( + node: arkts.ClassProperty, + decoratorList: string[] + ): arkts.AnnotationUsage | undefined { + return node.annotations?.find(annotation => + annotation.expr && + arkts.isIdentifier(annotation.expr) && + decoratorList.includes(getIdentifierName(annotation.expr)) + ); } - }); -} + private reportErrorAndChangeDecorator( + errorDecorator: arkts.AnnotationUsage, + hasComponentV2Decorator: arkts.AnnotationUsage, + structDecoratorName: string + ): void { + let propertyDecoratorName = getAnnotationName(errorDecorator); + const curStructDecoratorName = structDecoratorName === + PresetDecorators.COMPONENT_V2 ? PresetDecorators.COMPONENT_V1 : PresetDecorators.COMPONENT_V2; + this.report({ + node: errorDecorator, + message: this.messages.oldAndNewDecoratorsMixUse, + data: { + decoratorName: propertyDecoratorName, + component: structDecoratorName, + }, + fix: () => { + return { + title: `Change @${curStructDecoratorName} to @${structDecoratorName}`, + range: [hasComponentV2Decorator.startPosition, hasComponentV2Decorator.endPosition], + code: structDecoratorName, + }; + }, + }); + } -const rule: UISyntaxRule = { - name: 'old-new-decorator-mix-use-check', - messages: { - paramDecoratorError: `The '@Param' decorator can only be used in a 'struct' decorated with '@ComponentV2'.`, - stateDecoratorError: `The '@State' decorator can only be used in a 'struct' decorated with '@Component'.`, - }, - setup(context) { - return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; - } - findDecoratorError(node, context); - }, - }; - }, -}; + private reportErrorAndAddDecorator( + structNode: arkts.StructDeclaration, + errorDecorator: arkts.AnnotationUsage, + ): void { + let propertyDecoratorName = getAnnotationName(errorDecorator); + this.report({ + node: errorDecorator, + message: this.messages.oldAndNewDecoratorsMixUse, + data: { + decoratorName: propertyDecoratorName, + component: PresetDecorators.COMPONENT_V2, + }, + fix: () => { + return { + title: 'Add @ComponentV2 annotation', + range: [structNode.startPosition, structNode.startPosition], + code: `@${PresetDecorators.COMPONENT_V2}\n`, + }; + }, + }); + } +} -export default rule; +export default OldNewDecoratorMixUseCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts index fabe04af0..0c0d16f82 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts @@ -14,174 +14,131 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getAnnotationUsage, getClassPropertyAnnotationNames, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getClassPropertyAnnotationNames, PresetDecorators, findDecorator } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function findDecorator(member: arkts.ClassProperty, decorator: string): arkts.AnnotationUsage | undefined { - return member.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === decorator - ); -} - -// Check that the property decorator complies with the rules -function validatePropertyAnnotations( - body: arkts.ClassProperty, - context: UISyntaxRuleContext, - hasOnceDecorator: arkts.AnnotationUsage | undefined -): void { - const propertyAnnotations = getClassPropertyAnnotationNames(body); - hasOnceDecorator = findDecorator(body, PresetDecorators.ONCE); - if (hasOnceDecorator) { - const isParamUsed = propertyAnnotations.includes(PresetDecorators.PARAM); - // If @Once is found, check if @Param is also used - if (!isParamUsed) { - reportMissingParamWithOnce(hasOnceDecorator, context); - } else { - // If both @Once and @Param are used, check for other - // incompatible decorators - const otherDecorators = body.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() !== PresetDecorators.ONCE && - annotation.expr.dumpSrc() !== PresetDecorators.PARAM - ); - reportInvalidDecoratorsWithOnceAndParam(otherDecorators, context); +class OnceDecoratorCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidMemberDecorate: `'@Once' can only decorate member property.`, + invalidDecorator: `When a variable decorated with '@Once', it must also be decorated with '@Param'.`, + invalidNOtInStruct: `'@Once' annotation can only be used with 'struct'.` + }; } - } -} -function reportMissingParamWithOnce( - hasOnceDecorator: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - if (!hasOnceDecorator) { - return; - } - context.report({ - node: hasOnceDecorator, - message: rule.messages.invalidDecorator, - fix: (hasOnceDecorator) => { - const startPosition = arkts.getEndPosition(hasOnceDecorator); - const endPosition = arkts.getEndPosition(hasOnceDecorator); - return { - range: [startPosition, endPosition], - code: `@${PresetDecorators.PARAM}` - }; + public parsed(node: arkts.AstNode): void { + + this.validateOnlyInStruct(node); + this.validateOnlyOnProperty(node); + + if (arkts.isStructDeclaration(node)) { + this.validateDecorator(node); + } } - }); -} -function reportInvalidDecoratorsWithOnceAndParam( - otherDecorators: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - if (!otherDecorators) { - return; - } - context.report({ - node: otherDecorators, - message: rule.messages.invalidDecorator, - fix: (otherDecorators) => { - const startPosition = arkts.getStartPosition(otherDecorators); - const endPosition = arkts.getEndPosition(otherDecorators); - return { - range: [startPosition, endPosition], - code: '' - }; + private validateOnlyInStruct(node: arkts.AstNode): void { + if (arkts.isClassDeclaration(node)) { + node.definition?.body.forEach(member => { + if (arkts.isClassProperty(member)) { + this.validateOnceDecoratorUsage(member, this.messages.invalidNOtInStruct); + + } + if (arkts.isMethodDefinition(member)) { + this.validateOnceDecoratorUsage(member.scriptFunction, this.messages.invalidNOtInStruct); + } + }); + return; + } + // function/ variable/ interface/ type alias declaration + if (arkts.isFunctionDeclaration(node) || + arkts.isVariableDeclaration(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node) + ) { + this.validateOnceDecoratorUsage(node, this.messages.invalidNOtInStruct); + return; + } } - }); -} -// Check if the method is @Once decorated (not allowed) -function validateMethodAnnotations(body: arkts.MethodDefinition, context: UISyntaxRuleContext): void { - const methodAnnotations = body.scriptFunction.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === PresetDecorators.ONCE - ); - if (methodAnnotations) { - context.report({ - node: methodAnnotations, - message: rule.messages.invalidMemberDecorate, - fix: (methodAnnotations) => { - const startPosition = arkts.getStartPosition(methodAnnotations); - const endPosition = arkts.getEndPosition(methodAnnotations); - return { - range: [startPosition, endPosition], - code: '' - }; - } - }); - } -} + private validateOnceDecoratorUsage( + node: arkts.ClassProperty | arkts.VariableDeclaration | arkts.FunctionDeclaration | + arkts.ScriptFunction | arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration, + message: string, + ): void { + const decorator = findDecorator(node, PresetDecorators.ONCE); + if (!decorator) { + return; + } -function invalidComponentUsage( - body: arkts.ClassProperty, - hasOnceDecorator: arkts.AnnotationUsage | undefined, - componentV2DocoratorUsage: arkts.AnnotationUsage | undefined, - componentDocoratorUsage: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - hasOnceDecorator = findDecorator(body, PresetDecorators.ONCE); - if (hasOnceDecorator && !componentV2DocoratorUsage && componentDocoratorUsage) { - context.report({ - node: hasOnceDecorator, - message: rule.messages.invalidUsage, - fix: (hasOnceDecorator) => { - const startPosition = arkts.getStartPosition(componentDocoratorUsage); - const endPosition = arkts.getEndPosition(componentDocoratorUsage); - return { - range: [startPosition, endPosition], - code: `@${PresetDecorators.COMPONENT_V2}` - }; - } - }); - } -} + this.report({ + node: decorator, + message: message, + fix: (decorator) => { + let startPosition = decorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = decorator.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + } + }); + } + + private validateOnlyOnProperty(node: arkts.AstNode): void { + if (arkts.isFunctionDeclaration(node) || arkts.isScriptFunction(node) || arkts.isVariableDeclaration(node) || + arkts.isTSInterfaceDeclaration(node) || arkts.isTSTypeAliasDeclaration(node) + ) { + this.validateOnceDecoratorUsage(node, this.messages.invalidMemberDecorate); + } + } -function validateDecorater( - node: arkts.StructDeclaration, - hasOnceDecorator: arkts.AnnotationUsage | undefined, - componentV2DocoratorUsage: arkts.AnnotationUsage | undefined, - componentDocoratorUsage: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext, -): void { - node.definition?.body.forEach(body => { - // Check if @Once is used on a property and if @Param is used with - if (arkts.isClassProperty(body)) { - validatePropertyAnnotations(body, context, hasOnceDecorator); - // If @Once is used but not in a @ComponentV2 struct, report an error - invalidComponentUsage(body, hasOnceDecorator, componentV2DocoratorUsage, componentDocoratorUsage, context); + private validateDecorator( + node: arkts.StructDeclaration, + ): void { + node.definition?.body.forEach(body => { + // Check if @Once is used on a property and if @Param is used with + if (arkts.isClassProperty(body)) { + this.validatePropertyAnnotations(body); + } + }); } - if (!arkts.isMethodDefinition(body)) { - return; + + private validatePropertyAnnotations( + body: arkts.ClassProperty + ): void { + const propertyAnnotations = getClassPropertyAnnotationNames(body); + const onceDecorator = findDecorator(body, PresetDecorators.ONCE); + if (onceDecorator) { + const isParamUsed = propertyAnnotations.includes(PresetDecorators.PARAM); + // If @Once is found, check if @Param is not used + if (!isParamUsed) { + this.reportMissingParamWithOnce(onceDecorator); + } + } } - // Check if @Once is used on a method (which is not allowed) - validateMethodAnnotations(body, context); - }); -} -const rule: UISyntaxRule = { - name: 'once-decorator-check', - messages: { - invalidUsage: `@Once can only decorate member properties in a @ComponentV2 struct.`, - invalidMemberDecorate: `@Once can only decorate member properties.`, - invalidDecorator: `@Once must be only used with @Param. ` - }, - setup(context) { - return { - parsed: (node): void => { - // Check if the node is a struct declaration - if (!arkts.isStructDeclaration(node)) { - return; + private reportMissingParamWithOnce( + onceDecorator: arkts.AnnotationUsage | undefined + ): void { + if (!onceDecorator) { + return; } - let hasOnceDecorator: arkts.AnnotationUsage | undefined; - // Check if the struct is decorated with @ComponentV2 - const componentV2DocoratorUsage = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); - const componentDocoratorUsage = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); - validateDecorater(node, hasOnceDecorator, componentV2DocoratorUsage, componentDocoratorUsage, context); - }, - }; - }, -}; + this.report({ + node: onceDecorator, + message: this.messages.invalidDecorator, + fix: () => { + const startPosition = onceDecorator.endPosition; + const endPosition = onceDecorator.endPosition; + return { + title: 'Add @Param annotation', + range: [startPosition, endPosition], + code: `@${PresetDecorators.PARAM}` + }; + } + }); + } +} -export default rule; +export default OnceDecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/one-decorator-on-function-method.ts b/arkui-plugins/ui-syntax-plugins/rules/one-decorator-on-function-method.ts index 66dd9cdbd..38b35cae0 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/one-decorator-on-function-method.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/one-decorator-on-function-method.ts @@ -14,55 +14,110 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getAnnotationName } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getAnnotationName, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const allowedDecorators = new Set(['Extend', 'Builder', 'Styles']); +const allowedDecorators = PresetDecorators.BUILDER; +const PARAM_THIS_NAME = '=t'; +const DECORATOR_LIMIT = 1; -function validateFunctionDecorator(node: arkts.EtsScript, context: UISyntaxRuleContext): void { - node.statements.forEach((statement) => { - // If the node is not a function declaration, it is returned - if (!arkts.isFunctionDeclaration(statement)) { - return; +class OneDecoratorOnFunctionMethodRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidDecorator: `A function can only be decorated by the 'Builder'.`, + }; } - const annotations = statement.annotations; - // If there is no annotation, go straight back - if (!annotations) { - return; - } - // Check that each annotation is in the list of allowed decorators - annotations.forEach((annotation) => { - const decoratorName = getAnnotationName(annotation); - // rule1: misuse of decorator, only '@Extend', '@Builder' , '@Styles' decorators allowed on global functions - if (!allowedDecorators.has(decoratorName)) { - context.report({ - node: annotation, - message: rule.messages.invalidDecorator, - data: { - decoratorName, - }, - }); - } - }); - }); -} -const rule: UISyntaxRule = { - name: 'one-decorator-on-function-method', - messages: { - invalidDecorator: `misuse of '@{{decoratorName}}' decorator, only '@Extend', '@Builder' and '@Styles' decorators allowed on global functions.`, - }, - setup(context) { - return { - parsed: (node: arkts.AstNode): void => { + public parsed(node: arkts.AstNode): void { // If the node is not an ETS script, it is returned directly if (!arkts.isEtsScript(node)) { - return; + return; + } + this.validateFunctionDecorator(node); + } + + private validateFunctionDecorator(node: arkts.EtsScript): void { + node.statements.forEach((statement) => { + // If the node is not a function declaration, it is returned + if (!arkts.isFunctionDeclaration(statement)) { + return; + } + const annotations = statement.annotations; + // If there is no annotation, go straight back + if (!annotations) { + return; + } + // @AnimatableExtend decorators can only be used with functions with this parameter. + const animatableExtendDecorator = this.findDecorator(annotations, PresetDecorators.ANIMATABLE_EXTEND); + if (arkts.isScriptFunction(statement.scriptFunction) && animatableExtendDecorator) { + const member = statement.scriptFunction; + if (this.hasThisParameter(member)) { + return; + } + } + // Check that each annotation is in the list of allowed decorators + this.validateAllowedDecorators(annotations, this.otherDecoratorFilter(annotations)); + }); + } + + private findDecorator(annotations: arkts.AnnotationUsage[], decorator: string): arkts.AnnotationUsage | undefined { + return annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === decorator + ); + } + + private otherDecoratorFilter(annotations: arkts.AnnotationUsage[]): arkts.AnnotationUsage | undefined { + return annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name !== PresetDecorators.BUILDER + ); + } + + private hasThisParameter(member: arkts.ScriptFunction): boolean { + return member.params.some((param) => { + return arkts.isEtsParameterExpression(param) && + arkts.isIdentifier(param.identifier) && + param.identifier.name === PARAM_THIS_NAME; + }); + } + + private validateAllowedDecorators( + annotations: arkts.AnnotationUsage[], + otherDecorator: arkts.AnnotationUsage | undefined, + ): void { + annotations.forEach((annotation) => { + const decoratorName = getAnnotationName(annotation); + // rule1: misuse of decorator, only '@Builder' decorator allowed on global functions + if (allowedDecorators !== decoratorName || + (allowedDecorators === decoratorName && decoratorName.length > DECORATOR_LIMIT)) { + this.reportInvalidDecorator(annotation, otherDecorator); + } + }); + } + + private reportInvalidDecorator( + annotation: arkts.AnnotationUsage, + otherDecorator: arkts.AnnotationUsage | undefined, + ): void { + if (!otherDecorator) { + return; } - validateFunctionDecorator(node, context); - }, - }; - }, -}; + this.report({ + node: annotation, + message: this.messages.invalidDecorator, + fix: () => { + let startPosition = otherDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = otherDecorator.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: `` + }; + } + }); + } +} -export default rule; +export default OneDecoratorOnFunctionMethodRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/property-type.ts b/arkui-plugins/ui-syntax-plugins/rules/property-type.ts new file mode 100644 index 000000000..3a43242f0 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/property-type.ts @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { PresetDecorators, TypeFlags, getIdentifierName, findDecorator, hasAnnotation } from '../utils/index'; + +const v1DecoratorMustHasType = [ + PresetDecorators.STATE, + PresetDecorators.PROP_REF, + PresetDecorators.LINK, + PresetDecorators.OBJECT_LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.STORAGE_LINK, + PresetDecorators.WATCH, +]; + +const v2DecoratorMustHasType = [ + PresetDecorators.LOCAL, + PresetDecorators.PARAM, + PresetDecorators.EVENT, + PresetDecorators.PROVIDER, + PresetDecorators.CONSUMER, +]; + +const propertyPropDecorator = [ + PresetDecorators.PROP_REF, + PresetDecorators.STORAGE_PROP_REF, +]; + +const SimpleTypesUnSupported = [ + TypeFlags.Boolean, + TypeFlags.String, + TypeFlags.Number, + TypeFlags.Enum, + TypeFlags.BigInt, +]; + +const ARRAY_TYPES = ['Array', 'Map', 'Set', 'Date']; + +const PropErrorType = ['Any', 'unknown']; + +class PropertyTypeRule extends AbstractUISyntaxRule { + private noObservedV2ClassNames: string[] = []; + private observedV2ClassNames: string[] = []; + private builderFunctionName: string[] = []; + private currentStructBuilderMethodName: string[] = []; + private structStaticMethodsMap: Map> = new Map(); + + public setup(): Record { + return { + propertyObjectLink: `'@ObjectLink' cannot be used with this type. Apply it only to classes decorated by '@Observed' or initialized using the return value of 'makeV1Observed'.`, + propertyProp: `The '@{{decoratorName}}' decorated attribute '{{propertyName}}' must be of the string, number, boolean, enum or object type.`, + propertyBuilderParam: `'@BuilderParam' property can only be initialized by '@Builder' function or '@Builder' method in struct.`, + }; + } + + public beforeTransform(): void { + this.noObservedV2ClassNames = []; + this.observedV2ClassNames = []; + this.builderFunctionName = []; + this.currentStructBuilderMethodName = []; + this.structStaticMethodsMap = new Map(); + } + + public parsed(node: arkts.AstNode): void { + this.collectObservedV1AndV2ClassNameAndEnumName(node); + this.validatePropertyTypeAndDecorators(node); + this.getBuilderFunctionNameAndStaticMethods(node); + this.getStructNameWithMultiplyBuilderParam(node); + } + + private collectObservedV1AndV2ClassNameAndEnumName( + node: arkts.AstNode, + ): void { + // Check if it's of type "Program". + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + // Traverse all child nodes of the Program + for (const child of node.getChildren()) { + // Check if it is of the ClassDeclaration type + if (arkts.isClassDeclaration(child) && child.definition) { + // Get a list of annotators + const annotations = child.definition.annotations; + + // Check for @ObservedV2 decorators + const observedV2Decorator = hasAnnotation(annotations, PresetDecorators.OBSERVED_V2); + + if (!observedV2Decorator && child.definition && child.definition.ident) { + // If there is a @Observed decorator, record the class name + const className = child.definition.ident.name; + this.noObservedV2ClassNames.push(className); + } + if (observedV2Decorator && child.definition && child.definition.ident) { + // If there is a @ObservedV2 decorator, record the class name + const v2ClassName = child.definition.ident.name; + this.observedV2ClassNames.push(v2ClassName); + } + } + } + } + + private areAllUnionMembersValid(unionType: arkts.ETSUnionType): boolean { + const members = unionType.types; + // At least one valid type All types must be allowed types and do not contain illegal combinations + let isValidType = false; + for (const member of members) { + if (arkts.isETSTypeReference(member) && + member.part && + member.part.name && + arkts.isIdentifier(member.part.name)) { + const propertyTypeName = member.part.name.name; + // If it's a simple type or ObservedV2, reject the entire union type outright + if (this.observedV2ClassNames.includes(propertyTypeName) || + SimpleTypesUnSupported.includes(propertyTypeName) || + PropErrorType.includes(propertyTypeName)) { + return false; + } + if (this.noObservedV2ClassNames.includes(propertyTypeName) || ARRAY_TYPES.includes(propertyTypeName)) { + isValidType = true; + } + } else if (arkts.isETSPrimitiveType(member) || + arkts.nodeType(member) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_STRING_LITERAL_TYPE || + arkts.nodeType(member) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ERROR_TYPE_NODE) { + return false; + } else if (arkts.nodeType(member) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NULL_TYPE || + arkts.isETSUndefinedType(member)) { + continue; + } + } + return isValidType; + } + + private areAllUnionMembersNotAnyAndBigint(unionType: arkts.ETSUnionType): boolean { + const members = unionType.types; + for (const member of members) { + if (arkts.isETSTypeReference(member) && + member.part && + member.part.name && + arkts.isIdentifier(member.part.name)) { + const propertyTypeName = member.part.name.name; + if (propertyTypeName === PropErrorType[0] || propertyTypeName === TypeFlags.BigInt) { + return false; + } + } else if (arkts.nodeType(member) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ERROR_TYPE_NODE) { + return false; + } + } + return true; + } + + private validatePropertyTypeAndDecorators( + node: arkts.AstNode + ): void { + //Check whether the current node is a property + if (!arkts.isClassProperty(node) || !node.key || !arkts.isIdentifier(node.key)) { + return; + } + const propertyName = node.key.name; + // Gets the type of property + const propertyType = node.typeAnnotation; + const nodeKey = node.key; + const objectLinkDecorator = findDecorator(node, PresetDecorators.OBJECT_LINK); + // Determine whether the property has @Objectlink + if (objectLinkDecorator && propertyType) { + this.validateObjectLinkPropertyType(propertyType, nodeKey); + } + this.processPropertyAnnotations(node, propertyType, propertyName, nodeKey); + } + + private validateObjectLinkPropertyType( + propertyType: arkts.TypeNode, + nodeKey: arkts.Identifier + ): void { + if (arkts.isETSTypeReference(propertyType) && + propertyType.part && + propertyType.part.name && + arkts.isIdentifier(propertyType.part.name)) { + const propertyTypeName = propertyType.part.name.name; + if (this.observedV2ClassNames.includes(propertyTypeName) || + SimpleTypesUnSupported.includes(propertyTypeName) || + PropErrorType.includes(propertyTypeName)) { + this.report({ + node: nodeKey, + message: this.messages.propertyObjectLink, + }); + } + } else if (arkts.nodeType(propertyType) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_STRING_LITERAL_TYPE || + arkts.nodeType(propertyType) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NULL_TYPE || + arkts.nodeType(propertyType) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ERROR_TYPE_NODE || + arkts.isETSPrimitiveType(propertyType) || + arkts.isETSUndefinedType(propertyType) + ) { + this.report({ + node: nodeKey, + message: this.messages.propertyObjectLink, + }); + } + if (arkts.isETSUnionType(propertyType)) { + if (!this.areAllUnionMembersValid(propertyType)) { + this.report({ + node: nodeKey, + message: this.messages.propertyObjectLink, + }); + } + } + } + + private processPropertyAnnotations( + node: arkts.ClassProperty, + propertyType: arkts.TypeNode | undefined, + propertyName: string, + nodeKey: arkts.Identifier, + ): void { + // Iterate through all annotations + node.annotations?.forEach(annotation => { + if (!propertyType || !annotation.expr || !arkts.isIdentifier(annotation.expr)) { + return; + } + const decoratorName = annotation.expr.name; + if (!arkts.isETSUnionType(propertyType)) { + this.reportIfInvalidPropType(propertyType, decoratorName, propertyName, nodeKey); + } else if (arkts.isETSUnionType(propertyType)) { + this.reportIfInvalidUnionPropType(propertyType, decoratorName, propertyName, nodeKey); + } + }); + } + + private reportIfInvalidPropType( + propertyType: arkts.TypeNode, + decoratorName: string, + propertyName: string, + nodeKey: arkts.Identifier, + ): void { + if (arkts.isETSTypeReference(propertyType) && + propertyType.part && + propertyType.part.name && + arkts.isIdentifier(propertyType.part.name)) { + const propertyTypeName = propertyType.part.name.name; + if ((propertyPropDecorator.includes(decoratorName)) && nodeKey) { + // Check if the @Prop property is of any or bigint + if (propertyTypeName === PropErrorType[0] || + propertyTypeName === TypeFlags.BigInt + ) { + this.report({ + node: nodeKey, + message: this.messages.propertyProp, + data: { + decoratorName, + propertyName, + }, + }); + } + } + } + } + + private reportIfInvalidUnionPropType( + propertyType: arkts.ETSUnionType, + decoratorName: string, + propertyName: string, + nodeKey: arkts.Identifier, + ): void { + if (!this.areAllUnionMembersNotAnyAndBigint(propertyType) && propertyPropDecorator.includes(decoratorName)) { + this.report({ + node: nodeKey, + message: this.messages.propertyProp, + data: { + decoratorName, + propertyName, + }, + }); + } + } + + private getStructNameWithMultiplyBuilderParam( + node: arkts.AstNode, + ): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + // Only the situation within the component is judged + if (!arkts.isStructDeclaration(member) || !member.definition.ident) { + return; + } + this.getCurrentStructBuilderMethodName(member); + member.definition?.body?.forEach((item) => { + this.checkBuilderParamInitialization(item); + }); + this.currentStructBuilderMethodName.length = 0; + }); + } + + private checkBuilderParamInitialization( + item: arkts.AstNode + ): void { + if (!arkts.isClassProperty(item) || !item.key) { + return; + } + const builderParamDecorator = findDecorator(item, PresetDecorators.BUILDER_PARAM); + if (item.value && !arkts.isMemberExpression(item.value) && !arkts.isIdentifier(item.value) && + builderParamDecorator) { + this.reportInvalidBuilderParamInitialized(item.key); + } else if (item.value && arkts.isMemberExpression(item.value) && builderParamDecorator) { + this.checkValidMemberExpressionUsage(item.value, item.key); + } else if (item.value && arkts.isIdentifier(item.value) && item.typeAnnotation && builderParamDecorator) { + if (arkts.isETSFunctionType(item.typeAnnotation) && + !this.builderFunctionName.includes(item.value.name)) { + this.reportInvalidBuilderParamInitialized(item.key); + } + } + } + + private checkValidMemberExpressionUsage( + itemValue: arkts.MemberExpression, + itemKey: arkts.Expression + ): void { + if (!arkts.isThisExpression(itemValue.object) && !arkts.isIdentifier(itemValue.object)) { + this.reportInvalidBuilderParamInitialized(itemKey); + } else if (arkts.isThisExpression(itemValue.object) && + arkts.isIdentifier(itemValue.property) && + !this.currentStructBuilderMethodName.includes(itemValue.property.name)) { + this.reportInvalidBuilderParamInitialized(itemKey); + } else if (arkts.isIdentifier(itemValue.object) && arkts.isIdentifier(itemValue.property)) { + const structName = getIdentifierName(itemValue.object); + const staticMethodsName = getIdentifierName(itemValue.property); + const methods = this.structStaticMethodsMap.get(structName); + if (methods && !methods.has(staticMethodsName)) { + this.reportInvalidBuilderParamInitialized(itemKey); + } + } + } + + private reportInvalidBuilderParamInitialized(node: arkts.AstNode): void { + this.report({ + node: node, + message: this.messages.propertyBuilderParam, + }); + } + + private getBuilderFunctionNameAndStaticMethods( + node: arkts.AstNode + ): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (arkts.isFunctionDeclaration(member) && member.scriptFunction.id?.name) { + const hasBuilderDecorator = findDecorator(member, PresetDecorators.BUILDER); + if (hasBuilderDecorator) { + this.builderFunctionName.push(member.scriptFunction.id?.name); + } + } + if (arkts.isStructDeclaration(member) && member.definition.ident) { + let structName = member.definition.ident.name; + this.structStaticMethodsMap.set(structName, new Set()); + member.definition.body.forEach((item) => { + this.collectBuilderStaticMethodFromStruct(item, structName); + }); + } + }); + } + + private collectBuilderStaticMethodFromStruct( + item: arkts.AstNode, + structName: string + ): void { + if (!arkts.isMethodDefinition(item) || !item.scriptFunction.id || !item.isStatic) { + return; + } + const hasBuilderDecorator = findDecorator(item.scriptFunction, PresetDecorators.BUILDER); + // judgment static method + if (hasBuilderDecorator && arkts.isIdentifier(item.scriptFunction.id) && item.isStatic) { + const methodName = item.scriptFunction.id.name; + this.structStaticMethodsMap.get(structName)?.add(methodName); + } + } + + private getCurrentStructBuilderMethodName( + node: arkts.StructDeclaration + ): void { + node.definition?.body?.forEach((item) => { + if (!arkts.isMethodDefinition(item) || !item.scriptFunction.id) { + return; + } + const builderDecorator = findDecorator(item.scriptFunction, PresetDecorators.BUILDER); + // judgment static method + if (builderDecorator && arkts.isIdentifier(item.scriptFunction.id) && !item.isStatic) { + this.currentStructBuilderMethodName.push(item.scriptFunction.id.name); + } + }); + } +} + +export default PropertyTypeRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/require-decorator-regular.ts b/arkui-plugins/ui-syntax-plugins/rules/require-decorator-regular.ts new file mode 100644 index 000000000..dd3abf59c --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/require-decorator-regular.ts @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { getClassPropertyAnnotationNames, PresetDecorators, isPrivateClassProperty, getClassPropertyName, findDecorator } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class RequireDecoratorRegularRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidPrivateWithRequire: `Property '{{propertyName}}' can not be decorated with both {{decoratorName}} and private.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + this.checkRequireDecorator(node); + } + + private checkRequireDecorator(node: arkts.StructDeclaration): void { + node.definition.body.forEach(member => { + if (!arkts.isClassProperty(member)) { + return; + } + // Get the list of decorators applied to the class property + const propertyDecorators = getClassPropertyAnnotationNames(member); + if (!propertyDecorators.includes(PresetDecorators.REQUIRE)) { + return; + } + if (isPrivateClassProperty(member)) { + this.handlePrivateWithRequire(member); + } + }); + } + + private handlePrivateWithRequire(member: arkts.ClassProperty): void { + const requireDecorator = findDecorator(member, PresetDecorators.REQUIRE); + const propertyName = getClassPropertyName(member); + if (!propertyName) { + return; + } + if (requireDecorator) { + this.report({ + node: requireDecorator, + message: this.messages.invalidPrivateWithRequire, + data: { + propertyName, + decoratorName: PresetDecorators.REQUIRE, + }, + }); + } + } +} + +export default RequireDecoratorRegularRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/reusable-component-in-V2-check.ts b/arkui-plugins/ui-syntax-plugins/rules/reusable-component-in-V2-check.ts new file mode 100644 index 000000000..57901a88d --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/reusable-component-in-V2-check.ts @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { getAnnotationUsage, PresetDecorators } from '../utils'; + +class ReusableComponentInV2CheckRule extends AbstractUISyntaxRule { + private reusableStructName: string[] = []; + + public setup(): Record { + return { + noReusableV1InComponentV2: `When a custom component is decorated with @ComponentV2 and contains a child component decorated with @Reusable, the child component will not create.`, + }; + } + + public beforeTransform(): void { + this.reusableStructName = []; + } + public parsed(node: arkts.StructDeclaration): void { + this.initStructName(node); + this.checkNoReusableV1InComponentV2(node); + } + + private initStructName(node: arkts.AstNode): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + //Go through all the children of Program + for (const childNode of node.getChildren()) { + // Check whether the type is struct + if (!arkts.isStructDeclaration(childNode)) { + continue; + } + const reusableV1Decorator = getAnnotationUsage(childNode, PresetDecorators.REUSABLE_V1); + const structName = childNode.definition?.ident?.name; + if (reusableV1Decorator && structName) { + this.reusableStructName.push(structName); + } + } + } + + private checkNoReusableV1InComponentV2(node: arkts.AstNode,): void { + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; + } + if (this.reusableStructName.includes(node.expression.name)) { + // Traverse upwards to find the custom component. + let structNode: arkts.AstNode = node; + while (!arkts.isStructDeclaration(structNode)) { + if (!structNode.parent) { + return; + } + structNode = structNode.parent; + } + const annotationsList = structNode.definition.annotations; + // Check that the current component is decorated by the @ComponentV2 decorator + if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.COMPONENT_V2)) { + this.report({ + node: node, + message: this.messages.noReusableV1InComponentV2, + }); + } + } + } +} + +export default ReusableComponentInV2CheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/reusableV2-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/reusableV2-decorator-check.ts new file mode 100644 index 000000000..24a7ca87c --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/reusableV2-decorator-check.ts @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { PresetDecorators, getAnnotationUsage } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class ReusableV2DecoratorCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + conflictingDecorators: `The '@Reusable' and '@ReusableV2' annotations cannot be applied simultaneously.`, + invalidDecoratorUsage: `@ReusableV2 is only applicable to custom components decorated by @ComponentV2.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + if (!node.definition) { + return; + } + if (!arkts.isClassDefinition(node.definition)) { + return; + } + const structNode = node.definition.ident; + // Check whether the decoration exists, and mark true if it does + const reusableDecoratorUsage = getAnnotationUsage(node, PresetDecorators.REUSABLE_V1); + const reusableV2DecoratorUsage = getAnnotationUsage(node, PresetDecorators.REUSABLE_V2); + const componentV2DecoratorUsage = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); + + // Check whether @Reusable and @ReusableV2 exist at the same time + if (reusableV2DecoratorUsage && reusableDecoratorUsage && structNode) { + this.reportConflictingDecorators(reusableDecoratorUsage, structNode); + } + + // Check if @ReusableV2 is applied to a class decorated by @ComponentV2 + if (reusableV2DecoratorUsage && !componentV2DecoratorUsage && structNode) { + this.reportInvalidDecoratorUsage(node, structNode); + } + } + + private reportConflictingDecorators( + reusableDecoratorUsage: arkts.AnnotationUsage | undefined, + structNode: arkts.Identifier | undefined, + ): void { + if (!structNode || !reusableDecoratorUsage) { + return; + } + this.report({ + node: structNode, + message: this.messages.conflictingDecorators, + }); + } + + private reportInvalidDecoratorUsage( + node: arkts.StructDeclaration, + structNode: arkts.Identifier | undefined, + ): void { + if (!structNode || !node) { + return; + } + this.report({ + node: structNode, + message: this.messages.invalidDecoratorUsage, + }); + } +} + +export default ReusableV2DecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/reuse-attribute-check.ts b/arkui-plugins/ui-syntax-plugins/rules/reuse-attribute-check.ts index 773b133ac..509a2a2fd 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/reuse-attribute-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/reuse-attribute-check.ts @@ -14,94 +14,108 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; -import { PresetDecorators, getAnnotationUsage } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { PresetDecorators, getAnnotationUsage, ReuseConstants } from '../utils'; -function findStructsWithReusableAndComponentV2(node: arkts.AstNode, reusableV2ComponentV2Struct: string[]): void { - //Go through all the children of Program - for (const childNode of node.getChildren()) { - // Check whether the type is struct - if (!arkts.isStructDeclaration(childNode)) { - continue; - } - // Check that the current component has @ComponentV2 and @ReusableV2 decorators - const hasReusableV2Decorator = getAnnotationUsage(childNode, PresetDecorators.REUSABLE_V2); - const hasComponentV2Decorator = getAnnotationUsage(childNode, PresetDecorators.COMPONENT_V2); - if (hasReusableV2Decorator && hasComponentV2Decorator) { - const struceName = childNode.definition?.ident?.name ?? ''; - reusableV2ComponentV2Struct.push(struceName); - } - } -} +class ReuseAttributeCheckRule extends AbstractUISyntaxRule { + private reusableV2ComponentV2Struct: string[] = []; -function validateReuseOrReuseIdUsage(context: UISyntaxRuleContext, node: arkts.MemberExpression, - reusableV2ComponentV2Struct: string[]): void { - const structNode = node.object; - // Gets the reuse or reuseId attribute - const decoratedNode = node.property; - if (arkts.isCallExpression(structNode)) { - const Node = structNode.expression; - if (decoratedNode.dumpSrc() === 'reuse' && !reusableV2ComponentV2Struct.includes(Node.dumpSrc())) { - reportInvalidReuseUsage(context, node, structNode, rule); + public setup(): Record { + return { + invalidReuseUsage: `The reuse attribute is only applicable to custom components decorated with both @ComponentV2 and @ReusableV2.`, + invalidReuseIdUsage: `The reuseId attribute is not applicable to custom components decorated with both @ComponentV2 and @ReusableV2.`, + }; } - else if (decoratedNode.dumpSrc() === 'reuseId' && reusableV2ComponentV2Struct.includes(Node.dumpSrc())) { - reportInvalidReuseIdUsage(context, node, structNode, rule); - } - } -} - -function reportInvalidReuseUsage(context: UISyntaxRuleContext, node: arkts.AstNode, structNode: arkts.AstNode, - rule: any): void { - context.report({ - node: node, - message: rule.messages.invalidReuseUsage, - fix: (node) => { - const startPosition = arkts.getStartPosition(node); - const endPosition = arkts.getEndPosition(node); - return { - range: [startPosition, endPosition], - code: `${structNode.dumpSrc()}.reuseId`, - }; - }, - }); -} -function reportInvalidReuseIdUsage(context: UISyntaxRuleContext, node: arkts.AstNode, structNode: arkts.AstNode, - rule: any): void { - context.report({ - node: node, - message: rule.messages.invalidReuseIdUsage, - fix: (node) => { - const startPosition = arkts.getStartPosition(node); - const endPosition = arkts.getEndPosition(node); - return { - range: [startPosition, endPosition], - code: `${structNode.dumpSrc()}.reuse`, - }; - }, - }); -} + public beforeTransform(): void { + this.reusableV2ComponentV2Struct = []; + } -const rule: UISyntaxRule = { - name: 'reuse-attribute-check', - messages: { - invalidReuseUsage: `The reuse attribute is only applicable to custom components decorated with both @ComponentV2 and @ReusableV2.`, - invalidReuseIdUsage: `The reuseId attribute is not applicable to custom components decorated with both @ComponentV2 and @ReusableV2.`, - }, - setup(context) { - const reusableV2ComponentV2Struct: string[] = []; - return { - parsed: (node): void => { + public parsed(node: arkts.AstNode): void { // Check whether the type is "Program" if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - findStructsWithReusableAndComponentV2(node, reusableV2ComponentV2Struct); + this.findStructsWithReusableAndComponentV2(node); } if (arkts.isMemberExpression(node)) { - validateReuseOrReuseIdUsage(context, node, reusableV2ComponentV2Struct); + this.validateReuseOrReuseIdUsage(node); + } + } + + private findStructsWithReusableAndComponentV2(node: arkts.AstNode): void { + //Go through all the children of Program + for (const childNode of node.getChildren()) { + // Check whether the type is struct + if (!arkts.isStructDeclaration(childNode)) { + continue; + } + // Check that the current component has @ComponentV2 and @ReusableV2 decorators + const reusableV2Decorator = getAnnotationUsage(childNode, PresetDecorators.REUSABLE_V2); + const componentV2Decorator = getAnnotationUsage(childNode, PresetDecorators.COMPONENT_V2); + if (reusableV2Decorator && componentV2Decorator) { + const struceName = childNode.definition.ident?.name ?? ''; + this.reusableV2ComponentV2Struct.push(struceName); + } } - }, - }; - }, + } + + private validateReuseOrReuseIdUsage( + node: arkts.MemberExpression + ): void { + const structNode = node.object; + // Gets the reuse or reuseId attribute + const decoratedNode = node.property; + if (arkts.isCallExpression(structNode)) { + const nodeExpression = structNode.expression; + if (arkts.isIdentifier(nodeExpression) && arkts.isIdentifier(decoratedNode)) { + if (decoratedNode.name === ReuseConstants.REUSE && + !this.reusableV2ComponentV2Struct.includes(nodeExpression.name)) { + this.reportInvalidReuseUsage(node, decoratedNode); + } + else if (decoratedNode.name === ReuseConstants.REUSE_ID && + this.reusableV2ComponentV2Struct.includes(nodeExpression.name)) { + this.reportInvalidReuseIdUsage(node, decoratedNode); + } + } + } + } + + private reportInvalidReuseUsage( + node: arkts.AstNode, + structNode: arkts.AstNode, + ): void { + this.report({ + node: node, + message: this.messages.invalidReuseUsage, + fix: () => { + const startPosition = structNode.startPosition; + const endPosition = structNode.endPosition; + return { + title: 'Change reuse to reuseId', + range: [startPosition, endPosition], + code: ReuseConstants.REUSE_ID, + }; + }, + }); + } + + private reportInvalidReuseIdUsage( + node: arkts.AstNode, + structNode: arkts.AstNode, + ): void { + this.report({ + node: node, + message: this.messages.invalidReuseIdUsage, + fix: () => { + const startPosition = structNode.startPosition; + const endPosition = structNode.endPosition; + return { + title: 'Change reuseId to reuse', + range: [startPosition, endPosition], + code: ReuseConstants.REUSE, + }; + }, + }); + } }; -export default rule; \ No newline at end of file +export default ReuseAttributeCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/specific-component-children.ts b/arkui-plugins/ui-syntax-plugins/rules/specific-component-children.ts new file mode 100644 index 000000000..aace72ff3 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/specific-component-children.ts @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { + getIdentifierName, + PresetDecorators, + SINGLE_CHILD_COMPONENT, + TOGGLE_TYPE, + ToggleType, + TYPE +} from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class SpecificComponentChildrenRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + toggleTypeCheckboxWithNoChild: `When the component '{{componentName}}' set '{{toggleTypeKey}}' as '{{toggleTypeValue}}', it can't have any child.`, + toggleTypeButtonWithSingleChild: `When the component '{{componentName}}' set '{{toggleTypeKey}}' as '{{toggleTypeValue}}', it can only have a single child component.`, + }; + } + + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isCallExpression(node) || !node.expression) { + return; + } + // Check whether the current node is an identifier and toggle component + if (!arkts.isIdentifier(node.expression)) { + return; + } + const componentName: string = getIdentifierName(node.expression); + if (componentName !== PresetDecorators.TOGGLE) { + return; + } + const toggleTypeValue = this.getToggleType(node); + if (toggleTypeValue === '') { + return; + } + // If there is more than one subComponent in the BlockStatement, an error is reported + node.getChildren().forEach(member => { + if (!arkts.isBlockStatement(member)) { + return; + } + // If the toggle component type is checkbox and has child components, an error is reported + if (toggleTypeValue === ToggleType.CHECKBOX && member.statements.length > 0) { + this.report({ + node: node, + message: this.messages.toggleTypeCheckboxWithNoChild, + data: { + componentName: componentName, + toggleTypeKey: TYPE, + toggleTypeValue: toggleTypeValue + } + }); + } + // If the toggle component type is button and there is more than one child component, an error is reported + if (toggleTypeValue === ToggleType.BUTTON && member.statements.length > SINGLE_CHILD_COMPONENT) { + this.report({ + node: node, + message: this.messages.toggleTypeButtonWithSingleChild, + data: { + componentName: componentName, + toggleTypeKey: TYPE, + toggleTypeValue: toggleTypeValue + } + }); + } + }); + } + + private getToggleType(node: arkts.CallExpression): string { + let toggleType = ''; + node.arguments.forEach((member) => { + if (!arkts.isObjectExpression(member) || !member.properties) { + return; + } + member.properties.forEach((property) => { + if (!arkts.isProperty(property) || !property.value) { + return; + } + // If the property name is not 'toggle type' + if (!arkts.isMemberExpression(property.value) || !property.value.object || + !arkts.isIdentifier(property.value.object) || property.value.object.name !== TOGGLE_TYPE) { + return; + } + if (!property.value.property || !arkts.isIdentifier(property.value.property)) { + return; + } + toggleType = property.value.property.name; + }); + }); + return toggleType; + } +} + +export default SpecificComponentChildrenRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/static-param-require.ts b/arkui-plugins/ui-syntax-plugins/rules/static-param-require.ts new file mode 100644 index 000000000..8f2fdb8e2 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/static-param-require.ts @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { getClassPropertyName, hasAnnotation, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class StaticParamRequireRule extends AbstractUISyntaxRule { + private staticPropertyMap: Map = new Map(); + + public setup(): Record { + return { + cannotInitializePrivateVariables: `Static property '{{propertyName}}' can not be initialized through the component constructor.`, + }; + } + + public beforeTransform(): void { + this.staticPropertyMap = new Map(); + } + + public parsed(node: arkts.StructDeclaration): void { + // Check if the current node is the root node + if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident || !member.definition.ident.name) { + return; + } + const hasComponentV1 = hasAnnotation(member.definition.annotations, PresetDecorators.COMPONENT_V1); + const hasComponentV2 = hasAnnotation(member.definition.annotations, PresetDecorators.COMPONENT_V2); + const structName: string = member.definition.ident.name; + member.definition.body.forEach((item) => { + this.addStaticProperty(item, structName, hasComponentV1, hasComponentV2); + }); + }); + } + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; + } + const componentName = node.expression.name; + // If the initialization is for a component with private properties + if (!this.staticPropertyMap.has(componentName)) { + return; + } + node.arguments.forEach((member) => { + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property) || !property.key || !arkts.isIdentifier(property.key)) { + return; + } + const propertyName: string = property.key.name; + if (this.staticPropertyMap.get(componentName)!.includes(propertyName)) { + this.report({ + node: property, + message: this.messages.cannotInitializePrivateVariables, + data: { + propertyName: propertyName, + }, + }); + } + }); + }); + } + + private addStaticProperty( + item: arkts.AstNode, + structName: string, + hasComponentV1: boolean, + hasComponentV2: boolean + ): void { + if (!arkts.isClassProperty(item) || !item.isStatic) { + return; + } + const propertyName = getClassPropertyName(item); + if (!propertyName) { + return; + } + // Static properties with decorators in componentV2 need to be checked + if (hasComponentV2 && item.annotations.length > 0) { + this.addElement(structName, propertyName); + } + // Static properties in componentV1 need to be verified + if (hasComponentV1 && !hasComponentV2) { + this.addElement(structName, propertyName); + } + } + + private addElement(structName: string, propertyName: string): void { + // Check if structName already exists in privateMap + if (this.staticPropertyMap.has(structName)) { + // If it exists, retrieve the current string[] and append the new content + this.staticPropertyMap.get(structName)!.push(propertyName); + } else { + // If it doesn't exist, create a new string[] and add the content + this.staticPropertyMap.set(structName, [propertyName]); + } + } +} + +export default StaticParamRequireRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/struct-missing-decorator.ts b/arkui-plugins/ui-syntax-plugins/rules/struct-missing-decorator.ts index 7ca8e7b3a..11b48a219 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/struct-missing-decorator.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-missing-decorator.ts @@ -15,60 +15,45 @@ import * as arkts from '@koalaui/libarkts'; import { getAnnotationUsage, PresetDecorators } from '../utils'; -import { UISyntaxRule } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const rule: UISyntaxRule = { - name: 'struct-missing-decorator', - messages: { - missingComponentDecorator: `struct '{{structName}}' is missing '@Component' or '@CustomDialog' decorators`, - misusedPreview: `struct '{{structName}}' misused '@Preview' decorator. it should be decorated by '@Component' or '@CustomDialog'`, - misusedObserved: `struct '{{structName}}' misused '@Observed' decorator. it should be decorated by '@Component' or '@CustomDialog'`, - }, - setup(context) { - function hasDecorator(node: arkts.StructDeclaration, decorator: string): boolean { - return !!getAnnotationUsage(node, decorator); +class StructMissingDecoratorRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + missingComponentDecorator: `Annotation '@Component', '@ComponentV2', or '@CustomDialog' is missing for struct '{{structName}}'.` + }; } - return { - parsed: (node): void => { + public parsed(node: arkts.AstNode): void { if (!arkts.isStructDeclaration(node)) { - return; + return; + } + if (!node.definition) { + return; + } + if (!arkts.isClassDefinition(node.definition)) { + return; } // Check for the presence of specific decorators on the struct const structName = node.definition.ident?.name ?? ''; const structNode = node.definition.ident; - const hasComponent = hasDecorator(node, PresetDecorators.COMPONENT_V1); - const hasComponentV2 = hasDecorator(node, PresetDecorators.COMPONENT_V2); - const hasCustomDialog = hasDecorator(node, PresetDecorators.CUSTOM_DIALOG); - const hasPreview = getAnnotationUsage(node, PresetDecorators.PREVIEW); - const hasObserved = getAnnotationUsage(node, PresetDecorators.OBSERVED_V1); + const hasComponent = this.hasDecorator(node, PresetDecorators.COMPONENT_V1); + const hasComponentV2 = this.hasDecorator(node, PresetDecorators.COMPONENT_V2); + const hasCustomDialog = this.hasDecorator(node, PresetDecorators.CUSTOM_DIALOG); + // If no valid component decorators (@Component or @CustomDialog) are found if (!hasComponent && !hasComponentV2 && !hasCustomDialog && structNode) { - context.report({ - node: structNode, - message: rule.messages.missingComponentDecorator, - data: { structName }, - }); - } - // If the @Preview decorator is used but the struct is not decorated with @Component or @CustomDialog - if (hasPreview && !hasComponent && !hasComponentV2 && !hasCustomDialog && structNode) { - context.report({ - node: structNode, - message: rule.messages.misusedPreview, - data: { structName }, - }); + this.report({ + node: structNode, + message: this.messages.missingComponentDecorator, + data: { structName }, + }); } - // If the @Observed decorator is used but the struct is not decorated with @Component or @CustomDialog - if (hasObserved && !hasComponent && !hasComponentV2 && !hasCustomDialog && structNode) { - context.report({ - node: structNode, - message: rule.messages.misusedObserved, - data: { structName }, - }); - } - }, - }; - }, -}; + } + + private hasDecorator(node: arkts.StructDeclaration, decorator: string): boolean { + return !!getAnnotationUsage(node, decorator); + } +} -export default rule; +export default StructMissingDecoratorRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/local-builder-check.ts b/arkui-plugins/ui-syntax-plugins/rules/struct-no-extends.ts similarity index 32% rename from arkui-plugins/ui-syntax-plugins/rules/local-builder-check.ts rename to arkui-plugins/ui-syntax-plugins/rules/struct-no-extends.ts index cbfeef0c3..12945ee48 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/local-builder-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-no-extends.ts @@ -1,7 +1,7 @@ /* * Copyright (c) 2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); - * You may not use this file except in compliance with the License. + * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 @@ -14,50 +14,29 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function checkLocalBuilder(node: arkts.ClassDeclaration, context: UISyntaxRuleContext): void { - node.definition?.body.forEach(body => { - if (!arkts.isMethodDefinition(body)) { - return; - } - const localBuilder = body.scriptFunction?.annotations?.find( - annotation => annotation.expr && - annotation.expr.dumpSrc() === PresetDecorators.LOCAL_BUILDER); - if (!localBuilder) { - return; - } - context.report({ - node: localBuilder, - message: rule.messages.invalidUsage, - fix: (localBuilder) => { - const startPosition = arkts.getStartPosition(localBuilder); - const endPosition = arkts.getEndPosition(localBuilder); +class StructNoExtendsRule extends AbstractUISyntaxRule { + public setup(): Record { return { - range: [startPosition, endPosition], - code: '', + structNoExtends: `Structs are not allowed to inherit from classes or implement interfaces.`, }; - }, - }); - }); -} - -const rule: UISyntaxRule = { - name: 'local-builder-check', - messages: { - invalidUsage: `The '@LocalBuilder' decorator can only be used in 'struct'.`, - }, - setup(context) { - return { - parsed: (node): void => { - if (!arkts.isClassDeclaration(node)) { - return; + } + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isStructDeclaration(node) || !node.definition.ident) { + return; + } + const hasSuperClass: boolean = node.definition.super !== undefined; + const hasImplements: boolean = node.definition.implements.length > 0; + // If there is an inheritance class or implementation interface, an error is reported + if (hasSuperClass || hasImplements) { + const errorNode = node.definition.ident; + this.report({ + node: errorNode, + message: this.messages.structNoExtends, + }); } - checkLocalBuilder(node, context); - }, - }; - }, -}; + } +} -export default rule; +export default StructNoExtendsRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/struct-property-decorator.ts b/arkui-plugins/ui-syntax-plugins/rules/struct-property-decorator.ts index 9aaa53de0..81228846b 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/struct-property-decorator.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-property-decorator.ts @@ -14,39 +14,106 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getClassPropertyAnnotationNames } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -function checkInvalidStaticPropertyDecorations(context: UISyntaxRuleContext, node: arkts.StructDeclaration): void { - node.definition.body.forEach((member) => { - // Errors are reported when the node type is ClassProperty, - if (arkts.isClassProperty(member)) { - const propertyNameNode = member.key; - if ((member.isStatic && getClassPropertyAnnotationNames(member).length > 0) && propertyNameNode) { - context.report({ - node: propertyNameNode, - message: rule.messages.invalidStaticUsage - }); - } +import { getClassPropertyAnnotationNames, hasAnnotation, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const v1Decorators: string[] = [ + PresetDecorators.BUILDER_PARAM, + PresetDecorators.STATE, + PresetDecorators.PROP_REF, + PresetDecorators.LINK, + PresetDecorators.OBJECT_LINK, + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.STORAGE_LINK, + PresetDecorators.WATCH, + PresetDecorators.LOCAL_STORAGE_LINK, + PresetDecorators.REQUIRE, +]; + +const v2Decorators: string[] = [ + PresetDecorators.PARAM, + PresetDecorators.ONCE, + PresetDecorators.EVENT, + PresetDecorators.PROVIDER, + PresetDecorators.CONSUMER, + PresetDecorators.MONITOR, + PresetDecorators.REQUIRE, +]; + +class StructPropertyDecoratorRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidStaticUsage: `The static variable of struct cannot be used together with built-in annotations.` + }; } - }); -} -const rule: UISyntaxRule = { - name: 'struct-property-decorator', - messages: { - invalidStaticUsage: `Static variables in custom components cannot be decorated by built-in variable decorators.` - }, - setup(context) { - return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; + public parsed(node: arkts.AstNode): void { + if (arkts.isStructDeclaration(node)) { + const hasComponentV1 = hasAnnotation(node.definition.annotations, PresetDecorators.COMPONENT_V1); + const hasComponentV2 = hasAnnotation(node.definition.annotations, PresetDecorators.COMPONENT_V2); + this.checkInvalidStaticPropertyDecorations(node, hasComponentV1, hasComponentV2); + if (hasComponentV2) { + this.checkInvalidStaticMethodDecorations(node); + } + } + if (arkts.isClassDeclaration(node)) { + this.checkInvalidStaticMethodDecorations(node); } - checkInvalidStaticPropertyDecorations(context, node); - }, - }; - }, -}; + } + + private hasPropertyDecorator( + member: arkts.ClassProperty, + decorators: String[] + ): boolean { + const annotationName = getClassPropertyAnnotationNames(member); + return decorators.some(decorator => + annotationName.includes(decorator) + ); + } + + private checkInvalidStaticPropertyDecorations( + node: arkts.StructDeclaration, + hasComponentV1: boolean, + hasComponentV2: boolean + ): void { + node.definition.body.forEach((member) => { + // Errors are reported when the node type is static ClassProperty, + if (!arkts.isClassProperty(member) || !member.key || !member.isStatic) { + return; + } + if (hasComponentV1 && this.hasPropertyDecorator(member, v1Decorators) || + hasComponentV2 && this.hasPropertyDecorator(member, v2Decorators)) { + const propertyNameNode = member.key; + this.report({ + node: propertyNameNode, + message: this.messages.invalidStaticUsage + }); + } + }); + } + + private checkInvalidStaticMethodDecorations(node: arkts.ClassDeclaration | arkts.StructDeclaration): void { + node.definition?.body.forEach((member) => { + // Errors are reported when the node type is static Method, + if (!arkts.isMethodDefinition(member) || !member.name || !member.isStatic) { + return; + } + const hasMonitor = member.funcExpr.scriptFunction.annotations.some(annotation => { + if (!annotation.expr || !arkts.isIdentifier(annotation.expr)) { + return false; + } + return annotation.expr.name === PresetDecorators.MONITOR; + }); + if (!hasMonitor) { + return; + } + const propertyNameNode = member.name; + this.report({ + node: propertyNameNode, + message: this.messages.invalidStaticUsage + }); + }); + } +} -export default rule; \ No newline at end of file +export default StructPropertyDecoratorRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/struct-property-optional.ts b/arkui-plugins/ui-syntax-plugins/rules/struct-property-optional.ts new file mode 100644 index 000000000..899df9874 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-property-optional.ts @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { PresetDecorators, isClassPropertyOptional } from '../utils/index'; + +// @Prop needs to consider whether there is an initialization value +const requireDecorators = [ + PresetDecorators.LINK, + PresetDecorators.OBJECT_LINK, +]; + +class StructPropertyOptionalRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + propertyOptional: `The '{{decoratorName}}' property '{{propertyName}}' cannot be an optional parameter.`, + }; + } + + public parsed(node: arkts.AstNode): void { + // Check if it's a class property + if (!arkts.isClassProperty(node)) { + return; + } + if (!node.key || !arkts.isIdentifier(node.key)) { + return; + } + const keyname = node.key.name; + // If the property is optional, check the decorator further + if (!isClassPropertyOptional(node)) { + return; + } + this.hasPropOrRequireDecorator(node, keyname); + } + + private hasPropOrRequireDecorator(node: arkts.ClassProperty, propertyName: string): void { + node.annotations?.forEach(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr)) { + const decoratorName = annotation.expr?.name; + const nodeKey = node.key; + const nodeValue = node.value; + // Check whether the prop decorator has an initial value, and no alarm will be generated if there is an initial value + if (decoratorName === PresetDecorators.PROP_REF && nodeKey && !nodeValue) { + this.report({ + node: nodeKey, + message: this.messages.propertyOptional, + data: { + decoratorName, + propertyName, + }, + }); + } else if (requireDecorators.includes(decoratorName) && nodeKey) { + this.report({ + node: nodeKey, + message: this.messages.propertyOptional, + data: { + decoratorName, + propertyName, + }, + }); + } + } + }); + } +} + +export default StructPropertyOptionalRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/struct-variable-initialization.ts b/arkui-plugins/ui-syntax-plugins/rules/struct-variable-initialization.ts index 63c06587f..38cbf5e3f 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/struct-variable-initialization.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-variable-initialization.ts @@ -14,59 +14,95 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { PresetDecorators, findDecorator } from '../utils'; // A list of decorators that needs to be initialized locally -const mustInitializeDecorators = ['State', 'StorageLink', 'StorageProp', 'LocalStorageLink', 'LocalStorageProp', - 'Provide']; +const mustInitializeDecorators = [ + PresetDecorators.STATE, + PresetDecorators.STORAGE_LINK, + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.LOCAL_STORAGE_LINK, + PresetDecorators.PROVIDE, +]; // Disables a list of decorators that are initialized locally -const prohibitInitializeDecorators = ['Link', 'Consume', 'ObjectLink']; +const prohibitInitializeDecorators = [ + PresetDecorators.LINK, + PresetDecorators.OBJECT_LINK, +]; -function reportVariablesInitializationError(context: UISyntaxRuleContext, node: arkts.ClassProperty): void { - // Check whether the value field exists and whether it has been initialized - const valueExists = !!node.value; - // Iterate through each decorator and verify the initialization rules - node.annotations.forEach(annotation => { - const decoratorName = annotation.expr?.dumpSrc() ?? ''; - if (mustInitializeDecorators.includes(decoratorName)) { - // If it is a decorator that needs to be initialized - if (!valueExists) { - // If there is no initialization expression and there is no @Require, an error is reported - context.report({ - node: annotation, - message: rule.messages.mustBeInitializedLocally, - }); - } - } else if (prohibitInitializeDecorators.includes(decoratorName)) { - // If it is a decorator that prohibits initialization - if (valueExists) { - // If an initialization expression exists, an error is reported - context.report({ - node: annotation, - message: rule.messages.prohibitLocalInitialization, - }); - } +// When used with @Require, non-initialization is allowed +const requireCanReleaseMandatoryDecorators = [ + PresetDecorators.STATE, + PresetDecorators.PROVIDE, +]; + +class StructVariableInitializationRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + mustBeInitializedLocally: `The '@{{decoratorName}}' property must be specified a default value.`, + prohibitLocalInitialization: `The '@{{decoratorName}}' property cannot be specified a default value.`, + }; } - }); -} -const rule: UISyntaxRule = { - name: 'struct-variable-initialization', - messages: { - mustBeInitializedLocally: `Variables decorated by '@State', '@StorageLink', '@StorageProp', '@LocalStorageLink', '@LocalStorageProp' and '@Provide' must be initialized locally.`, - prohibitLocalInitialization: `Variables decorated by '@Link', '@Consume', and '@ObjectLink' cannot be initialized locally.` - }, - setup(context) { - return { - parsed: (node): void => { + public parsed(node: arkts.AstNode): void { // Check if the current node is a class attribute if (!arkts.isClassProperty(node)) { - return; + return; + } + this.reportVariablesInitializationError(node); + } + + private reportVariablesInitializationError(node: arkts.ClassProperty): void { + // Check whether the value field exists and whether it has been initialized + const valueExists = !!node.value; + // Check for the presence of require decorator + const hasRequire = findDecorator(node, PresetDecorators.REQUIRE); + node.annotations.some(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr) && + mustInitializeDecorators.includes(annotation.expr.name)) { + const decoratorName = annotation.expr.name; + this.reportInitializeError(decoratorName, valueExists, annotation, hasRequire); + return true; + } else if (annotation.expr && arkts.isIdentifier(annotation.expr) && + prohibitInitializeDecorators.includes(annotation.expr.name)) { + const decoratorName = annotation.expr.name; + this.reportProHibitInitializeError(decoratorName, valueExists, annotation); + return true; + } + return false; + }); + } + + private reportInitializeError(decoratorName: string, valueExists: boolean, + annotation: arkts.AnnotationUsage, hasRequire: arkts.AnnotationUsage | undefined): void { + // Used with @require allows non-initialization + if (hasRequire && requireCanReleaseMandatoryDecorators.includes(decoratorName)) { + return; + } + // If it is a decorator that needs to be initialized + if (!valueExists) { + // If there is no initialization expression and there is no @Require, an error is reported + this.report({ + node: annotation, + message: this.messages.mustBeInitializedLocally, + data: { decoratorName }, + }); + } + } + + private reportProHibitInitializeError(decoratorName: string, valueExists: boolean, + annotation: arkts.AnnotationUsage): void { + // If it is a decorator that prohibits initialization + if (valueExists) { + // If an initialization expression exists, an error is reported + this.report({ + node: annotation, + message: this.messages.prohibitLocalInitialization, + data: { decoratorName }, + }); } - reportVariablesInitializationError(context, node); - } - }; - } -}; + } +} -export default rule; \ No newline at end of file +export default StructVariableInitializationRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts index db909687b..a585241ec 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts @@ -14,133 +14,130 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; -import { PresetDecorators } from '../utils/index'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { PresetDecorators, findDecorator, getClassDeclarationAnnotation, getAnnotationUsage } from '../utils/index'; -const rule: UISyntaxRule = { - name: 'track-decorator-check', - messages: { - invalidTarget: `The '@Track' decorator can only be used on class member variables.`, - invalidClass: `The '@Track' decorator can only be used within a 'class' decorated with '@Observed'.` - }, - setup(context) { - return { - parsed: (node): void => { +class TrackDecoratorCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + trackOnClassMemberOnly: `The '@Track' annotation can decorate only member variables of a class.`, + trackMustUsedWithObserved: `'@Track' cannot be used with classes decorated by '@ObservedV2'. Use the '@Trace' annotation instead.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (arkts.isFunctionDeclaration(node) || + arkts.isVariableDeclaration(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node)) { + this.reportInvalidTrackDecoratorUsage(node); + } if (arkts.isStructDeclaration(node)) { - checkInvalidTrackAnnotations(context, node); + this.checkInvalidTrackAnnotations(node); } // Check if the current node is a class declaration if (arkts.isClassDeclaration(node)) { - checkTrackOnlyUsedWithObserved(context, node); + this.checkTrackUsedWithObservedV2(node); } - } - }; - } -}; - -function checkInvalidTrackAnnotations(context: UISyntaxRuleContext, node: arkts.StructDeclaration): void { - // Traverse all members of the struct body - node.definition.body.forEach((member) => { - // Check whether it is a member variable - if (arkts.isClassProperty(member)) { - const hasTrackDecorator = findClassPropertyAnnotation(member, PresetDecorators.TRACK); - // If a member variable is decorated with @Track, an error is reported immediately - if (hasTrackDecorator) { - reportInvalidTarget(context, hasTrackDecorator); - } } - // Check whether this is the method - if (arkts.isMethodDefinition(member)) { - const hasTrackDecorator = getMethodAnnotation(member, PresetDecorators.TRACK); - // If the method is decorated with @Track, an error is reported immediately - if (hasTrackDecorator) { - reportInvalidTarget(context, hasTrackDecorator); - } - } - },); -} -function checkTrackOnlyUsedWithObserved(context: UISyntaxRuleContext, node: arkts.ClassDeclaration): void { - // Check if the class is decorated with @Observed - const hasObservedDecorator = node.definition?.annotations?.find( - annotations => - annotations.expr && - arkts.isIdentifier(annotations.expr) && - annotations.expr.name === PresetDecorators.OBSERVED_V1 - ); - // Traverse all members of the body class - node.definition?.body.forEach((member) => { - // Check whether it is a class attribute - if (arkts.isClassProperty(member)) { - const hasTrackDecorator = findClassPropertyAnnotation(member, PresetDecorators.TRACK); - // If the class is not decorated with @Observed and has decorators, an error is reported - if (!hasObservedDecorator && hasTrackDecorator) { - reportInvalidClass(context, hasTrackDecorator); - } + private reportInvalidTrackDecoratorUsage( + node: arkts.FunctionDeclaration | arkts.VariableDeclaration | arkts.ScriptFunction | + arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration + ): void { + const trackDecorator = findDecorator(node, PresetDecorators.TRACK); + if (trackDecorator) { + this.reportInvalidTarget(trackDecorator); + } } - // Check whether this is the method - if (arkts.isMethodDefinition(member)) { - const hasTrackDecorator = getMethodAnnotation(member, PresetDecorators.TRACK); - // If the method is decorated with @Track, an error is reported immediately - if (hasTrackDecorator) { - reportInvalidTarget(context, hasTrackDecorator); - } + + private checkInvalidTrackAnnotations(node: arkts.StructDeclaration): void { + const trackAnnotation = getAnnotationUsage(node, PresetDecorators.TRACK); + if (trackAnnotation) { + this.reportInvalidTarget(trackAnnotation); + } + // Traverse all members of the struct body + node.definition.body.forEach((member) => { + // Check whether it is a member variable + if (arkts.isClassProperty(member)) { + const trackDecorator = findDecorator(member, PresetDecorators.TRACK); + // If a member variable is decorated with @Track, an error is reported immediately + if (trackDecorator) { + this.reportInvalidTarget(trackDecorator); + } + } + // Check whether this is the method + if (arkts.isMethodDefinition(member)) { + const trackDecorator = findDecorator(member.scriptFunction, PresetDecorators.TRACK); + // If the method is decorated with @Track, an error is reported immediately + if (trackDecorator) { + this.reportInvalidTarget(trackDecorator); + } + } + },); } - }); -} -function reportInvalidClass(context: UISyntaxRuleContext, hasTrackDecorator: arkts.AnnotationUsage): void { - context.report({ - node: hasTrackDecorator, - message: rule.messages.invalidClass, - fix: (hasTrackDecorator) => { - const startPosition = arkts.getStartPosition(hasTrackDecorator); - const endPosition = arkts.getEndPosition(hasTrackDecorator); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); -} + private checkTrackUsedWithObservedV2(node: arkts.ClassDeclaration): void { + // Check if the struct is decorated with @Observed + const observedV2Decorator = getClassDeclarationAnnotation(node, PresetDecorators.OBSERVED_V2); -function getMethodAnnotation( - node: arkts.MethodDefinition, - annotationName: string) - : arkts.AnnotationUsage | undefined { - return node.scriptFunction.annotations?.find( - annotation => - annotation.expr && - annotation.expr.dumpSrc() === annotationName - ); -} + // Traverse all members of the body class + node.definition?.body.forEach((member) => { + // Check whether it is a class attribute + if (arkts.isClassProperty(member)) { + const trackDecorator = findDecorator(member, PresetDecorators.TRACK); + // If the class is not decorated with @Observed and has decorators, an error is reported + if (observedV2Decorator && trackDecorator) { + this.reportInvalidClass(trackDecorator); + } + } + // Check whether this is the method + if (arkts.isMethodDefinition(member)) { + const trackDecorator = findDecorator(member.scriptFunction, PresetDecorators.TRACK); + // If the method is decorated with @Track, an error is reported immediately + if (trackDecorator) { + this.reportInvalidTarget(trackDecorator); + } + if (observedV2Decorator && trackDecorator) { + this.reportInvalidClass(trackDecorator); + } + } + }); + } -function findClassPropertyAnnotation( - node: arkts.ClassProperty, - annotationName: string) - : arkts.AnnotationUsage | undefined { - return node.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === annotationName - ); -} + private reportInvalidClass(trackDecorator: arkts.AnnotationUsage): void { + this.report({ + node: trackDecorator, + message: this.messages.trackMustUsedWithObserved, + fix: (trackDecorator) => { + let startPosition = trackDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = trackDecorator.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } -function reportInvalidTarget( - context: UISyntaxRuleContext, - node: arkts.AnnotationUsage) - : void { - context.report({ - node: node, - message: rule.messages.invalidTarget, - fix: (node) => { - const startPosition = arkts.getStartPosition(node); - const endPosition = arkts.getEndPosition(node); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); + private reportInvalidTarget(node: arkts.AnnotationUsage): void { + this.report({ + node: node, + message: this.messages.trackOnClassMemberOnly, + fix: (node) => { + let startPosition = node.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = node.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } } -export default rule; \ No newline at end of file +export default TrackDecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/type-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/type-decorator-check.ts deleted file mode 100644 index 79c60be2e..000000000 --- a/arkui-plugins/ui-syntax-plugins/rules/type-decorator-check.ts +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2025 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as arkts from '@koalaui/libarkts'; -import { getAnnotationName, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -function findTypeDecorator( - annotations: readonly arkts.AnnotationUsage[] -): arkts.AnnotationUsage | undefined { - let hasTypeDecorator = annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === PresetDecorators.TYPE - ); - return hasTypeDecorator; -} - -function hasDecoratorType( - node: arkts.ClassDeclaration, -): arkts.AnnotationUsage | undefined { - let hasTypeDecorator: arkts.AnnotationUsage | undefined; - node.definition?.body.forEach(member => { - if (arkts.isClassProperty(member) && member.annotations) { - hasTypeDecorator = findTypeDecorator(member.annotations); - } - }); - return hasTypeDecorator; -} - -// rule1: @Type can only be used for class -function checkTypeInStruct( - node: arkts.StructDeclaration, - context: UISyntaxRuleContext, -): void { - let hasTypeDecorator: arkts.AnnotationUsage | undefined; - node.definition?.body.forEach(member => { - if (arkts.isClassProperty(member) && member.annotations) { - hasTypeDecorator = findTypeDecorator(member.annotations); - reportInvalidTypeUsageInStruct(hasTypeDecorator, context); - } - }); -} - -function reportInvalidTypeUsageInStruct( - hasTypeDecorator: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - if (!hasTypeDecorator) { - return; - } - context.report({ - node: hasTypeDecorator, - message: rule.messages.invalidType, - fix: (hasTypeDecorator) => { - const startPosition = arkts.getStartPosition(hasTypeDecorator); - const endPosition = arkts.getEndPosition(hasTypeDecorator); - return { - range: [startPosition, endPosition], - code: '' - }; - } - }); -} - -// rule2: Conflict between @Type and @Observed -function checkObservedAndTypeConflict( - node: arkts.ClassDeclaration, - context: UISyntaxRuleContext -): void { - let hasTypeDecorator: arkts.AnnotationUsage | undefined; - node.definition?.annotations.forEach(member => { - const annotation = getAnnotationName(member); - hasTypeDecorator = hasDecoratorType(node); - if (annotation === PresetDecorators.OBSERVED_V1) { - reportObservedAndTypeDecoratorConflict(hasTypeDecorator, context); - } - }); -} - -function reportObservedAndTypeDecoratorConflict( - hasTypeDecorator: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - if (!hasTypeDecorator) { - return; - } - context.report({ - node: hasTypeDecorator, - message: rule.messages.invalidDecoratorWith, - fix: () => { - const startPosition = arkts.getStartPosition(hasTypeDecorator); - const endPosition = arkts.getEndPosition(hasTypeDecorator); - return { - range: [startPosition, endPosition], - code: '' - }; - } - }); -} - -// rule3: @TypeCannot be used for function members -function validateScriptFunctionForTypeDecorator( - node: arkts.ScriptFunction, - context: UISyntaxRuleContext -): void { - const hasTypeDecorator = findTypeDecorator(node.annotations); - reportInvalidTypeDecorator(hasTypeDecorator, context); -} - -function reportInvalidTypeDecorator( - hasTypeDecorator: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - if (!hasTypeDecorator) { - return; - } - context.report({ - node: hasTypeDecorator, - message: rule.messages.invalidTypeMember, - fix: (hasTypeDecorator) => { - const startPosition = arkts.getStartPosition(hasTypeDecorator); - const endPosition = arkts.getEndPosition(hasTypeDecorator); - return { - range: [startPosition, endPosition], - code: '' - }; - } - }); -} - -const rule: UISyntaxRule = { - name: 'type-decorator-check', - messages: { - invalidType: `The @Type decorator can only be used in 'class'.`, - invalidDecoratorWith: `The @Type decorator can not be used within a 'class' decorated with @Observed.`, - invalidTypeMember: `The @Type can decorate only member variables in a 'class'.` - }, - setup(context) { - return { - parsed: (node): void => { - // Check the decorator on the class - if (arkts.isClassDeclaration(node)) { - checkObservedAndTypeConflict(node, context); - } - if (arkts.isStructDeclaration(node)) { - checkTypeInStruct(node, context); - } - if (arkts.isScriptFunction(node) && node.annotations) { - validateScriptFunctionForTypeDecorator(node, context); - } - }, - }; - }, -}; -export default rule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/ui-consistent-check.ts b/arkui-plugins/ui-syntax-plugins/rules/ui-consistent-check.ts new file mode 100644 index 000000000..695b3579f --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/ui-consistent-check.ts @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { getConsistentResourceInfo } from '../utils'; + +class UiConsistentCheckRule extends AbstractUISyntaxRule { + // UI consistency is only detect in the limited decorator + private static readonly specificDecorators = ['Builder', 'Extend', 'AnimatableExtend']; + // The attributes of the VP unit must be used + private static readonly checkVpProperties = ['padding']; + // The property of VP/PX units must be used + private static readonly checkVpAndPxProperties = [ + 'borderWidth', 'borderRadius', 'outlineWidth', 'outlineRadius' + ]; + // The types of colors allowed + private static readonly colorUnionTypes = ['Color', 'Resource', 'string']; + // Resource color type tags + private static readonly resourceColorType = 'ResourceColor'; + + private static readonly consistentResourceInfo = getConsistentResourceInfo(); + + public setup(): Record { + return { + colorConsistentWarning: 'It is recommended that you use layered parameters for easier color mode switching and theme color changing.', + vpSizeWarning: 'It is recommended that you use layered parameters for polymorphism development and resolution adaptation.', + vpAndPxSizeWarning: 'It is recommended that you use layered parameters for polymorphism development and resolution adaptation.', + }; + } + + public parsed(node: arkts.StructDeclaration): void { + // Specific Attributes: Check the VP units + this.checkVpUnit(node); + // Specific attributes: Check the VP and PX units + this.checkVpAndPxUnit(node); + // Check the color parameter formatting + this.checkColorParams(node); + } + + private isHexColor(color: string): boolean { + return /^(#([0-9A-F]{3}|[0-9A-F]{4}|[0-9A-F]{6}|[0-9A-F]{8}))|(0x[0-9A-F]*)$/i.test( + color, + ); + } + + private isValidPx(size: string): boolean { + return /^\d+(\.\d+)?px$/.test(size); + } + + private isValidVp(size: string): boolean { + return /^\d+(\.\d+)?vp$/.test(size); + } + + private convertToDecimalPx(size: string): string { + // Remove meaningless 0, eg: '12.00px' + const formatSize = `${parseFloat(size)}${size.endsWith('px') ? 'px' : 'vp' + }`; + // Regular expressions match numbers and units (e.g. px) + const regex = /^(\d+)(px|vp)$/; + const match = regex.exec(formatSize); + + if (match) { + // Get the numerical part and the unit part + const numberPart = match[1]; + const unitPart = match[2]; + + // Add a decimal point and a zero if there is no decimal point after the original number + const decimalSize = `${parseFloat(numberPart).toFixed(1)}${unitPart}`; + + return decimalSize; + } else { + // If there is no match, the original string is returned + return size; + } + } + + // Check whether it is in the UI component + private isInUIComponent(node: arkts.AstNode): boolean | undefined { + if (!node) { + return false; + } + let curNode = node; + try { + while (!arkts.isStructDeclaration(curNode)) { + if (!curNode.parent) { + return false; + } + curNode = curNode.parent; + } + return true; + } catch (error) { + return false; + } + } + + // Whether or not it is decorated by a specific decorator + private isInSpecificDecorators(node: arkts.AstNode): boolean { + if (!node) { + return false; + } + let curNode = node; + try { + while (!arkts.isFunctionDeclaration(curNode)) { + if (!curNode.parent) { + return false; + } + curNode = curNode.parent; + } + const annotations = arkts.getAnnotations(curNode); + return annotations.some(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name && UiConsistentCheckRule.specificDecorators.includes(annotation.expr.name) + ); + } catch (error) { + return false; + } + } + + private isColorProperty(propertyName: string): boolean { + // If the attribute name contains 'ResourceColor', 'Color', 'Resource', 'string', it is maybe a color property + return propertyName.includes(UiConsistentCheckRule.resourceColorType) || + UiConsistentCheckRule.colorUnionTypes.some(str => propertyName.includes(str)); + } + + private checkVpUnit(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !node.expression || + !arkts.isMemberExpression(node.expression) || !node.expression.property) { + return; + } + // Verify the attribute whose unit is VP + if (!arkts.isIdentifier(node.expression.property) || + !UiConsistentCheckRule.checkVpProperties.includes(node.expression.property.name)) { + return; + } + // Only the content under the UI component and the special decorator is verified + if (!this.isInUIComponent(node.expression.property) && + !this.isInSpecificDecorators(node.expression.property)) { + return; + } + // Gets the attribute value text and verifies the formatting + const sizeParams = node.arguments.filter( + argNode => arkts.isStringLiteral(argNode) && this.isValidVp(argNode.str) + ); + sizeParams.forEach(argNode => { + const resources = + UiConsistentCheckRule.consistentResourceInfo.get((argNode as arkts.StringLiteral).str) ?? + UiConsistentCheckRule.consistentResourceInfo.get( + this.convertToDecimalPx((argNode as arkts.StringLiteral).str) + ); + + // If consistent resource information doesn't exist, it won't be fixed + if (!resources || resources.length < 1) { + return; + } + const propertyValue = (argNode as arkts.StringLiteral).str; + const targetValue = `$r('sys.float.${resources[0].resourceName}')`; + this.report({ + node: argNode, + message: this.messages.vpSizeWarning, + fix: () => { + return { + title: `change ${propertyValue} to ${targetValue}`, + range: [argNode.startPosition, argNode.endPosition], + code: `${targetValue}`, + }; + } + }); + }); + } + + private checkVpAndPxUnit(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !node.expression || + !arkts.isMemberExpression(node.expression) || !node.expression.property) { + return; + } + // Verify the attribute whose unit is VP or PX + if (!arkts.isIdentifier(node.expression.property) || + !UiConsistentCheckRule.checkVpAndPxProperties.includes(node.expression.property.name)) { + return; + } + // Only the content under the UI component and the special decorator is verified + if (!this.isInUIComponent(node.expression.property) && + !this.isInSpecificDecorators(node.expression.property)) { + return; + } + // Gets the attribute value text and verifies the formatting + const sizeParams = node.arguments.filter( + argNode => arkts.isStringLiteral(argNode) && (this.isValidVp(argNode.str) || this.isValidPx(argNode.str)) + ); + sizeParams.forEach(argNode => { + const resources = + UiConsistentCheckRule.consistentResourceInfo.get((argNode as arkts.StringLiteral).str) ?? + UiConsistentCheckRule.consistentResourceInfo.get( + this.convertToDecimalPx((argNode as arkts.StringLiteral).str) + ); + + // If consistent resource information doesn't exist, it won't be fixed + if (!resources || resources.length < 1) { + return; + } + const propertyValue = (argNode as arkts.StringLiteral).str; + const targetValue = `$r('sys.float.${resources[0].resourceName}')`; + this.report({ + node: argNode, + message: this.messages.vpAndPxSizeWarning, + fix: () => { + return { + title: `change ${propertyValue} to ${targetValue}`, + range: [argNode.startPosition, argNode.endPosition], + code: `${targetValue}`, + }; + } + }); + }); + } + + private checkColorParams(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !node.expression || + !arkts.isMemberExpression(node.expression) || !node.expression.property) { + return; + } + // Verify the attribute whose type is Color + if (!arkts.isIdentifier(node.expression.property) || + !this.isColorProperty(node.expression.property.name)) { + return; + } + // Only the content under the UI component and the special decorator is verified + if (!this.isInUIComponent(node.expression.property) && + !this.isInSpecificDecorators(node.expression.property)) { + return; + } + // Gets the attribute value text and verifies the formatting + const colorParams = node.arguments.filter( + argNode => arkts.isStringLiteral(argNode) && this.isHexColor(argNode.str) + ); + colorParams.forEach(argNode => { + const resources = + UiConsistentCheckRule.consistentResourceInfo.get((argNode as arkts.StringLiteral).str) ?? + UiConsistentCheckRule.consistentResourceInfo.get((argNode as arkts.StringLiteral).str.toUpperCase()); + + // If consistent resource information doesn't exist, it won't be fixed + if (!resources || resources.length < 1) { + return; + } + const propertyValue = (argNode as arkts.StringLiteral).str; + const targetValue = `$r('sys.color.${resources[0].resourceName}')`; + this.report({ + node: argNode, + message: this.messages.colorConsistentWarning, + fix: () => { + return { + title: `change ${propertyValue} to ${targetValue}`, + range: [argNode.startPosition, argNode.endPosition], + code: `${targetValue}`, + }; + } + }); + }); + } +} + +export default UiConsistentCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts b/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts index c71a7c298..817d5ddd0 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts @@ -14,30 +14,75 @@ */ import * as arkts from '@koalaui/libarkts'; +import { ProjectConfig } from 'common/plugin-context'; +import { UISyntaxRuleComponents } from 'ui-syntax-plugins/utils'; export type FixSuggestion = { - range: [start: arkts.SourcePosition, end: arkts.SourcePosition]; - code: string; + range: [start: arkts.SourcePosition, end: arkts.SourcePosition]; + title?: string; + code: string; }; export type ReportOptions = { - node: arkts.AstNode; - message: string; - data?: Record; - fix?: (node: arkts.AstNode) => FixSuggestion; + node: arkts.AstNode; + message: string; + data?: Record; + fix?: (node: arkts.AstNode) => FixSuggestion; + level?: UISyntaxRuleLevel; }; export type UISyntaxRuleContext = { - report(options: ReportOptions): void; - containerComponents?: Set | undefined; + projectConfig?: ProjectConfig; + componentsInfo: UISyntaxRuleComponents | undefined; + report(options: ReportOptions): void; + getMainPages(): string[]; }; -export type UISyntaxRuleHandler = (node: arkts.AstNode) => void; +export type UISyntaxRulePhaseHandler = (node: arkts.AstNode) => void; + +export type UISyntaxRuleHandler = { + parsed?: UISyntaxRulePhaseHandler; + checked?: UISyntaxRulePhaseHandler; +}; export type UISyntaxRule = { - name: string; - messages: Record; - setup(context: UISyntaxRuleContext): { - parsed?: UISyntaxRuleHandler; - }; + name: string; + messages: Record; + setup(context: UISyntaxRuleContext): UISyntaxRuleHandler; +}; + +export type UISyntaxRuleReportOptions = { + node: arkts.AstNode; + message: string; + data?: Record; + fix?: (node: arkts.AstNode) => FixSuggestion; }; + +export type UISyntaxRuleLevel = 'error' | 'warn' | 'none'; + +export interface UISyntaxRuleConstructor { + new(context: UISyntaxRuleContext, level: UISyntaxRuleLevel): AbstractUISyntaxRule; +} + +export abstract class AbstractUISyntaxRule { + protected messages: Record; + + constructor(protected context: UISyntaxRuleContext, protected level: UISyntaxRuleLevel) { + this.messages = this.setup(); + } + + public beforeTransform(): void { } + public afterTransform(): void { } + public parsed(node: arkts.AstNode): void { } + public checked(node: arkts.AstNode): void { } + public abstract setup(): Record; + + protected report(options: UISyntaxRuleReportOptions): void { + this.context.report({ + ...options, + level: this.level, + }); + } +} + +export type UISyntaxRuleConfig = [UISyntaxRuleConstructor, UISyntaxRuleLevel]; diff --git a/arkui-plugins/ui-syntax-plugins/rules/validate-build-in-struct.ts b/arkui-plugins/ui-syntax-plugins/rules/validate-build-in-struct.ts index 938c3a210..d187edd52 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/validate-build-in-struct.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/validate-build-in-struct.ts @@ -14,122 +14,153 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getIdentifierName } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getIdentifierName, BUILD_NAME } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; const NOT_PARAM_LENGTH: number = 0; -const BUILD_NAME: string = 'build'; const BUILD_FUNCTION_COUNT_INI: number = 0; const BUILD_FUNCTION_COUNT: number = 1; const NOT_STATEMENT_LENGTH: number = 0; -// rule1: Check if the build function contains arguments and report an error -function validateBuildFunctionParameters(buildFunction: arkts.MethodDefinition, context: UISyntaxRuleContext): void { - const paramsNodes = buildFunction.scriptFunction.params; - if (paramsNodes.length > NOT_PARAM_LENGTH) { - paramsNodes.forEach((param) => { - if (arkts.isEtsParameterExpression(param)) { - reportBuildParamNotAllowed(param, context); - } - }); - } -} +class ValidateBuildInStructRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidComponent: `The 'build' method can not have arguments.`, + invalidBuild: `The struct '{{structName}}' must have at least and at most one 'build' method.`, + }; + } -// Report an error with an unallowed parameter in the build function -function reportBuildParamNotAllowed( - param: arkts.ETSParameterExpression, - context: UISyntaxRuleContext -): void { - context.report({ - node: param, - message: rule.messages.invalidComponet, - fix: (param) => { - const startPosition = arkts.getStartPosition(param); - const endPosition = arkts.getEndPosition(param); - return { - range: [startPosition, endPosition], - code: '' - }; + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + let buildFunctionCount: number = BUILD_FUNCTION_COUNT_INI; + this.validateBuild(node, buildFunctionCount); } - }); -} -function validateConstructorForBuildFunction( - node: arkts.StructDeclaration, - member: arkts.MethodDefinition, - buildFunctionCount: number, - context: UISyntaxRuleContext -): void { - const blockStatement = member.scriptFunction.body; - if (!blockStatement || !arkts.isBlockStatement(blockStatement)) { - return; - } - const statements = blockStatement.statements; - const structName = node.definition.ident; - if (buildFunctionCount !== BUILD_FUNCTION_COUNT && - statements.length === NOT_STATEMENT_LENGTH) { - reportMissingBuildInStruct(structName, blockStatement, context); - } -} + private validateBuild( + node: arkts.StructDeclaration, + buildFunctionCount: number + ): void { + node.definition.body.forEach((member) => { + // Check if the member is defined for the method and the method name is 'build' + if (arkts.isMethodDefinition(member) && arkts.isIdentifier(member.name) && getIdentifierName(member.name) === BUILD_NAME) { + buildFunctionCount++; + this.validateBuildFunctionParameters(member); + this.validateDuplicateBuild(buildFunctionCount, member); + } + // rule2: This rule validates the use of the 'build' function + if (arkts.isMethodDefinition(member) && + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR === member.kind) { + this.validateConstructorForBuildFunction(node, member, buildFunctionCount); + } + }); + } -function reportMissingBuildInStruct( - structName: arkts.Identifier | undefined, - blockStatement: arkts.BlockStatement, - context: UISyntaxRuleContext -): void { - if (!structName) { - return; - } - context.report({ - node: structName, - message: rule.messages.invalidBuild, - fix: (structName) => { - const startPosition = arkts.getStartPosition(blockStatement); - const endPosition = startPosition; - return { - range: [startPosition, endPosition], - code: '{\nbuild {\n}' - }; + // rule1: Check if the build function contains arguments and report an error + private validateBuildFunctionParameters(buildFunction: arkts.MethodDefinition): void { + const paramsNodes = buildFunction.scriptFunction.params; + if (paramsNodes.length > NOT_PARAM_LENGTH) { + paramsNodes.forEach((param) => { + if (arkts.isEtsParameterExpression(param)) { + this.reportBuildParamNotAllowed(param); + } + }); + } } - }); -} -function validateBuild( - node: arkts.StructDeclaration, - buildFunctionCount: number, - context: UISyntaxRuleContext, -): void { - node.definition.body.forEach((member) => { - // Check if the member is defined for the method and the method name is 'build' - if (arkts.isMethodDefinition(member) && getIdentifierName(member.name) === BUILD_NAME) { - buildFunctionCount++; - validateBuildFunctionParameters(member, context); + private validateDuplicateBuild( + buildFunctionCount: number, + member: arkts.MethodDefinition, + ): void { + if (buildFunctionCount > BUILD_FUNCTION_COUNT) { + const buildNode = member.scriptFunction.id; + if (!buildNode) { + return; + } + if (!arkts.isIdentifier(buildNode)) { + return; + } + this.report({ + node: member, + message: this.messages.invalidBuild, + data: { + structName: getIdentifierName(buildNode), + }, + fix: () => { + const startPosition = member.startPosition; + const endPosition = member.endPosition; + return { + title: 'Remove the duplicate build function.', + range: [startPosition, endPosition], + code: `` + }; + } + }); + } } - // rule2: This rule validates the use of the 'build' function - if (arkts.isMethodDefinition(member) && - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR === member.kind) { - validateConstructorForBuildFunction(node, member, buildFunctionCount, context); + + private validateConstructorForBuildFunction( + node: arkts.StructDeclaration, + member: arkts.MethodDefinition, + buildFunctionCount: number, + ): void { + const blockStatement = member.scriptFunction.body; + if (!blockStatement || !arkts.isBlockStatement(blockStatement)) { + return; + } + const statements = blockStatement.statements; + const structName = node.definition.ident; + if (buildFunctionCount === BUILD_FUNCTION_COUNT_INI && + statements.length === NOT_STATEMENT_LENGTH) { + this.reportMissingBuildInStruct(structName, node); + } } - }); -} -const rule: UISyntaxRule = { - name: 'validate-build-in-struct', - messages: { - invalidComponet: `A custom component can have only one 'build' function, which does not require parameters.`, - invalidBuild: `This rule validates the use of the 'build' function`, - }, - setup(context) { - return { - parsed: (node: arkts.AstNode): void => { - if (!arkts.isStructDeclaration(node)) { - return; + // Report an error with an unallowed parameter in the build function + private reportBuildParamNotAllowed( + param: arkts.ETSParameterExpression, + ): void { + this.report({ + node: param, + message: this.messages.invalidComponent, + fix: (param) => { + const startPosition = param.startPosition; + const endPosition = param.endPosition; + return { + title: 'Remove the parmeters of the build function', + range: [startPosition, endPosition], + code: '' + }; + } + }); + } + + private reportMissingBuildInStruct( + structName: arkts.Identifier | undefined, + node: arkts.StructDeclaration, + ): void { + if (!structName) { + return; } - let buildFunctionCount: number = BUILD_FUNCTION_COUNT_INI; - validateBuild(node, buildFunctionCount, context); - }, - }; - }, -}; + this.report({ + node: structName, + message: this.messages.invalidBuild, + data: { + structName: getIdentifierName(structName), + }, + fix: () => { + let startPosition = node.endPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = startPosition; + return { + title: 'Add a build function to the custom component', + range: [startPosition, endPosition], + code: 'build() {\n}\n' + }; + } + }); + } +} -export default rule; \ No newline at end of file +export default ValidateBuildInStructRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/validate-decorator-target.ts b/arkui-plugins/ui-syntax-plugins/rules/validate-decorator-target.ts new file mode 100644 index 000000000..d48cb7dd8 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/validate-decorator-target.ts @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +// Can only be used with decorators for struct +const structOnlyDecorators = [ + PresetDecorators.REUSABLE_V1, + PresetDecorators.REUSABLE_V2, + PresetDecorators.COMPONENT_V1, + PresetDecorators.COMPONENT_V2, + PresetDecorators.ENTRY, + PresetDecorators.PREVIEW, + PresetDecorators.CUSTOM_DIALOG, +]; + +// Can only be used with decorators for property +const propertyOnlyDecorators = [ + PresetDecorators.STATE, + PresetDecorators.PROP_REF, + PresetDecorators.LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.STORAGE_LINK, + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.LOCAL_STORAGE_LINK, + PresetDecorators.WATCH, + PresetDecorators.REQUIRE, + PresetDecorators.OBJECT_LINK, +]; + +class ValidateDecoratorTargetRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + decoratorOnlyWithStruct: `The '@{{decoratorName}}' annotation can only be used with 'struct'.`, + decoratorOnlyWithMemberProperty: `'@{{decoratorName}}' can only decorate member property.` + }; + } + + public parsed(node: arkts.AstNode): void { + this.validateDecoratorPropertyOnly(node); + + if (!arkts.isStructDeclaration(node)) { + this.validateDecoratorStructOnly(node); + } + } + + private validateDecoratorPropertyOnly( + node: arkts.AstNode, + ): void { + if (arkts.isScriptFunction(node) || arkts.isVariableDeclaration(node) || arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node)) { + node.annotations.forEach((annotation) => { + this.validateDecorator(annotation, propertyOnlyDecorators, this.messages.decoratorOnlyWithMemberProperty); + }); + } + } + + private validateDecoratorStructOnly(node: arkts.AstNode): void { + // class + if (arkts.isClassDeclaration(node)) { + node.definition?.annotations?.forEach((annotation) => { + this.validateDecorator(annotation, structOnlyDecorators, this.messages.decoratorOnlyWithStruct); + }); + } + // function/ variable/ method/ classproperty/ interface/ type alias declaration + if (arkts.isFunctionDeclaration(node) || + arkts.isVariableDeclaration(node) || + arkts.isClassProperty(node) || + arkts.isScriptFunction(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node) + ) { + node.annotations.forEach((annotation) => { + this.validateDecorator(annotation, structOnlyDecorators, this.messages.decoratorOnlyWithStruct); + }); + } + + // get /set method + if (arkts.isMethodDefinition(node) && + (node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET || + node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET)) { + node.scriptFunction.annotations.forEach((annotation) => { + this.validateDecorator(annotation, structOnlyDecorators, this.messages.decoratorOnlyWithStruct); + }); + } + } + + // decorator check function + private validateDecorator( + annotation: arkts.AnnotationUsage, + decorator: string[], + message: string, + ): void { + if (annotation.expr && arkts.isIdentifier(annotation.expr)) { + if (decorator.includes(annotation.expr.name)) { + this.report({ + node: annotation, + message: message, + data: { + decoratorName: annotation.expr.name, + }, + }); + } + } + } +} + +export default ValidateDecoratorTargetRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-cons.ts b/arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-cons.ts deleted file mode 100644 index 7660e6624..000000000 --- a/arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-cons.ts +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (c) 2025 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as arkts from '@koalaui/libarkts'; -import { getIdentifierName, getClassPropertyAnnotationNames, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -// Define a function to add property data to the property map -function addProperty(propertyMap: Map>, structName: string, - propertyName: string, annotationName: string): void { - if (!propertyMap.has(structName)) { - propertyMap.set(structName, new Map()); - } - const structProperties = propertyMap.get(structName); - if (structProperties) { - structProperties.set(propertyName, annotationName); - } -} -// categorizePropertyBasedOnAnnotations -function checkPropertyByAnnotations( - item: arkts.AstNode, - structName: string, - mustInitMap: Map>, - cannotInitMap: Map> = new Map(), - mustInitArray: string[][], - cannotInitArray: string[][] -): void { - if (!arkts.isClassProperty(item)) { - return; - } - const propertyName: string = item.key?.dumpSrc() ?? ''; - if (item.annotations.length === 0 || propertyName === '') { - return; - } - const annotationArray: string[] = getClassPropertyAnnotationNames(item); - // If the member variable is decorated, it is added to the corresponding map - mustInitArray.forEach(arr => { - if (arr.every(annotation => annotationArray.includes(annotation))) { - const annotationName: string = arr[0]; - addProperty(mustInitMap, structName, propertyName, annotationName); - } - }); - cannotInitArray.forEach(arr => { - if (arr.every(annotation => annotationArray.includes(annotation))) { - const annotationName: string = arr[0]; - addProperty(cannotInitMap, structName, propertyName, annotationName); - } - }); -} - -function initMap( - node: arkts.AstNode, - mustInitMap: Map>, - cannotInitMap: Map> = new Map(), - mustInitArray: string[][], - cannotInitArray: string[][] -): void { - if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - return; - } - node.getChildren().forEach((member) => { - if (!arkts.isStructDeclaration(member)) { - return; - } - const structName: string = member.definition.ident?.name ?? ''; - if (structName === '') { - return; - } - member.definition?.body.forEach((item) => { - checkPropertyByAnnotations(item, structName, mustInitMap, cannotInitMap, mustInitArray, cannotInitArray); - }); - }); -} - -function getChildKeyNameArray(member: arkts.AstNode): string[] { - const childkeyNameArray: string[] = []; - member.getChildren().forEach((property) => { - if (arkts.isProperty(property)) { - const childkeyName = property.key?.dumpSrc() ?? ''; - if (childkeyName !== '') { - childkeyNameArray.push(childkeyName); - } - } - }); - return childkeyNameArray; -} - -function checkMustInitialize( - node: arkts.AstNode, - context: UISyntaxRuleContext, - mustInitMap: Map> -): void { - if (!arkts.isIdentifier(node)) { - return; - } - const structName: string = getIdentifierName(node); - if (!mustInitMap.has(structName)) { - return; - } - const parentNode: arkts.AstNode = node.parent; - if (!arkts.isCallExpression(parentNode)) { - return; - } - // Get all the properties of a record via StructName - const mustInitName: Map = mustInitMap.get(structName)!; - parentNode.arguments?.forEach((member) => { - const childkeyNameArray: string[] = getChildKeyNameArray(member); - mustInitName.forEach((value, key) => { - // If an attribute that must be initialized is not initialized, an error is reported - if (!childkeyNameArray.includes(key)) { - context.report({ - node: parentNode, - message: rule.messages.mustInitializeRule, - data: { - annotationName: value, - propertyName: key, - }, - }); - } - }); - }); -} - -function checkCannotInitialize( - node: arkts.AstNode, - context: UISyntaxRuleContext, - cannotInitMap: Map> -): void { - if (!arkts.isIdentifier(node)) { - return; - } - const structName: string = getIdentifierName(node); - if (!cannotInitMap.has(structName)) { - return; - } - const parentNode: arkts.AstNode = node.parent; - if (!arkts.isCallExpression(parentNode)) { - return; - } - // Get all the properties of a record via StructName - const cannotInitName: Map = cannotInitMap.get(structName)!; - parentNode.arguments.forEach((member) => { - member.getChildren().forEach((property) => { - if (!arkts.isProperty(property)) { - return; - } - if (!property.key) { - return; - } - const propertyName = property.key.dumpSrc(); - // If a property that cannot be initialized is initialized, an error is reported - if (cannotInitName.has(propertyName)) { - context.report({ - node: property.key, - message: rule.messages.cannotInitializeRule, - data: { - annotationName: cannotInitName.get(propertyName)!, - propertyName: propertyName, - }, - }); - } - }); - }); -} - -const rule: UISyntaxRule = { - name: 'variable-initialization-via-component-cons', - messages: { - mustInitializeRule: `'@{{annotationName}}' decorated '{{propertyName}}' must be initialized through the component constructor.`, - cannotInitializeRule: `'@{{annotationName}}' decorated '{{propertyName}}' cannot be initialized through the component constructor.`, - }, - setup(context) { - let mustInitMap: Map> = new Map(); - let cannotInitMap: Map> = new Map(); - const mustInitArray: string[][] = [ - [PresetDecorators.REQUIRE, PresetDecorators.PROP], - [PresetDecorators.REQUIRE, PresetDecorators.BUILDER_PARAM], - [PresetDecorators.LINK], - [PresetDecorators.OBJECT_LINK] - ]; - const cannotInitArray: string[][] = [ - [PresetDecorators.STORAGE_LINK], - [PresetDecorators.STORAGE_PROP], - [PresetDecorators.CONSUME], - [PresetDecorators.LOCAL_STORAGE_LINK], - [PresetDecorators.LOCAL_STORAGE_PROP] - ]; - return { - parsed: (node): void => { - initMap(node, mustInitMap, cannotInitMap, mustInitArray, cannotInitArray); - checkMustInitialize(node, context, mustInitMap); - checkCannotInitialize(node, context, cannotInitMap); - }, - }; - }, -}; - -export default rule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-constructor.ts b/arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-constructor.ts new file mode 100644 index 000000000..29c7209c6 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-constructor.ts @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { getIdentifierName, getClassPropertyAnnotationNames, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class VariableInitializationViaComponentConstructorRule extends AbstractUISyntaxRule { + private mustInitMap: Map> = new Map(); + private cannotInitMap: Map> = new Map(); + + private static readonly mustInitInConstructorDecorators: string[][] = [ + [PresetDecorators.REQUIRE], + [PresetDecorators.REQUIRE, PresetDecorators.STATE], + [PresetDecorators.REQUIRE, PresetDecorators.PROVIDE], + [PresetDecorators.REQUIRE, PresetDecorators.PROP_REF], + [PresetDecorators.REQUIRE, PresetDecorators.BUILDER_PARAM], + [PresetDecorators.REQUIRE, PresetDecorators.PARAM] + ]; + + private static readonly notAllowInitInConstructorDecorators: string[][] = [ + [PresetDecorators.STORAGE_LINK], + [PresetDecorators.STORAGE_PROP_REF], + [PresetDecorators.CONSUME], + [PresetDecorators.LOCAL_STORAGE_LINK], + ]; + + public setup(): Record { + return { + requireVariableInitializationViaComponentConstructor: `'@Require' decorated '{{varName}}' must be initialized through the component constructor.`, + disallowVariableInitializationViaComponentConstructor: `The '{{decoratorName}}' property '{{varName}}' in the custom component '{{customComponentName}}' cannot be initialized here (forbidden to specify).`, + }; + } + + public beforeTransform(): void { + this.mustInitMap = new Map(); + this.cannotInitMap = new Map(); + } + + public parsed(node: arkts.StructDeclaration): void { + this.initMap(node); + this.checkMustInitialize(node); + this.checkCannotInitialize(node); + } + + // Define a function to add property data to the property map + private addProperty( + propertyMap: Map>, + structName: string, + propertyName: string, + annotationName: string + ): void { + if (!propertyMap.has(structName)) { + propertyMap.set(structName, new Map()); + } + const structProperties = propertyMap.get(structName); + if (structProperties) { + structProperties.set(propertyName, annotationName); + } + } + // categorizePropertyBasedOnAnnotations + private checkPropertyByAnnotations(item: arkts.AstNode, structName: string): void { + if (!arkts.isClassProperty(item) || !item.key || !arkts.isIdentifier(item.key)) { + return; + } + const propertyName: string = item.key.name; + if (item.annotations.length === 0 || propertyName === '') { + return; + } + const annotationArray: string[] = getClassPropertyAnnotationNames(item); + // If the member variable is decorated, it is added to the corresponding map + VariableInitializationViaComponentConstructorRule.mustInitInConstructorDecorators.forEach(arr => { + if (arr.every(annotation => annotationArray.includes(annotation))) { + const annotationName: string = arr[0]; + this.addProperty(this.mustInitMap, structName, propertyName, annotationName); + } + }); + VariableInitializationViaComponentConstructorRule.notAllowInitInConstructorDecorators.forEach(arr => { + if (arr.every(annotation => annotationArray.includes(annotation))) { + const annotationName: string = arr[0]; + this.addProperty(this.cannotInitMap, structName, propertyName, annotationName); + } + }); + } + + private initMap(node: arkts.AstNode): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member)) { + return; + } + if (!member.definition || !member.definition.ident || !arkts.isIdentifier(member.definition.ident)) { + return; + } + const structName: string = member.definition.ident.name; + if (structName === '') { + return; + } + member.definition?.body.forEach((item) => { + this.checkPropertyByAnnotations(item, structName); + }); + }); + } + + private getChildKeyNameArray(node: arkts.CallExpression): string[] { + const childKeyNameArray: string[] = []; + node.arguments.forEach((member) => { + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property)) { + return; + } + if (!property.key || !arkts.isIdentifier(property.key)) { + return; + } + const childKeyName = property.key.name; + if (childKeyName !== '') { + childKeyNameArray.push(childKeyName); + } + }); + }); + return childKeyNameArray; + } + + private checkMustInitialize(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !node.expression) { + return; + } + if (!arkts.isIdentifier(node.expression)) { + return; + } + const structName: string = getIdentifierName(node.expression); + if (!this.mustInitMap.has(structName)) { + return; + } + // Get all the properties of a record via StructName + const mustInitProperty: Map = this.mustInitMap.get(structName)!; + const childKeyNameArray: string[] = this.getChildKeyNameArray(node); + // If an attribute that must be initialized is not initialized, an error is reported + mustInitProperty.forEach((value, key) => { + if (!childKeyNameArray.includes(key)) { + this.report({ + node: node, + message: this.messages.requireVariableInitializationViaComponentConstructor, + data: { + varName: key, + }, + }); + } + }); + } + + private checkCannotInitialize(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !node.expression) { + return; + } + if (!arkts.isIdentifier(node.expression)) { + return; + } + const structName: string = getIdentifierName(node.expression); + if (!this.cannotInitMap.has(structName)) { + return; + } + // Get all the properties of a record via StructName + const cannotInitName: Map = this.cannotInitMap.get(structName)!; + node.arguments.forEach((member) => { + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property)) { + return; + } + if (!property.key || !arkts.isIdentifier(property.key)) { + return; + } + const propertyName = property.key.name; + // If a property that cannot be initialized is initialized, an error is reported + if (cannotInitName.has(propertyName)) { + const propertyType: string = cannotInitName.get(propertyName)!; + this.report({ + node: property, + message: this.messages.disallowVariableInitializationViaComponentConstructor, + data: { + decoratorName: `@${propertyType}`, + varName: propertyName, + customComponentName: structName + }, + }); + } + }); + }); + } +} + +export default VariableInitializationViaComponentConstructorRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-function.ts b/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-function.ts index dae346a79..fe2135a6e 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-function.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-function.ts @@ -14,115 +14,181 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getIdentifierName, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getIdentifierName, isPrivateClassProperty, PresetDecorators, getClassPropertyName, findDecorator } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -// Gets the names of all methods in the struct -function getMethodNames(node: arkts.StructDeclaration): string[] { - const methodNames: string[] = []; - node.definition.body.forEach((member) => { - if (arkts.isMethodDefinition(member)) { - const methodName = getIdentifierName(member.name); - if (methodName) { - methodNames.push(methodName); - } +class WatchDecoratorFunctionRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidWatch: `'@watch' cannot be used with '{{parameterName}}'. Apply it only to parameters that correspond to existing methods.`, + stringOnly: `'@Watch' cannot be used with '{{parameterName}}'. Apply it only to 'string' parameters.`, + }; } - }); - return methodNames; -} -// Invalid @Watch decorator bugs are reported -function reportInvalidWatch( - member: arkts.ClassProperty, - methodName: string, - hasWatchDecorator: arkts.AnnotationUsage, - context: UISyntaxRuleContext -): void { - context.report({ - node: hasWatchDecorator, - message: rule.messages.invalidWatch, - data: { methodName }, - fix: () => { - const startPosition = arkts.getEndPosition(member); - const endPosition = arkts.getEndPosition(member); - return { - range: [startPosition, endPosition], - code: `\n${methodName}(){\n}`, - }; - }, - }); -} + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + // Get all method names + const methodNames = this.getMethodNames(node); + // Get a private variable + const privateNames = this.getPrivateNames(node); + this.validateWatch(node, methodNames, privateNames); + } -function validateWatchDecorator( - member: arkts.ClassProperty, - methodNames: string[], - hasWatchDecorator: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - member.annotations.forEach((annotation) => { - validateWatchProperty(annotation, member, methodNames, hasWatchDecorator, context); - }); -} + private getExpressionValue(parameters: arkts.Expression, privateNames: string[]): string { + const type = arkts.nodeType(parameters); + if (type === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_NUMBER_LITERAL) { + return parameters.dumpSrc(); // Try extracting the string representation with dumpSrc + } else if (type === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_BOOLEAN_LITERAL) { + return parameters.dumpSrc(); + } else if (type === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_NULL_LITERAL) { + return 'null'; + } else if (type === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_UNDEFINED_LITERAL) { + return 'undefined'; + } else if (arkts.isMemberExpression(parameters)) { + if (arkts.isIdentifier(parameters.property)) { + if (privateNames.includes(parameters.property.name)) { + return parameters.property.name; + } + } + } + return parameters.dumpSrc(); // By default, an empty string is returned + } -function validateWatchProperty( - annotation: arkts.AnnotationUsage, - member: arkts.ClassProperty, - methodNames: string[], - hasWatchDecorator: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - if ( - annotation.expr && - annotation.expr.dumpSrc() === PresetDecorators.WATCH - ) { - annotation.properties.forEach((element) => { - if (!arkts.isClassProperty(element)) { - return; - } - const methodName = element.value?.dumpSrc().slice(1, -1); - if (hasWatchDecorator && methodName && !methodNames.includes(methodName)) { - reportInvalidWatch(member, methodName, hasWatchDecorator, context); - } - }); - } + // Gets the names of all methods in the struct + private getMethodNames(node: arkts.StructDeclaration): string[] { + const methodNames: string[] = []; + node.definition.body.forEach((member) => { + if (arkts.isMethodDefinition(member) && arkts.isIdentifier(member.name)) { + const methodName = getIdentifierName(member.name); + if (methodName) { + methodNames.push(methodName); + } + } + }); + return methodNames; + } -} + private getPrivateNames(node: arkts.StructDeclaration): string[] { + const privateNames: string[] = []; + node.definition.body.forEach((member) => { + if (arkts.isClassProperty(member) && isPrivateClassProperty(member)) { + const privateName = getClassPropertyName(member); + if (privateName) { + privateNames.push(privateName); + } + } + }); + return privateNames; + } -function validateWatch( - node: arkts.StructDeclaration, - methodNames: string[], - context: UISyntaxRuleContext -): void { - node.definition.body.forEach(member => { - if (!arkts.isClassProperty(member)) { - return; + private validateWatch( + node: arkts.StructDeclaration, + methodNames: string[], + privateNames: string[] + ): void { + node.definition.body.forEach(member => { + if (!arkts.isClassProperty(member)) { + return; + } + const watchDecorator = findDecorator(member, PresetDecorators.WATCH); + // Determine whether it contains @watch decorators + this.validateWatchDecorator(member, methodNames, privateNames, watchDecorator); + }); } - const hasWatchDecorator = member.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === PresetDecorators.WATCH - ); - // Determine whether it contains @watch decorators - validateWatchDecorator(member, methodNames, hasWatchDecorator, context); - }); -} -const rule: UISyntaxRule = { - name: 'watch-decorator-function', - messages: { - invalidWatch: `The '@Watch' decorated parameter must be a callback '{{methodName}}' of a function in a custom component.`, - }, - setup(context) { - return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; + private validateWatchDecorator( + member: arkts.ClassProperty, + methodNames: string[], + privateNames: string[], + watchDecorator: arkts.AnnotationUsage | undefined + ): void { + member.annotations.forEach((annotation) => { + this.validateWatchProperty(annotation, member, methodNames, privateNames, watchDecorator); + }); + } + + private validateWatchProperty( + annotation: arkts.AnnotationUsage, + member: arkts.ClassProperty, + methodNames: string[], + privateNames: string[], + watchDecorator: arkts.AnnotationUsage | undefined + ): void { + if ( + !annotation.expr || + !arkts.isIdentifier(annotation.expr) || + annotation.expr.name !== PresetDecorators.WATCH + ) { + return; } - // Get all method names - const methodNames = getMethodNames(node); - validateWatch(node, methodNames, context); - }, - }; - }, -}; + annotation.properties.forEach((element) => { + if (!arkts.isClassProperty(element)) { + return; + } + if (!element.value) { + return; + } + if (!arkts.isStringLiteral(element.value)) { + if (!watchDecorator) { + return; + } + this.reportStringOnly(element.value, privateNames, watchDecorator); + return; + } + const parameterName = element.value.str; + if (watchDecorator && parameterName && !methodNames.includes(parameterName)) { + this.reportInvalidWatch(member, parameterName, watchDecorator); + } + }); + } + + // Invalid @Watch decorator bugs are reported + private reportInvalidWatch( + member: arkts.ClassProperty, + parameterName: string, + watchDecorator: arkts.AnnotationUsage + ): void { + this.report({ + node: watchDecorator, + message: this.messages.invalidWatch, + data: { parameterName }, + fix: () => { + const startPosition = member.endPosition; + const endPosition = member.endPosition; + return { + title: 'Add a watch function to the custom component', + range: [startPosition, endPosition], + code: `\n${parameterName}(){\n}`, + }; + }, + }); + } + + private reportStringOnly( + parameters: arkts.Expression | undefined, + privateNames: string[], + watchDecorator: arkts.AnnotationUsage + ): void { + if (!parameters) { + return; + } + this.report({ + node: watchDecorator, + message: this.messages.stringOnly, + data: { parameterName: this.getExpressionValue(parameters, privateNames) }, + fix: () => { + const startPosition = parameters.startPosition; + const endPosition = parameters.endPosition; + return { + title: 'Remove the parameters', + range: [startPosition, endPosition], + code: ``, + }; + }, + }); + } +} -export default rule; \ No newline at end of file +export default WatchDecoratorFunctionRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-regular.ts b/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-regular.ts index 22eb47c74..fc2c30f39 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-regular.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-regular.ts @@ -14,47 +14,46 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getClassPropertyAnnotationNames, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { findDecorator, getClassPropertyAnnotationNames, getClassPropertyName, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; const PROPERTY_ANNOTATION_NUM: number = 2; -function validateWatchDecorator(node: arkts.StructDeclaration, context: UISyntaxRuleContext): void { - node.definition.body.forEach(member => { - if (!arkts.isClassProperty(member)) { - return; +class WatchDecoratorRegularRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidWatch: `Regular variable '{{propertyName}}' can not be decorated with '@Watch'.`, + }; } - const hasWatchDecorator = member.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === PresetDecorators.WATCH - ); - const propertyAnnotationNames = getClassPropertyAnnotationNames(member); - // Determine if there are any decorations other than @watch decorations - // rule1: The @Watch decorator must be used with other decorators - if (hasWatchDecorator && propertyAnnotationNames.length < PROPERTY_ANNOTATION_NUM) { - context.report({ - node: hasWatchDecorator, - message: rule.messages.invalidWatch, - }); - } - }); -} -const rule: UISyntaxRule = { - name: 'watch-decorator-regular', - messages: { - invalidWatch: `The @Watch decorator must be used with other decorators.`, - }, - setup(context) { - return { - parsed: (node): void => { + public parsed(node: arkts.AstNode): void { if (!arkts.isStructDeclaration(node)) { - return; + return; } - validateWatchDecorator(node, context); - }, - }; - }, -}; + this.validateWatchDecorator(node); + } + + private validateWatchDecorator(node: arkts.StructDeclaration): void { + node.definition.body.forEach(member => { + if (!arkts.isClassProperty(member)) { + return; + } + const watchDecorator = findDecorator(member, PresetDecorators.WATCH); + const propertyAnnotationNames = getClassPropertyAnnotationNames(member); + const propertyName = getClassPropertyName(member); + // Determine if there are any decorations other than @watch decorations + // rule1: The @Watch decorator must be used with other decorators + if (propertyName && watchDecorator && propertyAnnotationNames.length < PROPERTY_ANNOTATION_NUM) { + this.report({ + node: watchDecorator, + message: this.messages.invalidWatch, + data: { + propertyName: propertyName + } + }); + } + }); + } +} -export default rule; +export default WatchDecoratorRegularRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/wrap-builder-check.ts b/arkui-plugins/ui-syntax-plugins/rules/wrap-builder-check.ts index 5f87ef3fc..a8f2da56f 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/wrap-builder-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/wrap-builder-check.ts @@ -14,96 +14,73 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getIdentifierName, getAnnotationName, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getIdentifierName, PresetDecorators, WRAP_BUILDER, getFunctionAnnotationUsage } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const WRAPBUILDER_NAME: string = 'wrapBuilder'; -// Collect all the function names that are decorated with @Builder -function collectBuilderFunctions(node: arkts.EtsScript, builderFunctionNames: string[]): void { - node.statements.forEach((statement) => { - if (!arkts.isFunctionDeclaration(statement)) { - return; +class StructNoExtendsRule extends AbstractUISyntaxRule { + private builderFunctionNames: string[] = []; + + public setup(): Record { + return { + invalidWrapBuilderCheck: 'The wrapBuilder\'s parameter should be @Builder function.', + }; } - const annotations = statement.annotations; - if (!annotations) { - return; + + public beforeTransform(): void { + this.builderFunctionNames = []; } - annotations.forEach((annotation) => { - const decoratorName = getAnnotationName(annotation); - // Find all the functions that are decorated with @Builder and note their names - if (!decoratorName.includes(PresetDecorators.BUILDER)) { - return; - } - const functionName = statement.scriptFunction.id?.name; - if (!functionName || builderFunctionNames.includes(functionName)) { - return; - } - builderFunctionNames.push(functionName); - }); - }); -} -// Verify that the wrapBuilder's arguments are decorated with @Builder -function validateWrapBuilderArguments( - member: arkts.ClassProperty, - context: UISyntaxRuleContext, - builderFunctionNames: string[] -): void { - member.getChildren().forEach((child) => { - if (!arkts.isCallExpression(child) || child.expression.dumpSrc() !== WRAPBUILDER_NAME) { - return; + public parsed(node: arkts.StructDeclaration): void { + this.collectBuilderFunctions(node); + this.validateWrapBuilderInIdentifier(node); } - let functionName: string | undefined; - child.arguments.forEach(firstArgument => { - if (arkts.isMemberExpression(firstArgument)) { - functionName = getIdentifierName(firstArgument.property); - } else if (arkts.isIdentifier(firstArgument)) { - functionName = firstArgument.name; - } - // Verify that wrapBuilder's arguments are decorated with @Builder - // rule1: The wrapBuilder accepts only a function decorated by '@Builder' - if (functionName && !builderFunctionNames.includes(functionName)) { - context.report({ - node: firstArgument, - message: rule.messages.invalidBuilderCheck, - }); - } - }); - }); -} -function validateWrapBuilder( - node: arkts.StructDeclaration, - builderFunctionNames: string[], - context: UISyntaxRuleContext -): void { - node.definition.body.forEach(member => { - if (!arkts.isClassProperty(member)) { - return; + // Collect all the function names that are decorated with @Builder + private collectBuilderFunctions(node: arkts.AstNode): void { + if (!arkts.isEtsScript(node)) { + return; + } + node.statements.forEach((statement) => { + if (!arkts.isFunctionDeclaration(statement)) { + return; + } + const buildDecoratorUsage = getFunctionAnnotationUsage(statement, PresetDecorators.BUILDER); + if (!buildDecoratorUsage) { + return; + } + const functionName = statement.scriptFunction.id?.name; + if (!functionName || functionName === '' || this.builderFunctionNames.includes(functionName)) { + return; + } + this.builderFunctionNames.push(functionName); + }); } - validateWrapBuilderArguments(member, context, builderFunctionNames); - }); -} -const rule: UISyntaxRule = { - name: 'wrap-builder-check', - messages: { - invalidBuilderCheck: 'The wrapBuilder accepts only a function decorated by @Builder.', - }, - setup(context) { - let builderFunctionNames: string[] = []; - return { - parsed: (node): void => { - if (arkts.isEtsScript(node)) { - collectBuilderFunctions(node, builderFunctionNames); + private validateWrapBuilderInIdentifier(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !node.expression) { + return; + } + // If the current node is not a wrap builder, return + if (!arkts.isIdentifier(node.expression) || getIdentifierName(node.expression) !== WRAP_BUILDER) { + return; } - if (!arkts.isStructDeclaration(node)) { - return; + let functionName: string = ''; + // Get the parameters of the wrap builder + node.arguments.forEach(argument => { + if (!arkts.isIdentifier(argument)) { + return; + } + functionName = argument.name; + }); + // If the function name is not empty and not decorated by the @builder, an error is reported + if (functionName === '' || !this.builderFunctionNames.includes(functionName)) { + const errorNode = node.arguments[0]; + this.report({ + node: errorNode, + message: this.messages.invalidWrapBuilderCheck, + }); } - validateWrapBuilder(node, builderFunctionNames, context); - }, - }; - }, -}; + } +} -export default rule; +export default StructNoExtendsRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/transformers/parsed-ui-syntax-linter-transformer.ts b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-transformer.ts similarity index 76% rename from arkui-plugins/ui-syntax-plugins/transformers/parsed-ui-syntax-linter-transformer.ts rename to arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-transformer.ts index d8c642ed2..0e86f8152 100644 --- a/arkui-plugins/ui-syntax-plugins/transformers/parsed-ui-syntax-linter-transformer.ts +++ b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-transformer.ts @@ -17,9 +17,13 @@ import * as arkts from '@koalaui/libarkts'; import { UISyntaxLinterVisitor } from './ui-syntax-linter-visitor'; export class ParsedUISyntaxLinterTransformer extends UISyntaxLinterVisitor { - visitor(node: arkts.AstNode): arkts.AstNode { - this.processor.parsed(node); - node = this.visitEachChild(node); - return node; - } + handle(node: arkts.AstNode): void { + this.processor.parsed(node); + } +} + +export class CheckedUISyntaxLinterTransformer extends UISyntaxLinterVisitor { + handle(node: arkts.AstNode): void { + this.processor.checked(node); + } } diff --git a/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-visitor.ts b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-visitor.ts index 356304f88..33e1c85f1 100644 --- a/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-visitor.ts +++ b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-visitor.ts @@ -13,11 +13,27 @@ * limitations under the License. */ +import * as arkts from '@koalaui/libarkts'; import { AbstractVisitor } from '../../common/abstract-visitor'; import { UISyntaxRuleProcessor } from '../processor'; export abstract class UISyntaxLinterVisitor extends AbstractVisitor { - constructor(protected processor: UISyntaxRuleProcessor) { - super(); - } + constructor(protected processor: UISyntaxRuleProcessor) { + super(); + } + + visitor(node: arkts.AstNode): arkts.AstNode { + this.handle(node); + node = this.visitEachChild(node); + return node; + } + + transform(node: arkts.AstNode): arkts.AstNode { + this.processor.beforeTransform(); + const transformedNode = this.visitor(node); + this.processor.afterTransform(); + return transformedNode; + } + + abstract handle(node: arkts.AstNode): void; } diff --git a/arkui-plugins/ui-syntax-plugins/utils/index.ts b/arkui-plugins/ui-syntax-plugins/utils/index.ts index 87ba336a7..0e3f34341 100644 --- a/arkui-plugins/ui-syntax-plugins/utils/index.ts +++ b/arkui-plugins/ui-syntax-plugins/utils/index.ts @@ -16,185 +16,542 @@ import * as arkts from '@koalaui/libarkts'; import * as fs from 'fs'; import * as path from 'path'; +import { UISyntaxRuleContext } from 'ui-syntax-plugins/rules/ui-syntax-rule'; + +export const EXCLUDE_EXTERNAL_SOURCE_PREFIXES: Array = [ + 'std', + 'escompat', + 'security', + 'application', + 'permissions', + 'bundleManager', + 'commonEvent', + 'global', + 'arkui', + /@arkts\..*/, + /@ohos\.*/, + /@system\..*/, + /@koalaui\./, + /ability\..*/, +]; + +export const BUILD_NAME: string = 'build'; + +export const SINGLE_CHILD_COMPONENT: number = 1; +export const MAX_ENTRY_DECORATOR_COUNT: number = 1; +export const MAX_PREVIEW_DECORATOR_COUNT: number = 10; + +export const COMPONENT_REPEAT: string = 'Repeat'; +export const TEMPLATE: string = 'template'; + +export const PresetType = { + STRING: 'string', + NUMBER: 'number', + BOOLEAN: 'boolean', + BIGINT: 'bigint', +}; + +export const forbiddenUseStateType: string[] = [ + 'Scroller', + 'SwiperScroller', + 'VideoController', + 'WebController', + 'CustomDialogController', + 'SwiperController', + 'TabsController', + 'CalendarController', + 'AbilityController', + 'XComponentController', + 'CanvasRenderingContext2D', + 'CanvasGradient', + 'ImageBitmap', + 'ImageData', + 'Path2D', + 'RenderingContextSettings', + 'OffscreenCanvasRenderingContext2D', + 'PatternLockController', + 'TextAreaController', + 'TextInputController', + 'TextTimerController', + 'SearchController', + 'RichEditorController', +]; export const PresetDecorators = { - BUILDER_PARAM: 'BuilderParam', - COMPONENT_V1: 'Component', - COMPONENT_V2: 'ComponentV2', - COMPUTED: 'Computed', - CONSUME: 'Consume', - CONSUMER: 'Consumer', - CUSTOM_DIALOG: 'CustomDialog', - ENTRY: 'Entry', - EVENT: 'Event', - PREVIEW: 'Preview', - STATE: 'State', - PARAM: 'Param', - PROP: 'Prop', - PROVIDE: 'Provide', - PROVIDER: 'Provider', - LINK: 'Link', - LOCAL: 'Local', - OBJECT_LINK: 'ObjectLink', - STORAGE_PROP: 'StorageProp', - STORAGE_LINK: 'StorageLink', - LOCAL_STORAGE_PROP: 'LocalStorageProp', - LOCAL_STORAGE_LINK: 'LocalStorageLink', - REQUIRE: 'Require', - REUSABLE_V1: 'Reusable', - REUSABLE_V2: 'ReusableV2', - OBSERVED_V1: 'Observed', - OBSERVED_V2: 'ObservedV2', - TYPE: 'Type', - WATCH: 'Watch', - BUILDER: 'Builder', - TRACK: 'Track', - TRACE: 'Trace', - ONCE: 'Once', - MONITOR: 'Monitor', - LOCAL_BUILDER: 'LocalBuilder', + TOGGLE: 'Toggle', + BUILDER_PARAM: 'BuilderParam', + COMPONENT_V1: 'Component', + COMPONENT_V2: 'ComponentV2', + COMPUTED: 'Computed', + CONSUME: 'Consume', + CONSUMER: 'Consumer', + CUSTOM_DIALOG: 'CustomDialog', + ENTRY: 'Entry', + EVENT: 'Event', + PREVIEW: 'Preview', + STATE: 'State', + PARAM: 'Param', + PROP_REF: 'PropRef', + PROVIDE: 'Provide', + PROVIDER: 'Provider', + LINK: 'Link', + LOCAL: 'Local', + OBJECT_LINK: 'ObjectLink', + STORAGE_PROP_REF: 'StoragePropRef', + STORAGE_LINK: 'StorageLink', + LOCAL_STORAGE_LINK: 'LocalStorageLink', + REQUIRE: 'Require', + REUSABLE_V1: 'Reusable', + REUSABLE_V2: 'ReusableV2', + OBSERVED_V1: 'Observed', + OBSERVED_V2: 'ObservedV2', + TYPE: 'Type', + WATCH: 'Watch', + BUILDER: 'Builder', + TRACK: 'Track', + TRACE: 'Trace', + ONCE: 'Once', + MONITOR: 'Monitor', + LOCAL_BUILDER: 'LocalBuilder', + REGULAR: 'regular', + VARIABLE: 'variable', + PARAMETER: 'parameter', + ANIMATABLE_EXTEND: 'AnimatableExtend', +}; + +export const TOGGLE_TYPE: string = 'ToggleType'; +export const TYPE: string = 'type'; +export const WRAP_BUILDER: string = 'wrapBuilder'; + +export const ToggleType = { + CHECKBOX: 'Checkbox', + BUTTON: 'Button', +}; + +export const ReuseConstants = { + REUSE: 'reuse', + REUSE_ID: 'reuseId', }; -const PUBLIC_PROPERTY_MODIFIERS: Number = 4; -const PROTECTED_PROPERTY_MODIFIERS: Number = 8; -const PRIVATE_PROPERTY_MODIFIERS: Number = 16; +export function isClassPropertyOptional(node: arkts.ClassProperty): boolean { + return arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL); +} + export function getIdentifierName(node: arkts.AstNode): string { - if (!arkts.isIdentifier(node)) { - throw new Error(`Except a Identifier type!`); - } - return node.name; + if (!arkts.isIdentifier(node)) { + return ''; + } + return node.name; } export function getAnnotationName(annotation: arkts.AnnotationUsage): string { - if (!annotation.expr) { - throw new Error(`The expr property does not exist!`); - } - return getIdentifierName(annotation.expr); + if (!annotation.expr) { + return ''; + } + return getIdentifierName(annotation.expr); } export function getAnnotationUsage( - declaration: arkts.StructDeclaration, - annotationName: string, + declaration: arkts.StructDeclaration, + annotationName: string ): arkts.AnnotationUsage | undefined { - return declaration.definition.annotations.find( - (annotation) => - annotation.expr && - arkts.isIdentifier(annotation.expr) && - annotation.expr.name === annotationName, - ); + return declaration.definition.annotations.find( + (annotation) => + annotation.expr && arkts.isIdentifier(annotation.expr) && annotation.expr.name === annotationName + ); } export function getClassAnnotationUsage( - declaration: arkts.ClassDeclaration, - annotationName: string, + declaration: arkts.ClassDeclaration, + annotationName: string ): arkts.AnnotationUsage | undefined { - if (!declaration.definition || !declaration.definition.annotations) { - return undefined; - } - return declaration.definition.annotations.find( - (annotation) => - annotation.expr && - ((arkts.isIdentifier(annotation.expr) && annotation.expr.name === annotationName) || - (arkts.isCallExpression(annotation.expr) && - arkts.isIdentifier(annotation.expr) && - annotation.expr.name === annotationName)) - ); + if (!declaration.definition || !declaration.definition.annotations) { + return undefined; + } + return declaration.definition.annotations.find( + (annotation) => + annotation.expr && + ((arkts.isIdentifier(annotation.expr) && annotation.expr.name === annotationName) || + (arkts.isCallExpression(annotation.expr) && + arkts.isIdentifier(annotation.expr) && + annotation.expr.name === annotationName)) + ); } +export function getClassPropertyName(property: arkts.ClassProperty): string | undefined { + if (!property.key) { + return undefined; + } + return getIdentifierName(property.key); +} +export function getClassPropertyType(property: arkts.ClassProperty): string | undefined { + return property.typeAnnotation?.dumpSrc(); +} + +export function getClassPropertyAnnotationNames(property: arkts.ClassProperty): string[] { + return property.annotations.map((annotation) => getAnnotationName(annotation)); +} -export function getClassPropertyName(property: arkts.ClassProperty): string { - return getIdentifierName(property.key); +export function getClassPropertyAnnotation( + property: arkts.ClassProperty, + decoratorName: string +): arkts.AnnotationUsage | undefined { + return property.annotations?.find(annotation => + annotation.expr && + arkts.isIdentifier(annotation.expr) && + annotation.expr.name === decoratorName + ); } -export function getClassPropertyType(property: arkts.ClassProperty): string { - return property.typeAnnotation.dumpSrc(); +export function getClassDeclarationAnnotation( + classDeclaration: arkts.ClassDeclaration, + decoratorName: string +): arkts.AnnotationUsage | undefined { + return classDeclaration.definition?.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === decoratorName + ); } -export function getClassPropertyAnnotationNames( - property: arkts.ClassProperty, -): string[] { - return property.annotations.map((annotation) => - getAnnotationName(annotation), - ); +export function findDecorator( + member: arkts.ClassProperty | arkts.VariableDeclaration | arkts.FunctionDeclaration | + arkts.ScriptFunction | arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration, + decoratorName: string +): arkts.AnnotationUsage | undefined { + return member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === decoratorName + ); } export function isPublicClassProperty(property: arkts.ClassProperty): boolean { - // todo 使用接口实现 - return property.modifiers === PUBLIC_PROPERTY_MODIFIERS; + return arkts.hasModifierFlag(property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC); } export function isPrivateClassProperty(property: arkts.ClassProperty): boolean { - // todo 使用接口实现 - return property.modifiers === PRIVATE_PROPERTY_MODIFIERS; + return arkts.hasModifierFlag(property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE); } -export function isProtectedlassProperty(property: arkts.ClassProperty): boolean { - // todo 使用接口实现 - return property.modifiers === PROTECTED_PROPERTY_MODIFIERS; +export function isProtectedClassProperty(property: arkts.ClassProperty): boolean { + return arkts.hasModifierFlag(property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PROTECTED); +} + +export function listToString(strList: string[]): string { + return strList.length > 1 ? `${strList.join(',')}` : strList.join(''); } export class MultiMap { - private readonly map: Map; - constructor() { - this.map = new Map(); - } - /** - * Add key-value pairs to MultiMap - * @param key key - * @param value value - */ - add(key: K, value: V): void { - if (!this.map.has(key)) { - this.map.set(key, []); - } - this.map.get(key)!.push(value); - } - - /** - * Gets all the values of the specified key - * @param key key - * @returns An array of values, which returns an empty array if the key does not exist - */ - get(key: K): V[] { - return this.map.get(key) || []; - } - - /** - * Check if the specified key exists in the MultiMap - * @param key key - * @returns Whether it exists - */ - has(key: K): boolean { - return this.map.has(key); - } + private readonly map: Map; + constructor() { + this.map = new Map(); + } + /** + * Add key-value pairs to MultiMap + * @param key key + * @param value value + */ + add(key: K, value: V): void { + if (!this.map.has(key)) { + this.map.set(key, []); + } + this.map.get(key)!.push(value); + } + + /** + * Gets all the values of the specified key + * @param key key + * @returns An array of values, which returns an empty array if the key does not exist + */ + get(key: K): V[] { + return this.map.get(key) || []; + } + + /** + * Check if the specified key exists in the MultiMap + * @param key key + * @returns Whether it exists + */ + has(key: K): boolean { + return this.map.has(key); + } +} + +export function hasAnnotation(annoArray: readonly arkts.AnnotationUsage[], annotationName: string): boolean { + return (annoArray || []).some((anno) => anno.expr && getIdentifierName(anno.expr) === annotationName); } interface ComponentJson { - name: string; - atomic?: boolean; - attrs: string[]; -} - -export function getContainerComponents(dirPath: string): Set { - const resultSet = new Set(); - const absolutePath = path.resolve(__dirname, dirPath); - - if (!fs.existsSync(absolutePath)) { - throw new Error(`Directory does not exist: ${absolutePath}`); - } - // Read all files in the directory - const files = fs.readdirSync(absolutePath); - - files.forEach(file => { - if (path.extname(file) === '.json') { - const filePath = path.join(absolutePath, file); - const fileContent = fs.readFileSync(filePath, 'utf-8'); - const componentJson: ComponentJson = JSON.parse(fileContent); - if ((!componentJson.atomic || componentJson.atomic !== true) && (componentJson.name)) { - resultSet.add(componentJson.name); - } - } - }); - return resultSet; + name: string; + atomic?: boolean; + attrs: string[]; + single?: boolean; + parents?: string[]; + children?: string[]; +} + +export interface UISyntaxRuleComponents { + builtInAttributes: string[]; + containerComponents: string[]; + atomicComponents: string[]; + singleChildComponents: string[]; + validParentComponent: Map; + validChildComponent: Map; +} + +export function getUIComponents(dirPath: string): UISyntaxRuleComponents | undefined { + const absolutePath = path.resolve(__dirname, dirPath); + let builtInAttributes: string[] = []; + let containerComponents: string[] = []; + let atomicComponents: string[] = []; + let singleChildComponents: string[] = []; + let validParentComponent: Map = new Map(); + let validChildComponent: Map = new Map(); + + if (!fs.existsSync(absolutePath)) { + return undefined; + } + // Read all files in the directory + const files = fs.readdirSync(absolutePath); + + files.forEach((file) => { + if (path.extname(file) === '.json') { + const filePath = path.join(absolutePath, file); + const fileContent = fs.readFileSync(filePath, 'utf-8'); + const componentJson: ComponentJson = JSON.parse(fileContent); + // Record the container component name + if ((!componentJson.atomic || componentJson.atomic !== true) && componentJson.name) { + containerComponents.push(componentJson.name); + } + // Record the atomic component name + if (componentJson.atomic && componentJson.atomic === true && componentJson.name) { + atomicComponents.push(componentJson.name); + } + // Record the name of a single subcomponent component name + if (componentJson.single && componentJson.single === true && componentJson.name) { + singleChildComponents.push(componentJson.name); + } + // Record a valid parent component name + if (componentJson.parents && componentJson.name) { + validParentComponent.set(componentJson.name, componentJson.parents); + } + // Record a valid children component name + if (componentJson.children && componentJson.name) { + validChildComponent.set(componentJson.name, componentJson.children); + } + // Document all built-in attributes + componentJson.attrs + ?.filter((attr) => !builtInAttributes.includes(attr)) + .forEach((attr) => builtInAttributes.push(attr)); + } + }); + const componentsInfo: UISyntaxRuleComponents = { + builtInAttributes, + containerComponents, + atomicComponents, + singleChildComponents, + validParentComponent, + validChildComponent, + }; + + return componentsInfo; +} + +export function getConsistentResourceInfo(): Map { + const resultMap = new Map(); + const consistentResourcePath = + path.resolve(__dirname, '../../../../../../../previewer/common/resources/entry/resources.txt'); + let resourceText: string = ''; + try { + // The contents of the file are read synchronously + resourceText = fs.readFileSync(path.resolve(consistentResourcePath), 'utf-8'); + } catch (error: unknown) { + return resultMap; + } + // Split text by line + const lines = resourceText.split('\n'); + for (const line of lines) { + // Skip blank lines + if (!line.trim()) { + continue; + } + const match = line.match(/id:(\d+),\s*'([^']+)'\s*'([^']+)'/); + if (match && match.length === 4) { + const id = match[1]; + const value = match[2]; + const resourceName = match[3]; + // Remove resource names that start with 'ohos_id' or 'ohos_fa' + if (resourceName.startsWith('ohos_id') || resourceName.startsWith('ohos_fa')) { + continue; + } + let entries = resultMap.get(value); + if (!entries) { + entries = []; + resultMap.set(value, entries); + } + entries.push({ + id: id, + resourceName: resourceName, + }); + } + } + return resultMap; +} + +export function isBuiltInAttribute(context: UISyntaxRuleContext, attributeName: string): boolean { + if (!context.componentsInfo) { + return false; + } + return context.componentsInfo.builtInAttributes.includes(attributeName); +} +export function isBuildInComponent(context: UISyntaxRuleContext, componentName: string): boolean { + if (!context.componentsInfo) { + return false; + } + return ( + context.componentsInfo.containerComponents.includes(componentName) || + context.componentsInfo.atomicComponents.includes(componentName) + ); +} + +export function isAtomicComponent(context: UISyntaxRuleContext, componentName: string): boolean { + if (!context.componentsInfo) { + return false; + } + return context.componentsInfo.atomicComponents.includes(componentName); +} + +export function isContainerComponent(context: UISyntaxRuleContext, componentName: string): boolean { + if (!context.componentsInfo) { + return false; + } + return context.componentsInfo.containerComponents.includes(componentName); +} + +export function isSingleChildComponent(context: UISyntaxRuleContext, componentName: string): boolean { + if (!context.componentsInfo) { + return false; + } + return context.componentsInfo.singleChildComponents.includes(componentName); +} + +export function readJSON(path: string): T | null { + if (!fs.existsSync(path)) { + return null; + } + const content = fs.readFileSync(path).toString(); + if (!content) { + return null; + } + return JSON.parse(content) as T; +} + +export function tracePerformance any>(name: string, fn: T): T { + return function (this: ThisParameterType, ...args: Parameters): ReturnType { + arkts.Performance.getInstance().createEvent(name); + const result = fn.apply(this, args); + arkts.Performance.getInstance().stopEvent(name, true); + return result; + } as T; +} + +const ANNOTATION_PRESET_MODULE_PREFIXES: string[] = ['arkui.', '@ohos.', '@kit.ArkUI']; + +export function isFromPresetModules(moduleName: string): boolean { + for (const presetModulePrefix of ANNOTATION_PRESET_MODULE_PREFIXES) { + if (moduleName.startsWith(presetModulePrefix)) { + return true; + } + } + return false; +} + +export function getAnnotationUsagesByName( + annotations: readonly arkts.AnnotationUsage[], + annotationNames: string[] +): Array { + return annotationNames.map((annotationName) => getAnnotationUsageByName(annotations, annotationName)); +} + +export function getAnnotationUsageByName( + annotations: readonly arkts.AnnotationUsage[], + annotationName: string +): arkts.AnnotationUsage | undefined { + return annotations.find((annotation: arkts.AnnotationUsage): boolean => { + if (!annotation.expr || !arkts.isIdentifier(annotation.expr) || annotation.expr.name !== annotationName) { + return false; + } + const annotationDeclaration = arkts.getDecl(annotation.expr); + if (!annotationDeclaration) { + return false; + } + const program = arkts.getProgramFromAstNode(annotationDeclaration); + if (!isFromPresetModules(program.moduleName)) { + return false; + } + return true; + }); +} + +export function isStructClassDeclaration(node: arkts.AstNode): node is arkts.ClassDeclaration { + return ( + arkts.isClassDeclaration(node) && !!node.definition && arkts.classDefinitionIsFromStructConst(node.definition) + ); +} + +export function getFunctionAnnotationUsage( + declaration: arkts.FunctionDeclaration, + annotationName: string, +): arkts.AnnotationUsage | undefined { + if (!declaration || !declaration.annotations) { + return undefined; + } + return declaration.annotations.find( + (annotation) => + annotation.expr && + ((arkts.isIdentifier(annotation.expr) && annotation.expr.name === annotationName) || + (arkts.isCallExpression(annotation.expr) && + arkts.isIdentifier(annotation.expr) && + annotation.expr.name === annotationName)) + ); +} + +export function getCallee(callExpression: arkts.CallExpression): arkts.Identifier | undefined { + const expression = callExpression.expression; + if (arkts.isIdentifier(expression)) { + return expression; + } + if (arkts.isMemberExpression(expression)) { + if (arkts.isCallExpression(expression.object)) { + return getCallee(expression.object); + } + } + return undefined; +} + +export const TypeFlags = { + Boolean: 'boolean', + String: 'string', + Number: 'number', + Enum: 'enum', + Null: 'null', + Undefined: 'undefined', + Object: 'object', + Array: 'array', + Function: 'function', + Symbol: 'symbol', + BigInt: 'bigint', + Unknown: 'unknown', + Any: 'any', + Never: 'never', + Void: 'void', + This: 'this', + TypeParameter: 'typeParameter', + Literal: 'literal', + Union: 'union', +}; + +export function getCurrentFilePath(node: arkts.AstNode): string | undefined { + const program = arkts.getProgramFromAstNode(node); + return program.absName; } \ No newline at end of file diff --git a/compiler/package-lock.json b/compiler/package-lock.json index 3aa764b4f..3eaad9295 100644 --- a/compiler/package-lock.json +++ b/compiler/package-lock.json @@ -1538,6 +1538,43 @@ "picomatch": "^2.3.1" } }, + "@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://repo.huaweicloud.com/repository/npm/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://repo.huaweicloud.com/repository/npm/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "requires": { + "@sinonjs/commons": "^3.0.0" + } + }, + "@sinonjs/samsam": { + "version": "8.0.3", + "resolved": "https://repo.huaweicloud.com/repository/npm/@sinonjs/samsam/-/samsam-8.0.3.tgz", + "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", + "requires": { + "@sinonjs/commons": "^3.0.1", + "type-detect": "^4.1.0" + }, + "dependencies": { + "type-detect": { + "version": "4.1.0", + "resolved": "https://repo.huaweicloud.com/repository/npm/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==" + } + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://repo.huaweicloud.com/repository/npm/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==" + }, "@types/eslint": { "version": "8.40.2", "resolved": "https://repo.huaweicloud.com/repository/npm/@types/eslint/-/eslint-8.40.2.tgz", @@ -2702,6 +2739,16 @@ "flat-cache": "^3.0.4" } }, + "fill-keys": { + "version": "1.0.2", + "resolved": "https://repo.huaweicloud.com/repository/npm/fill-keys/-/fill-keys-1.0.2.tgz", + "integrity": "sha512-tcgI872xXjwFF4xgQmLxi76GnwJG3g/3isB1l4/G5Z4zrbddGpBjqZCO9oEAcB5wX0Hj/5iQB3toxfO7in1hHA==", + "dev": true, + "requires": { + "is-object": "~1.0.1", + "merge-descriptors": "~1.0.0" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://repo.huaweicloud.com/repository/npm/fill-range/-/fill-range-7.0.1.tgz", @@ -3071,6 +3118,12 @@ "resolved": "https://repo.huaweicloud.com/repository/npm/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, + "is-object": { + "version": "1.0.2", + "resolved": "https://repo.huaweicloud.com/repository/npm/is-object/-/is-object-1.0.2.tgz", + "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==", + "dev": true + }, "is-path-cwd": { "version": "2.2.0", "resolved": "https://repo.huaweicloud.com/repository/npm/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -3207,6 +3260,11 @@ "graceful-fs": "^4.1.6" } }, + "just-extend": { + "version": "6.2.0", + "resolved": "https://repo.huaweicloud.com/repository/npm/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==" + }, "kind-of": { "version": "6.0.3", "resolved": "https://repo.huaweicloud.com/repository/npm/kind-of/-/kind-of-6.0.3.tgz", @@ -3317,6 +3375,12 @@ } } }, + "merge-descriptors": { + "version": "1.0.3", + "resolved": "https://repo.huaweicloud.com/repository/npm/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true + }, "merge-stream": { "version": "2.0.0", "resolved": "https://repo.huaweicloud.com/repository/npm/merge-stream/-/merge-stream-2.0.0.tgz", @@ -3501,6 +3565,12 @@ } } }, + "module-not-found-error": { + "version": "1.0.1", + "resolved": "https://repo.huaweicloud.com/repository/npm/module-not-found-error/-/module-not-found-error-1.0.1.tgz", + "integrity": "sha512-pEk4ECWQXV6z2zjhRZUongnLJNUeGQJ3w6OQ5ctGwD+i5o93qjRQUk2Rt6VdNeu3sEP0AB4LcfvdebpxBRVr4g==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://repo.huaweicloud.com/repository/npm/ms/-/ms-2.1.2.tgz", @@ -3523,6 +3593,28 @@ "resolved": "https://repo.huaweicloud.com/repository/npm/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "nise": { + "version": "5.1.9", + "resolved": "https://repo.huaweicloud.com/repository/npm/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", + "requires": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" + }, + "dependencies": { + "@sinonjs/fake-timers": { + "version": "11.3.1", + "resolved": "https://repo.huaweicloud.com/repository/npm/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz", + "integrity": "sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==", + "requires": { + "@sinonjs/commons": "^3.0.1" + } + } + } + }, "node-releases": { "version": "2.0.12", "resolved": "https://repo.huaweicloud.com/repository/npm/node-releases/-/node-releases-2.0.12.tgz", @@ -3631,6 +3723,11 @@ "resolved": "https://repo.huaweicloud.com/repository/npm/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "path-to-regexp": { + "version": "6.3.0", + "resolved": "https://repo.huaweicloud.com/repository/npm/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==" + }, "path-type": { "version": "4.0.0", "resolved": "https://repo.huaweicloud.com/repository/npm/path-type/-/path-type-4.0.0.tgz", @@ -3684,6 +3781,17 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "proxyquire": { + "version": "2.1.3", + "resolved": "https://repo.huaweicloud.com/repository/npm/proxyquire/-/proxyquire-2.1.3.tgz", + "integrity": "sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==", + "dev": true, + "requires": { + "fill-keys": "^1.0.2", + "module-not-found-error": "^1.0.1", + "resolve": "^1.11.1" + } + }, "punycode": { "version": "2.3.0", "resolved": "https://repo.huaweicloud.com/repository/npm/punycode/-/punycode-2.3.0.tgz", @@ -3930,6 +4038,26 @@ "resolved": "https://repo.huaweicloud.com/repository/npm/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, + "sinon": { + "version": "15.2.0", + "resolved": "https://repo.huaweicloud.com/repository/npm/sinon/-/sinon-15.2.0.tgz", + "integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", + "requires": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^10.3.0", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.4", + "supports-color": "^7.2.0" + }, + "dependencies": { + "diff": { + "version": "5.2.0", + "resolved": "https://repo.huaweicloud.com/repository/npm/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==" + } + } + }, "slash": { "version": "3.0.0", "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", @@ -4096,8 +4224,7 @@ "type-detect": { "version": "4.0.8", "resolved": "https://repo.huaweicloud.com/repository/npm/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" }, "type-fest": { "version": "0.20.2", diff --git a/koala-wrapper/koalaui/interop/src/cpp/crashdump.h b/koala-wrapper/koalaui/interop/src/cpp/crashdump.h index 7d1bac040..467e57abf 100644 --- a/koala-wrapper/koalaui/interop/src/cpp/crashdump.h +++ b/koala-wrapper/koalaui/interop/src/cpp/crashdump.h @@ -26,7 +26,9 @@ static void onCrashHandler(int signo) { void* stack[20]; size_t size = backtrace(stack, 20); backtrace_symbols_fd(stack, size, STDERR_FILENO); - if (oldCrashHandler) oldCrashHandler(signo); + if (oldCrashHandler) { + oldCrashHandler(signo); + } } static void installCrashHandlers() { diff --git a/koala-wrapper/koalaui/interop/src/cpp/ets/convertors-ets.cc b/koala-wrapper/koalaui/interop/src/cpp/ets/convertors-ets.cc index 103c8c7de..751326493 100644 --- a/koala-wrapper/koalaui/interop/src/cpp/ets/convertors-ets.cc +++ b/koala-wrapper/koalaui/interop/src/cpp/ets/convertors-ets.cc @@ -30,7 +30,7 @@ static bool registerNatives(ets_env *env, const ets_class clazz, const std::vect std::vector methods; methods.reserve(impls.size()); bool result = true; - for (const auto &[name, type, func, flag] : impls) { + for (const auto &[name, _, func, flag] : impls) { EtsNativeMethod method; method.name = name.c_str(); method.func = func; diff --git a/koala-wrapper/native/BUILD.gn b/koala-wrapper/native/BUILD.gn index a79c063ec..ab7989a90 100644 --- a/koala-wrapper/native/BUILD.gn +++ b/koala-wrapper/native/BUILD.gn @@ -25,6 +25,7 @@ shared_library("es2panda") { "./src/bridges.cc", "./src/common.cc", "./src/generated/bridges.cc", + "./src/memoryTracker.cc" ] include_dirs = [ @@ -113,7 +114,6 @@ shared_library("es2panda") { "-Xclang", "-instcombine-lower-dbg-declare=0", "-no-canonical-prefixes", - "-static", "-rtlib=compiler-rt", "-stdlib=libc++", "-lunwind", @@ -124,13 +124,6 @@ shared_library("es2panda") { "-fno-strict-aliasing", "-Wno-builtin-macro-redefined", "-fms-extensions", - "-static", - "-rtlib=compiler-rt", - "-stdlib=libc++", - "-std=c++17", - "-lunwind", - "-lpthread", - "-Qunused-arguments", "-target", "x86_64-pc-windows-gnu", "-D__CUSTOM_SECURITY_LIBRARY", diff --git a/koala-wrapper/native/include/common.h b/koala-wrapper/native/include/common.h index 4c17074fb..2803e71af 100644 --- a/koala-wrapper/native/include/common.h +++ b/koala-wrapper/native/include/common.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef COMMON_H -#define COMMON_H +#ifndef COMMON_H_ +#define COMMON_H_ #include "dynamic-loader.h" #include "es2panda_lib.h" @@ -24,16 +24,16 @@ #include #include -using std::string, std::cout, std::endl, std::vector; +using std::string; es2panda_Impl *GetImpl(); -string getString(KStringPtr ptr); +string getString(const KStringPtr &ptr); -char* getStringCopy(KStringPtr& ptr); +char* getStringCopy(const KStringPtr& ptr); -inline KUInt unpackUInt(const KByte* bytes); +KUInt unpackUInt(const KByte* bytes); es2panda_ContextState intToState(KInt state); -#endif // COMMON_H \ No newline at end of file +#endif // COMMON_H_ \ No newline at end of file diff --git a/koala-wrapper/native/include/memoryTracker.h b/koala-wrapper/native/include/memoryTracker.h new file mode 100644 index 000000000..3f0dc71eb --- /dev/null +++ b/koala-wrapper/native/include/memoryTracker.h @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KOALA_MEMORY_TRACKER +#define KOALA_MEMORY_TRACKER + +#include +#include + +// 内存统计结构体 +struct MemoryStats { + size_t currentRss = 0; // 当前驻留集大小 (字节) + size_t peakRss = 0; // 峰值驻留集大小 (字节) + size_t currentVss = 0; // 当前虚拟内存大小 (字节) + size_t pageFaultsMinor = 0; // 小页错误次数 + size_t pageFaultsMajor = 0; // 大页错误次数 +}; + +class MemoryTracker { +public: + MemoryTracker() + { + Reset(); + } + + void Reset(); + MemoryStats GetDelta(); + + template + MemoryStats MeasureMemory(Func&& func); + + void Report(MemoryStats stats); + +private: + MemoryStats baseline; +}; + +MemoryStats GetMemoryStats(); + +#endif diff --git a/koala-wrapper/native/src/bridges.cc b/koala-wrapper/native/src/bridges.cc index 1db37cc8e..384054dcc 100644 --- a/koala-wrapper/native/src/bridges.cc +++ b/koala-wrapper/native/src/bridges.cc @@ -15,6 +15,61 @@ #include "common.h" +#include +#include +#include +#include "memoryTracker.h" + +KInt impl_ClassDefinitionLanguageConst(KNativePointer context, KNativePointer receiver) +{ + const auto _context = reinterpret_cast(context); + const auto _receiver = reinterpret_cast(receiver); + auto result = GetImpl()->ClassDefinitionLanguageConst(_context, _receiver); + return result; +} +KOALA_INTEROP_2(ClassDefinitionLanguageConst, KInt, KNativePointer, KNativePointer); + +KNativePointer impl_CreateClassDefinition3(KNativePointer context, KNativePointer ident, KNativePointer typeParams, KNativePointer superTypeParams, KNativePointerArray _implements, KUInt _implementsSequenceLength, KNativePointer ctor, KNativePointer superClass, KNativePointerArray body, KUInt bodySequenceLength, KInt modifiers, KInt flags, KInt lang) +{ + const auto _context = reinterpret_cast(context); + const auto _ident = reinterpret_cast(ident); + const auto _typeParams = reinterpret_cast(typeParams); + const auto _superTypeParams = reinterpret_cast(superTypeParams); + const auto __implements = reinterpret_cast(_implements); + const auto __implementsSequenceLength = static_cast(_implementsSequenceLength); + const auto _ctor = reinterpret_cast(ctor); + const auto _superClass = reinterpret_cast(superClass); + const auto _body = reinterpret_cast(body); + const auto _bodySequenceLength = static_cast(bodySequenceLength); + const auto _modifiers = static_cast(modifiers); + const auto _flags = static_cast(flags); + const auto _lang = static_cast(lang); + auto result = GetImpl()->CreateClassDefinition3(_context, _ident, _typeParams, _superTypeParams, __implements, __implementsSequenceLength, _ctor, _superClass, _body, _bodySequenceLength, _modifiers, _flags, _lang); + return result; +} +KOALA_INTEROP_13(CreateClassDefinition3, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointerArray, KUInt, KNativePointer, KNativePointer, KNativePointerArray, KUInt, KInt, KInt, KInt); + +KNativePointer impl_UpdateClassDefinition3(KNativePointer context, KNativePointer original, KNativePointer ident, KNativePointer typeParams, KNativePointer superTypeParams, KNativePointerArray _implements, KUInt _implementsSequenceLength, KNativePointer ctor, KNativePointer superClass, KNativePointerArray body, KUInt bodySequenceLength, KInt modifiers, KInt flags, KInt lang) +{ + const auto _context = reinterpret_cast(context); + const auto _original = reinterpret_cast(original); + const auto _ident = reinterpret_cast(ident); + const auto _typeParams = reinterpret_cast(typeParams); + const auto _superTypeParams = reinterpret_cast(superTypeParams); + const auto __implements = reinterpret_cast(_implements); + const auto __implementsSequenceLength = static_cast(_implementsSequenceLength); + const auto _ctor = reinterpret_cast(ctor); + const auto _superClass = reinterpret_cast(superClass); + const auto _body = reinterpret_cast(body); + const auto _bodySequenceLength = static_cast(bodySequenceLength); + const auto _modifiers = static_cast(modifiers); + const auto _flags = static_cast(flags); + const auto _lang = static_cast(lang); + auto result = GetImpl()->UpdateClassDefinition3(_context, _original, _ident, _typeParams, _superTypeParams, __implements, __implementsSequenceLength, _ctor, _superClass, _body, _bodySequenceLength, _modifiers, _flags, _lang); + return result; +} +KOALA_INTEROP_14(UpdateClassDefinition3, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointerArray, KUInt, KNativePointer, KNativePointer, KNativePointerArray, KUInt, KInt, KInt, KInt); + KBoolean impl_ClassDefinitionIsFromStructConst(KNativePointer contextPtr, KNativePointer instancePtr) { auto context = reinterpret_cast(contextPtr); @@ -178,7 +233,7 @@ KOALA_INTEROP_1(ContextProgram, KNativePointer, KNativePointer) KNativePointer impl_ProgramAst(KNativePointer contextPtr, KNativePointer programPtr) { - auto context = reinterpret_cast(programPtr); + auto context = reinterpret_cast(contextPtr); auto program = reinterpret_cast(programPtr); return GetImpl()->ProgramAst(context, program); } @@ -198,6 +253,14 @@ KNativePointer impl_CreateContextFromString(KNativePointer configPtr, KStringPtr } KOALA_INTEROP_3(CreateContextFromString, KNativePointer, KNativePointer, KStringPtr, KStringPtr) +KNativePointer impl_CreateContextFromStringWithHistory(KNativePointer configPtr, KStringPtr& sourcePtr, + KStringPtr& filenamePtr) +{ + auto config = reinterpret_cast(configPtr); + return GetImpl()->CreateContextFromStringWithHistory(config, sourcePtr.data(), filenamePtr.data()); +} +KOALA_INTEROP_3(CreateContextFromStringWithHistory, KNativePointer, KNativePointer, KStringPtr, KStringPtr) + KNativePointer impl_CreateContextFromFile(KNativePointer configPtr, KStringPtr& filenamePtr) { auto config = reinterpret_cast(configPtr); @@ -260,12 +323,31 @@ static KNativePointer impl_ProgramExternalSources(KNativePointer contextPtr, KNa { auto context = reinterpret_cast(contextPtr); auto&& instance = reinterpret_cast(instancePtr); - std::size_t source_len = 0; - auto external_sources = GetImpl()->ProgramExternalSources(context, instance, &source_len); - return new std::vector(external_sources, external_sources + source_len); + std::size_t sourceLen = 0; + auto externalSources = GetImpl()->ProgramExternalSources(context, instance, &sourceLen); + return new std::vector(externalSources, externalSources + sourceLen); } KOALA_INTEROP_2(ProgramExternalSources, KNativePointer, KNativePointer, KNativePointer); +static KNativePointer impl_ProgramDirectExternalSources(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto context = reinterpret_cast(contextPtr); + auto&& instance = reinterpret_cast(instancePtr); + std::size_t sourceLen = 0; + auto externalSources = GetImpl()->ProgramDirectExternalSources(context, instance, &sourceLen); + return new std::vector(externalSources, externalSources + sourceLen); +} +KOALA_INTEROP_2(ProgramDirectExternalSources, KNativePointer, KNativePointer, KNativePointer); + +static KNativePointer impl_ProgramModuleNameConst(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto context = reinterpret_cast(contextPtr); + auto program = reinterpret_cast(instancePtr); + auto result = GetImpl()->ProgramModuleNameConst(context, program); + return new std::string(result); +} +KOALA_INTEROP_2(ProgramModuleNameConst, KNativePointer, KNativePointer, KNativePointer); + static KNativePointer impl_ExternalSourceName(KNativePointer instance) { auto&& _instance_ = reinterpret_cast(instance); @@ -277,9 +359,9 @@ KOALA_INTEROP_1(ExternalSourceName, KNativePointer, KNativePointer); static KNativePointer impl_ExternalSourcePrograms(KNativePointer instance) { auto&& _instance_ = reinterpret_cast(instance); - std::size_t program_len = 0; - auto programs = GetImpl()->ExternalSourcePrograms(_instance_, &program_len); - return new std::vector(programs, programs + program_len); + std::size_t programLen = 0; + auto programs = GetImpl()->ExternalSourcePrograms(_instance_, &programLen); + return new std::vector(programs, programs + programLen); } KOALA_INTEROP_1(ExternalSourcePrograms, KNativePointer, KNativePointer); @@ -328,13 +410,21 @@ KBoolean impl_IsETSFunctionType(KNativePointer nodePtr) KOALA_INTEROP_1(IsETSFunctionType, KBoolean, KNativePointer) KInt impl_GenerateTsDeclarationsFromContext(KNativePointer contextPtr, KStringPtr &outputDeclEts, KStringPtr &outputEts, - KBoolean exportAll, KStringPtr &recordFile) + KBoolean exportAll, KBoolean isolated, KStringPtr &recordFile) { auto context = reinterpret_cast(contextPtr); return GetImpl()->GenerateTsDeclarationsFromContext(context, outputDeclEts.data(), outputEts.data(), - exportAll, recordFile.data()); + exportAll, isolated, recordFile.data()); } -KOALA_INTEROP_5(GenerateTsDeclarationsFromContext, KInt, KNativePointer, KStringPtr, KStringPtr, KBoolean, KStringPtr) +KOALA_INTEROP_6(GenerateTsDeclarationsFromContext, KInt, KNativePointer, KStringPtr, KStringPtr, KBoolean, KBoolean, + KStringPtr) + +KInt impl_GenerateStaticDeclarationsFromContext(KNativePointer contextPtr, KStringPtr &outputPath) +{ + auto context = reinterpret_cast(contextPtr); + return GetImpl()->GenerateStaticDeclarationsFromContext(context, outputPath.data()); +} +KOALA_INTEROP_2(GenerateStaticDeclarationsFromContext, KInt, KNativePointer, KStringPtr) void impl_InsertETSImportDeclarationAndParse(KNativePointer context, KNativePointer program, KNativePointer importDeclaration) @@ -377,6 +467,14 @@ KInt impl_SourcePositionLine(KNativePointer context, KNativePointer instance) } KOALA_INTEROP_2(SourcePositionLine, KInt, KNativePointer, KNativePointer); +KInt impl_SourcePositionCol(KNativePointer context, KNativePointer instance) +{ + auto&& _context_ = reinterpret_cast(context); + auto&& _instance_ = reinterpret_cast(instance); + return GetImpl()->SourcePositionCol(_context_, _instance_); +} +KOALA_INTEROP_2(SourcePositionCol, KInt, KNativePointer, KNativePointer); + KNativePointer impl_CreateSourceRange(KNativePointer context, KNativePointer start, KNativePointer end) { auto&& _context_ = reinterpret_cast(context); @@ -412,6 +510,14 @@ KNativePointer impl_ProgramFileNameWithExtensionConst(KNativePointer contextPtr, } KOALA_INTEROP_2(ProgramFileNameWithExtensionConst, KNativePointer, KNativePointer, KNativePointer) +KBoolean impl_ProgramIsASTLoweredConst(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto context = reinterpret_cast(contextPtr); + auto &&instance = reinterpret_cast(instancePtr); + return GetImpl()->ProgramIsASTLoweredConst(context, instance); +} +KOALA_INTEROP_2(ProgramIsASTLoweredConst, KBoolean, KNativePointer, KNativePointer); + KNativePointer impl_ETSParserGetGlobalProgramAbsName(KNativePointer contextPtr) { auto context = reinterpret_cast(contextPtr); @@ -420,6 +526,15 @@ KNativePointer impl_ETSParserGetGlobalProgramAbsName(KNativePointer contextPtr) } KOALA_INTEROP_1(ETSParserGetGlobalProgramAbsName, KNativePointer, KNativePointer) +KNativePointer impl_ProgramAbsoluteNameConst(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto context = reinterpret_cast(contextPtr); + auto &&instance = reinterpret_cast(instancePtr); + auto result = GetImpl()->ProgramAbsoluteNameConst(context, instance); + return new std::string(result); +} +KOALA_INTEROP_2(ProgramAbsoluteNameConst, KNativePointer, KNativePointer, KNativePointer); + KNativePointer impl_ClassVariableDeclaration(KNativePointer context, KNativePointer classInstance) { const auto _context = reinterpret_cast(context); @@ -490,6 +605,7 @@ KNativePointer impl_SourceRangeEnd(KNativePointer context, KNativePointer range) return result; } KOALA_INTEROP_2(SourceRangeEnd, KNativePointer, KNativePointer, KNativePointer) + bool impl_ClassPropertyIsDefaultAccessModifierConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -506,6 +622,16 @@ KNativePointer impl_AstNodeStartConst(KNativePointer context, KNativePointer rec } KOALA_INTEROP_2(AstNodeStartConst, KNativePointer, KNativePointer, KNativePointer); +void impl_AstNodeSetStart(KNativePointer context, KNativePointer receiver, KNativePointer start) +{ + auto _context = reinterpret_cast(context); + auto _receiver = reinterpret_cast(receiver); + auto _start = reinterpret_cast(start); + GetImpl()->AstNodeSetStart(_context, _receiver, _start); + return; +} +KOALA_INTEROP_V3(AstNodeSetStart, KNativePointer, KNativePointer, KNativePointer) + KNativePointer impl_AstNodeEndConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -514,6 +640,16 @@ KNativePointer impl_AstNodeEndConst(KNativePointer context, KNativePointer recei } KOALA_INTEROP_2(AstNodeEndConst, KNativePointer, KNativePointer, KNativePointer); +void impl_AstNodeSetEnd(KNativePointer context, KNativePointer receiver, KNativePointer end) +{ + const auto _context = reinterpret_cast(context); + const auto _receiver = reinterpret_cast(receiver); + const auto _end = reinterpret_cast(end); + GetImpl()->AstNodeSetEnd(_context, _receiver, _end); + return; +} +KOALA_INTEROP_V3(AstNodeSetEnd, KNativePointer, KNativePointer, KNativePointer); + KBoolean impl_IsArrayExpression(KNativePointer nodePtr) { auto node = reinterpret_cast(nodePtr); @@ -521,6 +657,15 @@ KBoolean impl_IsArrayExpression(KNativePointer nodePtr) } KOALA_INTEROP_1(IsArrayExpression, KBoolean, KNativePointer) +KNativePointer impl_CreateDiagnosticKind(KNativePointer context, KStringPtr& message, KInt type) +{ + const auto _context = reinterpret_cast(context); + const auto _message = getStringCopy(message); + const auto _type = static_cast(type); + return const_cast(GetImpl()->CreateDiagnosticKind(_context, _message, _type)); +} +KOALA_INTEROP_3(CreateDiagnosticKind, KNativePointer, KNativePointer, KStringPtr, KInt); + inline KUInt unpackUInt(const KByte* bytes) { const KUInt BYTE_0 = 0; @@ -535,3 +680,142 @@ inline KUInt unpackUInt(const KByte* bytes) | (bytes[BYTE_2] << BYTE_2_SHIFT) | (bytes[BYTE_3] << BYTE_3_SHIFT) ); } + +KNativePointer impl_CreateDiagnosticInfo(KNativePointer context, KNativePointer kind, KStringArray argsPtr, + KInt argc, KNativePointer pos) +{ + const auto _context = reinterpret_cast(context); + const auto _kind = reinterpret_cast(kind); + const auto _pos = reinterpret_cast(pos); + const std::size_t headerLen = 4; + const char** _args = new const char*[argc]; + std::size_t position = headerLen; + std::size_t strLen; + for (std::size_t i = 0; i < static_cast(argc); ++i) { + strLen = unpackUInt(argsPtr + position); + position += headerLen; + _args[i] = strdup(std::string(reinterpret_cast(argsPtr + position), strLen).c_str()); + position += strLen; + } + return GetImpl()->CreateDiagnosticInfo(_context, _kind, _args, argc, _pos); +} +KOALA_INTEROP_5(CreateDiagnosticInfo, KNativePointer, KNativePointer, KNativePointer, + KStringArray, KInt, KNativePointer); + +KNativePointer impl_CreateSuggestionInfo(KNativePointer context, KNativePointer kind, KStringArray argsPtr, + KInt argc, KStringPtr& substitutionCode, KStringPtr& title, + KNativePointer range) +{ + const auto _context = reinterpret_cast(context); + const auto _kind = reinterpret_cast(kind); + const auto _range = reinterpret_cast(range); + const std::size_t headerLen = 4; + const char** _args = new const char*[argc]; + std::size_t position = headerLen; + std::size_t strLen; + for (std::size_t i = 0; i < static_cast(argc); ++i) { + strLen = unpackUInt(argsPtr + position); + position += headerLen; + _args[i] = strdup(std::string(reinterpret_cast(argsPtr + position), strLen).c_str()); + position += strLen; + } + const auto _substitutionCode = getStringCopy(substitutionCode); + const auto _title = getStringCopy(title); + return GetImpl()->CreateSuggestionInfo(_context, _kind, _args, argc, _substitutionCode, _title, _range); +} +KOALA_INTEROP_7(CreateSuggestionInfo, KNativePointer, KNativePointer, KNativePointer, + KStringArray, KInt, KStringPtr, KStringPtr, KNativePointer); + +void impl_LogDiagnostic(KNativePointer context, KNativePointer kind, KStringArray argvPtr, + KInt argc, KNativePointer pos) +{ + auto&& _context_ = reinterpret_cast(context); + auto&& _kind_ = reinterpret_cast(kind); + auto&& _pos_ = reinterpret_cast(pos); + const std::size_t headerLen = 4; + const char** argv = new const char*[argc]; + std::size_t position = headerLen; + std::size_t strLen; + for (std::size_t i = 0; i < static_cast(argc); ++i) { + strLen = unpackUInt(argvPtr + position); + position += headerLen; + argv[i] = strdup(std::string(reinterpret_cast(argvPtr + position), strLen).c_str()); + position += strLen; + } + GetImpl()->LogDiagnostic(_context_, _kind_, argv, argc, _pos_); +} +KOALA_INTEROP_V5(LogDiagnostic, KNativePointer, KNativePointer, KStringArray, KInt, KNativePointer); + +void impl_LogDiagnosticWithSuggestion(KNativePointer context, KNativePointer diagnosticInfo, + KNativePointer suggestionInfo) +{ + const auto _context = reinterpret_cast(context); + const auto _diagnosticInfo = reinterpret_cast(diagnosticInfo); + const auto _suggestionInfo = reinterpret_cast(suggestionInfo); + GetImpl()->LogDiagnosticWithSuggestion(_context, _diagnosticInfo, _suggestionInfo); +} +KOALA_INTEROP_V3(LogDiagnosticWithSuggestion, KNativePointer, KNativePointer, KNativePointer); + +KBoolean impl_CallExpressionIsTrailingCallConst(KNativePointer context, KNativePointer receiver) +{ + const auto _context = reinterpret_cast(context); + const auto _receiver = reinterpret_cast(receiver); + return GetImpl()->CallExpressionIsTrailingCallConst(_context, _receiver); +} +KOALA_INTEROP_2(CallExpressionIsTrailingCallConst, KBoolean, KNativePointer, KNativePointer); + +MemoryTracker tracker; +void impl_MemoryTrackerReset(KNativePointer context) +{ + tracker.Reset(); +} +KOALA_INTEROP_V1(MemoryTrackerReset, KNativePointer); + +void impl_MemoryTrackerGetDelta(KNativePointer context) +{ + tracker.Report(tracker.GetDelta()); +} +KOALA_INTEROP_V1(MemoryTrackerGetDelta, KNativePointer); + +void impl_MemoryTrackerPrintCurrent(KNativePointer context) +{ + tracker.Report(GetMemoryStats()); +} +KOALA_INTEROP_V1(MemoryTrackerPrintCurrent, KNativePointer); + +KNativePointer impl_CreateTypeNodeFromTsType(KNativePointer context, KNativePointer nodePtr) +{ + const auto _context = reinterpret_cast(context); + const auto _nodePtr = reinterpret_cast(nodePtr); + auto _tsType = GetImpl()->TypedTsType(_context, _nodePtr); + if (_tsType == nullptr) { + _tsType = GetImpl()->ExpressionTsType(_context, _nodePtr); + } + if (_tsType == nullptr) { + return nullptr; + } + const auto _nodeTsType = reinterpret_cast(_tsType); + auto _typeAnnotation = GetImpl()->CreateOpaqueTypeNode(_context, _nodeTsType); + return _typeAnnotation; +} +KOALA_INTEROP_2(CreateTypeNodeFromTsType, KNativePointer, KNativePointer, KNativePointer); + +static KNativePointer impl_JsdocStringFromDeclaration(KNativePointer contextPtr, KNativePointer decl) +{ + auto context = reinterpret_cast(contextPtr); + auto node = reinterpret_cast(decl); + + auto result = GetImpl()->JsdocStringFromDeclaration(context, node); + return new std::string(result); +} +KOALA_INTEROP_2(JsdocStringFromDeclaration, KNativePointer, KNativePointer, KNativePointer); + +static KNativePointer impl_ProgramSourceFilePathConst(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto context = reinterpret_cast(contextPtr); + auto program = reinterpret_cast(instancePtr); + + auto result = GetImpl()->ProgramSourceFilePathConst(context, program); + return new std::string(result); +} +KOALA_INTEROP_2(ProgramSourceFilePathConst, KNativePointer, KNativePointer, KNativePointer); diff --git a/koala-wrapper/native/src/common.cc b/koala-wrapper/native/src/common.cc index 084c86ea5..29baa82db 100644 --- a/koala-wrapper/native/src/common.cc +++ b/koala-wrapper/native/src/common.cc @@ -31,8 +31,10 @@ static es2panda_Impl *impl = nullptr; #ifdef __x86_64__ #define PLUGIN_DIR "linux_host_tools" - #else + #elif defined(__aarch64__) #define PLUGIN_DIR "linux_arm64_host_tools" + #else + #error "Unsupported architecture!" #endif #define LIB_PREFIX "lib" @@ -40,6 +42,10 @@ static es2panda_Impl *impl = nullptr; #endif const char* LIB_ES2PANDA_PUBLIC = LIB_PREFIX "es2panda_public" LIB_SUFFIX; +constexpr const char* IS_UI_FLAG = "IS_UI_FLAG"; +constexpr const char* NOT_UI_FLAG = "NOT_UI_FLAG"; +const string MODULE_SUFFIX = ".d.ets"; +const string ARKUI = "arkui"; #ifdef KOALA_WINDOWS const char *SEPARATOR = "\\"; @@ -103,11 +109,13 @@ es2panda_ContextState intToState(KInt state) return es2panda_ContextState(state); } -string getString(KStringPtr ptr) { +string getString(const KStringPtr& ptr) +{ return ptr.data(); } -char* getStringCopy(KStringPtr& ptr) { +char* getStringCopy(const KStringPtr& ptr) +{ return strdup(ptr.c_str()); } @@ -128,6 +136,56 @@ inline KUInt unpackUInt(const KByte* bytes) { ); } +void impl_MemInitialize() +{ + GetImpl()->MemInitialize(); +} +KOALA_INTEROP_V0(MemInitialize) + +void impl_MemFinalize() +{ + GetImpl()->MemFinalize(); +} +KOALA_INTEROP_V0(MemFinalize) + +KNativePointer impl_CreateGlobalContext(KNativePointer configPtr, KStringArray externalFileListPtr, + KInt fileNum, KBoolean lspUsage) +{ + auto config = reinterpret_cast(configPtr); + + const std::size_t headerLen = 4; + + const char** externalFileList = new const char* [fileNum]; + std::size_t position = headerLen; + std::size_t strLen; + for (std::size_t i = 0; i < static_cast(fileNum); ++i) { + strLen = unpackUInt(externalFileListPtr + position); + position += headerLen; + externalFileList[i] = strdup(std::string( + reinterpret_cast(externalFileListPtr + position), strLen).c_str()); + position += strLen; + } + + return GetImpl()->CreateGlobalContext(config, externalFileList, fileNum, lspUsage); +} +KOALA_INTEROP_4(CreateGlobalContext, KNativePointer, KNativePointer, KStringArray, KInt, KBoolean) + +void impl_DestroyGlobalContext(KNativePointer globalContextPtr) +{ + auto context = reinterpret_cast(globalContextPtr); + GetImpl()->DestroyGlobalContext(context); +} +KOALA_INTEROP_V1(DestroyGlobalContext, KNativePointer) + +KNativePointer impl_CreateCacheContextFromFile(KNativePointer configPtr, KStringPtr& fileName, + KNativePointer globalContext, KBoolean isExternal) +{ + auto config = reinterpret_cast(configPtr); + auto context = reinterpret_cast(globalContext); + return GetImpl()->CreateCacheContextFromFile(config, getStringCopy(fileName), context, isExternal); +} +KOALA_INTEROP_4(CreateCacheContextFromFile, KNativePointer, KNativePointer, KStringPtr, KNativePointer, KBoolean) + KNativePointer impl_CreateConfig(KInt argc, KStringArray argvPtr) { const std::size_t headerLen = 4; @@ -200,8 +258,20 @@ TODO: NOT FROM API (shouldn't be there) ----------------------------------------------------------------------------------------------------------------------------- */ -es2panda_AstNode * cachedParentNode; -es2panda_Context * cachedContext; +KNativePointer impl_AstNodeProgram(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto _context = reinterpret_cast(contextPtr); + auto _receiver = reinterpret_cast(instancePtr); + + if (GetImpl()->AstNodeIsProgramConst(_context, _receiver)) { + return GetImpl()->ETSModuleProgram(_context, _receiver); + } + return impl_AstNodeProgram(_context, GetImpl()->AstNodeParent(_context, _receiver)); +} +KOALA_INTEROP_2(AstNodeProgram, KNativePointer, KNativePointer, KNativePointer) + +thread_local es2panda_AstNode *cachedParentNode; +thread_local es2panda_Context *cachedContext; static void changeParent(es2panda_AstNode *child) { @@ -236,7 +306,7 @@ KNativePointer impl_AstNodeUpdateChildren(KNativePointer contextPtr, KNativePoin } KOALA_INTEROP_2(AstNodeUpdateChildren, KNativePointer, KNativePointer, KNativePointer) -std::vector cachedChildren; +thread_local std::vector cachedChildren; static void visitChild(es2panda_AstNode *node) { cachedChildren.emplace_back(node); @@ -256,6 +326,43 @@ KNativePointer impl_AstNodeChildren( } KOALA_INTEROP_2(AstNodeChildren, KNativePointer, KNativePointer, KNativePointer) +static bool isUIHeaderFile(es2panda_Context* context, es2panda_Program* program) +{ + auto result = GetImpl()->ProgramFileNameWithExtensionConst(context, program); + string fileNameWithExtension(result); + result = GetImpl()->ProgramModuleNameConst(context, program); + string moduleName(result); + + return fileNameWithExtension.length() >= MODULE_SUFFIX.length() + && fileNameWithExtension.substr(fileNameWithExtension.length() - MODULE_SUFFIX.length()) == MODULE_SUFFIX + && moduleName.find(ARKUI) != std::string::npos; +} + +KBoolean impl_ProgramCanSkipPhases(KNativePointer context, KNativePointer program) +{ + KStringPtr isUiFlag(IS_UI_FLAG); + KStringPtr notUiFlag(NOT_UI_FLAG); + const auto _context = reinterpret_cast(context); + const auto _program = reinterpret_cast(program); + if (isUIHeaderFile(_context, _program)) { + return false; + } + std::size_t sourceLen; + const auto externalSources = reinterpret_cast + (GetImpl()->ProgramExternalSources(_context, _program, &sourceLen)); + for (std::size_t i = 0; i < sourceLen; ++i) { + std::size_t programLen; + auto programs = GetImpl()->ExternalSourcePrograms(externalSources[i], &programLen); + for (std::size_t j = 0; j < programLen; ++j) { + if (isUIHeaderFile(_context, programs[j])) { + return false; + } + } + } + return true; +} +KOALA_INTEROP_2(ProgramCanSkipPhases, KBoolean, KNativePointer, KNativePointer) + /* ----------------------------------------------------------------------------------------------------------------------------- */ diff --git a/koala-wrapper/native/src/generated/bridges.cc b/koala-wrapper/native/src/generated/bridges.cc index 5bcf9071c..01b34d85a 100644 --- a/koala-wrapper/native/src/generated/bridges.cc +++ b/koala-wrapper/native/src/generated/bridges.cc @@ -13,7 +13,7 @@ * limitations under the License. */ -#include +#include "common.h" KNativePointer impl_CreateLabelledStatement(KNativePointer context, KNativePointer ident, KNativePointer body) { @@ -298,24 +298,6 @@ KInt impl_ETSFunctionTypeIrFlags(KNativePointer context, KNativePointer receiver } KOALA_INTEROP_2(ETSFunctionTypeIrFlags, KInt, KNativePointer, KNativePointer); -KBoolean impl_ETSFunctionTypeIrIsThrowingConst(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - auto result = GetImpl()->ETSFunctionTypeIrIsThrowingConst(_context, _receiver); - return result; -} -KOALA_INTEROP_2(ETSFunctionTypeIrIsThrowingConst, KBoolean, KNativePointer, KNativePointer); - -KBoolean impl_ETSFunctionTypeIrIsRethrowingConst(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - auto result = GetImpl()->ETSFunctionTypeIrIsRethrowingConst(_context, _receiver); - return result; -} -KOALA_INTEROP_2(ETSFunctionTypeIrIsRethrowingConst, KBoolean, KNativePointer, KNativePointer); - KBoolean impl_ETSFunctionTypeIrIsExtensionFunctionConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -2707,16 +2689,6 @@ void impl_TSTypeAliasDeclarationSetTypeParameters(KNativePointer context, KNativ } KOALA_INTEROP_V3(TSTypeAliasDeclarationSetTypeParameters, KNativePointer, KNativePointer, KNativePointer); -KNativePointer impl_TSTypeAliasDeclarationAnnotations(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - std::size_t length; - auto result = GetImpl()->TSTypeAliasDeclarationAnnotations(_context, _receiver, &length); - return new std::vector(result, result + length); -} -KOALA_INTEROP_2(TSTypeAliasDeclarationAnnotations, KNativePointer, KNativePointer, KNativePointer); - KNativePointer impl_TSTypeAliasDeclarationAnnotationsConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -3251,24 +3223,6 @@ KBoolean impl_ScriptFunctionHasThrowStatementConst(KNativePointer context, KNati } KOALA_INTEROP_2(ScriptFunctionHasThrowStatementConst, KBoolean, KNativePointer, KNativePointer); -KBoolean impl_ScriptFunctionIsThrowingConst(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - auto result = GetImpl()->ScriptFunctionIsThrowingConst(_context, _receiver); - return result; -} -KOALA_INTEROP_2(ScriptFunctionIsThrowingConst, KBoolean, KNativePointer, KNativePointer); - -KBoolean impl_ScriptFunctionIsRethrowingConst(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - auto result = GetImpl()->ScriptFunctionIsRethrowingConst(_context, _receiver); - return result; -} -KOALA_INTEROP_2(ScriptFunctionIsRethrowingConst, KBoolean, KNativePointer, KNativePointer); - KBoolean impl_ScriptFunctionIsDynamicConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -3325,16 +3279,6 @@ void impl_ScriptFunctionAddFlag(KNativePointer context, KNativePointer receiver, } KOALA_INTEROP_V3(ScriptFunctionAddFlag, KNativePointer, KNativePointer, KInt); -void impl_ScriptFunctionAddModifier(KNativePointer context, KNativePointer receiver, KInt flags) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - const auto _flags = static_cast(flags); - GetImpl()->ScriptFunctionAddModifier(_context, _receiver, _flags); - return ; -} -KOALA_INTEROP_V3(ScriptFunctionAddModifier, KNativePointer, KNativePointer, KInt); - KUInt impl_ScriptFunctionFormalParamsLengthConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -6691,16 +6635,6 @@ KNativePointer impl_ImportDeclarationSpecifiersConst(KNativePointer context, KNa } KOALA_INTEROP_2(ImportDeclarationSpecifiersConst, KNativePointer, KNativePointer, KNativePointer); -KNativePointer impl_ImportDeclarationSpecifiers(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - std::size_t length; - auto result = GetImpl()->ImportDeclarationSpecifiers(_context, _receiver, &length); - return new std::vector(result, result + length); -} -KOALA_INTEROP_2(ImportDeclarationSpecifiers, KNativePointer, KNativePointer, KNativePointer); - KBoolean impl_ImportDeclarationIsTypeKindConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -6787,15 +6721,6 @@ KNativePointer impl_UpdateETSImportDeclaration(KNativePointer context, KNativePo } KOALA_INTEROP_6(UpdateETSImportDeclaration, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointerArray, KUInt, KInt); -KNativePointer impl_ETSImportDeclarationAssemblerName(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - auto result = GetImpl()->ETSImportDeclarationAssemblerName(_context, _receiver); - return new std::string(result); -} -KOALA_INTEROP_2(ETSImportDeclarationAssemblerName, KNativePointer, KNativePointer, KNativePointer); - KNativePointer impl_ETSImportDeclarationAssemblerNameConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); diff --git a/koala-wrapper/native/src/memoryTracker.cc b/koala-wrapper/native/src/memoryTracker.cc new file mode 100644 index 000000000..c12fcd248 --- /dev/null +++ b/koala-wrapper/native/src/memoryTracker.cc @@ -0,0 +1,178 @@ +/** + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "memoryTracker.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #include +#elif defined(__APPLE__) + #include + #include +#elif defined(__linux__) + #include + #include +#endif + +constexpr const char* UNIT_K = "kB"; +constexpr size_t BYTES_PER_KB = 1024; +constexpr const char* MEMORY_STATUS_FILE = "/proc/self/status"; +const std::regex VM_RSS_REGEX(R"#(VmRSS:\s*(\d+)\s*([kKmMgG]?B))#", + std::regex_constants::ECMAScript | std::regex_constants::icase); +const std::regex VM_SIZE_REGEX(R"#(VmSize:\s*(\d+)\s*([kKmMgG]?B))#", + std::regex_constants::ECMAScript | std::regex_constants::icase); + +constexpr int MATCH_GROUP_VALUE = 1; +constexpr int MATCH_GROUP_UNIT = 2; +constexpr int MATCH_GROUP_SIZE = 3; + +#if defined(_WIN32) +MemoryStats GetMemoryStats() +{ + MemoryStats stats = {0, 0, 0, 0, 0}; + PROCESS_MEMORY_COUNTERS_EX pmc; + if (GetProcessMemoryInfo(GetCurrentProcess(), + reinterpret_cast(&pmc), sizeof(pmc))) { + stats.currentRss = pmc.WorkingSetSize; + stats.peakRss = pmc.PeakWorkingSetSize; + stats.currentVss = pmc.PrivateUsage; // 私有内存使用量 + stats.pageFaultsMinor = pmc.PageFaultCount; + // Windows API不直接提供主缺页错误计数 + stats.pageFaultsMajor = 0; + } + return stats; +} + +#elif defined(__APPLE__) +MemoryStats GetMemoryStats() +{ + MemoryStats stats = {0, 0, 0, 0, 0}; + struct rusage ru; + if (getrusage(RUSAGE_SELF, &ru) == 0) { + stats.currentRss = 0; // macOS需要专用API获取当前内存 + stats.peakRss = ru.ru_maxrss; // macOS返回字节 + stats.pageFaultsMinor = ru.ru_minflt; + stats.pageFaultsMajor = ru.ru_majflt; + + // 获取当前内存使用 (macOS专用API) + task_basic_info info; + mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT; + if (task_info(mach_task_self(), TASK_BASIC_INFO_64, + (task_info_t)&info, &count) == KERN_SUCCESS) { + stats.currentRss = info.resident_size; // 物理内存使用量 + stats.currentVss = info.virtual_size; // 虚拟内存总量 + } + } + return stats; +} + +#elif defined(__linux__) +MemoryStats GetMemoryStats() +{ + MemoryStats stats = {0, 0, 0, 0, 0}; + struct rusage ru; + if (getrusage(RUSAGE_SELF, &ru) == 0) { + stats.peakRss = static_cast(ru.ru_maxrss) * BYTES_PER_KB; // KB -> 字节 + stats.pageFaultsMinor = ru.ru_minflt; + stats.pageFaultsMajor = ru.ru_majflt; + } + std::ifstream statusFile(MEMORY_STATUS_FILE); + if (!statusFile) { + return stats; + } + std::string line; + std::smatch matches; + while (std::getline(statusFile, line)) { + if (std::regex_match(line, matches, VM_RSS_REGEX) && matches.size() >= MATCH_GROUP_SIZE) { + stats.currentRss = std::stoull(matches[MATCH_GROUP_VALUE].str()); + std::string unit = matches[MATCH_GROUP_UNIT].str(); + if (unit == UNIT_K) { + stats.currentRss *= BYTES_PER_KB; + } + } else if (std::regex_match(line, matches, VM_SIZE_REGEX) && matches.size() >= MATCH_GROUP_SIZE) { + stats.currentVss = std::stoull(matches[MATCH_GROUP_VALUE].str()); + std::string unit = matches[MATCH_GROUP_UNIT].str(); + if (unit == UNIT_K) { + stats.currentVss *= BYTES_PER_KB; + } + } + } + return stats; +} +#endif + +void MemoryTracker::Reset() +{ + baseline = GetMemoryStats(); +} + +MemoryStats MemoryTracker::GetDelta() +{ + MemoryStats current = GetMemoryStats(); + MemoryStats delta = { + current.currentRss - baseline.currentRss, + current.peakRss - baseline.peakRss, + current.currentVss - baseline.currentVss, + current.pageFaultsMinor - baseline.pageFaultsMinor, + current.pageFaultsMajor - baseline.pageFaultsMajor + }; + return delta; +} + +template +MemoryStats MemoryTracker::MeasureMemory(Func&& func) +{ + Reset(); + auto preStats = GetMemoryStats(); + func(); + auto postStats = GetMemoryStats(); + + return { + postStats.currentRss - preStats.currentRss, + postStats.peakRss - preStats.peakRss, + postStats.currentVss - preStats.currentVss, + postStats.pageFaultsMinor - preStats.pageFaultsMinor, + postStats.pageFaultsMajor - preStats.pageFaultsMajor + }; +} + +void MemoryTracker::Report(MemoryStats stats) +{ + auto formatBytes = [](size_t bytes) -> std::string { + const double kb = BYTES_PER_KB; + const double mb = kb * BYTES_PER_KB; + const double gb = mb * BYTES_PER_KB; + + if (bytes > gb) return std::to_string(bytes / gb) + " GB"; + if (bytes > mb) return std::to_string(bytes / mb) + " MB"; + if (bytes > kb) return std::to_string(bytes / kb) + " KB"; + return std::to_string(bytes) + " B"; + }; + + std::cout << "Current RSS: " << formatBytes(stats.currentRss) << "\n"; + std::cout << "Peak RSS : " << formatBytes(stats.peakRss) << "\n"; + std::cout << "VSS : " << formatBytes(stats.currentVss) << "\n"; + std::cout << "FaultsMinor: " << stats.pageFaultsMinor << "\n"; + std::cout << "FaultsMajor: " << stats.pageFaultsMajor << "\n"; + return; +} \ No newline at end of file diff --git a/koala-wrapper/package-lock.json b/koala-wrapper/package-lock.json deleted file mode 100644 index 497d96895..000000000 --- a/koala-wrapper/package-lock.json +++ /dev/null @@ -1,1792 +0,0 @@ -{ - "name": "@koalaui/libarkts", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@babel/cli": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/cli/-/cli-7.20.7.tgz", - "integrity": "sha512-WylgcELHB66WwQqItxNILsMlaTd8/SO6SgTTjMp4uCI7P4QyH1r3nqgFmO3BfM4AtfniHgFMH3EpYFj/zynBkQ==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.8", - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.4.0", - "commander": "^4.0.1", - "convert-source-map": "^1.1.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.2.0", - "make-dir": "^2.1.0", - "slash": "^2.0.0" - } - }, - "@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - } - }, - "@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", - "dev": true - }, - "@babel/core": { - "version": "7.20.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.12", - "@babel/types": "^7.20.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", - "dev": true, - "requires": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "dev": true, - "requires": { - "@babel/types": "^7.27.3" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dev": true, - "requires": { - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", - "dev": true, - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "dev": true, - "requires": { - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "dev": true, - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", - "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", - "dev": true, - "requires": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helpers": { - "version": "7.27.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", - "dev": true, - "requires": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - } - }, - "@babel/parser": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", - "dev": true, - "requires": { - "@babel/types": "^7.28.0" - } - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - } - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", - "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", - "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", - "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", - "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.0" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", - "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", - "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - } - }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", - "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.28.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz", - "integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", - "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/preset-env": { - "version": "7.20.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-env/-/preset-env-7.20.2.tgz", - "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.1", - "@babel/helper-compilation-targets": "^7.20.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.20.1", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.2", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.20.2", - "@babel/plugin-transform-classes": "^7.20.2", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.20.2", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.19.6", - "@babel/plugin-transform-modules-commonjs": "^7.19.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.6", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.20.1", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.19.0", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.18.10", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/preset-modules": { - "version": "0.1.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-modules/-/preset-modules-0.1.6.tgz", - "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" - } - }, - "@babel/runtime": { - "version": "7.20.13", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/runtime/-/runtime-7.20.13.tgz", - "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, - "@babel/template": { - "version": "7.27.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - } - }, - "@babel/traverse": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", - "debug": "^4.3.1" - } - }, - "@babel/types": { - "version": "7.28.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.28.1.tgz", - "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", - "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", - "dev": true, - "optional": true - }, - "@tsconfig/recommended": { - "version": "1.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/@tsconfig/recommended/-/recommended-1.0.8.tgz", - "integrity": "sha512-TotjFaaXveVUdsrXCdalyF6E5RyG6+7hHHQVZonQtdlk1rJZ1myDIvPUUKPhoYv+JAzThb2lQJh9+9ZfF46hsA==", - "dev": true - }, - "@types/node": { - "version": "18.19.119", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/node/-/node-18.19.119.tgz", - "integrity": "sha512-d0F6m9itIPaKnrvEMlzE48UjwZaAnFW7Jwibacw9MNdqadjKNpUm9tfJYDwmShJmgqcoqYUX3EMKO1+RWiuuNg==", - "dev": true, - "requires": { - "undici-types": "~5.26.4" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "optional": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "binary-extensions": { - "version": "2.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "optional": true, - "requires": { - "fill-range": "^7.1.1" - } - }, - "browserslist": { - "version": "4.25.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - } - }, - "caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://repo.huaweicloud.com/repository/npm/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", - "dev": true - }, - "chokidar": { - "version": "3.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "commander": { - "version": "4.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "core-js-compat": { - "version": "3.44.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/core-js-compat/-/core-js-compat-3.44.0.tgz", - "integrity": "sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==", - "dev": true, - "requires": { - "browserslist": "^4.25.1" - } - }, - "debug": { - "version": "4.4.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "requires": { - "ms": "^2.1.3" - } - }, - "electron-to-chromium": { - "version": "1.5.187", - "resolved": "https://repo.huaweicloud.com/repository/npm/electron-to-chromium/-/electron-to-chromium-1.5.187.tgz", - "integrity": "sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==", - "dev": true - }, - "escalade": { - "version": "3.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "optional": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "hasown": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "requires": { - "function-bind": "^1.1.2" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "optional": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.16.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "requires": { - "hasown": "^2.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "optional": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "optional": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "jsesc": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node-addon-api": { - "version": "8.5.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/node-addon-api/-/node-addon-api-8.5.0.tgz", - "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", - "dev": true - }, - "node-releases": { - "version": "2.0.19", - "resolved": "https://repo.huaweicloud.com/repository/npm/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "picocolors": { - "version": "1.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "optional": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "optional": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", - "dev": true, - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true - }, - "regexpu-core": { - "version": "6.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", - "dev": true, - "requires": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - } - }, - "regjsgen": { - "version": "0.8.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true - }, - "regjsparser": { - "version": "0.12.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "dev": true, - "requires": { - "jsesc": "~3.0.2" - }, - "dependencies": { - "jsesc": { - "version": "3.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true - } - } - }, - "resolve": { - "version": "1.22.10", - "resolved": "https://repo.huaweicloud.com/repository/npm/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "requires": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "optional": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "typescript": { - "version": "5.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true - }, - "undici-types": { - "version": "5.26.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true - }, - "update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "requires": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } -} diff --git a/koala-wrapper/src/Es2pandaEnums.ts b/koala-wrapper/src/Es2pandaEnums.ts index bf2ce0783..4aa8f8c2d 100644 --- a/koala-wrapper/src/Es2pandaEnums.ts +++ b/koala-wrapper/src/Es2pandaEnums.ts @@ -70,6 +70,7 @@ export enum Es2pandaAstNodeType { AST_NODE_TYPE_UNDEFINED_LITERAL, AST_NODE_TYPE_NUMBER_LITERAL, AST_NODE_TYPE_OMITTED_EXPRESSION, + AST_NODE_TYPE_OVERLOAD_DECLARATION, AST_NODE_TYPE_PREFIX_ASSERTION_EXPRESSION, AST_NODE_TYPE_PROPERTY, AST_NODE_TYPE_REGEXP_LITERAL, @@ -83,6 +84,7 @@ export enum Es2pandaAstNodeType { AST_NODE_TYPE_ETS_UNDEFINED_TYPE, AST_NODE_TYPE_ETS_NEVER_TYPE, AST_NODE_TYPE_ETS_STRING_LITERAL_TYPE, + AST_NODE_TYPE_ETS_INTRINSIC_NODE_TYPE, AST_NODE_TYPE_ETS_FUNCTION_TYPE, AST_NODE_TYPE_ETS_WILDCARD_TYPE, AST_NODE_TYPE_ETS_PRIMITIVE_TYPE, @@ -181,10 +183,18 @@ export enum Es2pandaAstNodeType { AST_NODE_TYPE_OBJECT_PATTERN, AST_NODE_TYPE_SPREAD_ELEMENT, AST_NODE_TYPE_REST_ELEMENT, -} +}; export enum Es2pandaImportFlags { IMPORT_FLAGS_NONE, IMPORT_FLAGS_DEFAULT_IMPORT, IMPORT_FLAGS_IMPLICIT_PACKAGE_IMPORT, } + +export enum Es2pandaLanguage { + AS = 0, + JS = 1, + TS = 2, + ETS = 3, + COUNT = 4 +} \ No newline at end of file diff --git a/koala-wrapper/src/Es2pandaNativeModule.ts b/koala-wrapper/src/Es2pandaNativeModule.ts index 770e72aef..09bccfe0c 100644 --- a/koala-wrapper/src/Es2pandaNativeModule.ts +++ b/koala-wrapper/src/Es2pandaNativeModule.ts @@ -16,14 +16,18 @@ import { KNativePointer as KPtr, KInt, + KStringPtr, KBoolean, KNativePointer, registerNativeModuleLibraryName, loadNativeModuleLibrary, KDouble, + KStringArrayPtr, + KUInt, } from '@koalaui/interop'; import { Es2pandaNativeModule as GeneratedEs2pandaNativeModule } from './generated/Es2pandaNativeModule'; import * as path from 'path'; +import { PluginDiagnosticType } from './arkts-api/peers/DiagnosticKind'; // TODO: this type should be in interop export type KPtrArray = BigUint64Array; @@ -110,6 +114,9 @@ export class Es2pandaNativeModule { string[]): KPtr { throw new Error('Not implemented'); } + _CreateContextFromStringWithHistory(config: KPtr, source: String, filename: String): KPtr { + throw new Error('Not implemented'); + } _CreateContextFromFile(config: KPtr, filename: String): KPtr { throw new Error('Not implemented'); } @@ -741,7 +748,12 @@ export class Es2pandaNativeModule { _ProgramExternalSources(context: KNativePointer, instance: KNativePointer): KNativePointer { throw new Error('Not implemented'); } - + _ProgramDirectExternalSources(context: KNativePointer, instance: KNativePointer): KNativePointer { + throw new Error('Not implemented'); + } + _AstNodeProgram(context: KNativePointer, instance: KNativePointer): KNativePointer { + throw new Error('Not implemented'); + } _ExternalSourceName(instance: KNativePointer): KNativePointer { throw new Error('Not implemented'); } @@ -750,16 +762,28 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } + _ProgramCanSkipPhases(context: KNativePointer, program: KNativePointer): boolean { + throw new Error('Not implemented'); + } + _GenerateTsDeclarationsFromContext( config: KPtr, outputDeclEts: String, outputEts: String, exportAll: KBoolean, + isolated: KBoolean, recordFile: String ): KPtr { throw new Error('Not implemented'); } + _GenerateStaticDeclarationsFromContext( + config: KPtr, + outputPath: String + ): KPtr { + throw new Error('Not implemented'); + } + _InsertETSImportDeclarationAndParse( context: KNativePointer, program: KNativePointer, @@ -781,6 +805,9 @@ export class Es2pandaNativeModule { _SourcePositionLine(context: KNativePointer, instance: KNativePointer): KInt { throw new Error('Not implemented'); } + _SourcePositionCol(context: KNativePointer, instance: KNativePointer): KInt { + throw new Error('Not implemented'); + } _CreateETSStringLiteralType(context: KNativePointer, str: String): KNativePointer { throw new Error('Not implemented'); } @@ -792,6 +819,17 @@ export class Es2pandaNativeModule { _ClassDefinitionSetFromStructModifier(context: KNativePointer, instance: KNativePointer): void { throw new Error('Not implemented'); } + + _CreateClassDefinition3(context: KNativePointer, ident: KNativePointer, typeParams: KNativePointer, superTypeParams: KNativePointer, _implements: BigUint64Array, _implementsSequenceLength: KUInt, ctor: KNativePointer, superClass: KNativePointer, body: BigUint64Array, bodySequenceLength: KUInt, modifiers: KInt, flags: KInt, lang: KInt): KNativePointer { + throw new Error("'CreateClassDefinition was not overloaded by native module initialization") + } + _UpdateClassDefinition3(context: KNativePointer, original: KNativePointer, ident: KNativePointer, typeParams: KNativePointer, superTypeParams: KNativePointer, _implements: BigUint64Array, _implementsSequenceLength: KUInt, ctor: KNativePointer, superClass: KNativePointer, body: BigUint64Array, bodySequenceLength: KUInt, modifiers: KInt, flags: KInt, lang: KInt): KNativePointer { + throw new Error("'UpdateClassDefinition was not overloaded by native module initialization") + } + + _ClassDefinitionLanguageConst(context: KNativePointer, receiver: KNativePointer): KInt { + throw new Error("'ClassDefinitionLanguageConst was not overloaded by native module initialization") + } _ProgramFileNameConst(context: KPtr, program: KPtr): KNativePointer { throw new Error('Not implemented'); @@ -801,10 +839,18 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } + _ProgramIsASTLoweredConst(context: KPtr, program: KPtr): KBoolean { + throw new Error('Not implemented'); + } + _ETSParserGetGlobalProgramAbsName(context: KNativePointer): KNativePointer { throw new Error('Not implemented'); } + _ProgramAbsoluteNameConst(context: KNativePointer, instance: KNativePointer): KNativePointer { + throw new Error('Not implemented'); + } + _ImportSpecifierIsRemovableConst(context: KNativePointer, instance: KNativePointer): KBoolean { throw new Error('Not implemented'); } @@ -821,18 +867,30 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } + _AstNodeSetStart(context: KNativePointer, receiver: KNativePointer, start: KNativePointer): void { + throw new Error('Not implemented'); + } + _AstNodeEndConst(context: KNativePointer, receiver: KNativePointer): KNativePointer { throw new Error('Not implemented'); } + _AstNodeSetEnd(context: KNativePointer, receiver: KNativePointer, end: KNativePointer): void { + throw new Error('Not implemented'); + } + _ClassVariableDeclaration(context: KNativePointer, classInstance: KNativePointer): KNativePointer { throw new Error('Not implemented'); } - + _IsMethodDefinition(node: KPtr): KBoolean { throw new Error('Not implemented'); } + _ProgramModuleNameConst(context: KPtr, program: KPtr): KNativePointer { + throw new Error('Not implemented'); + } + _AstNodeRangeConst(context: KNativePointer, node: KNativePointer): KNativePointer { throw new Error('CreateFunctionDecl was not overloaded by native module initialization'); } @@ -850,12 +908,85 @@ export class Es2pandaNativeModule { } _IsArrayExpression(node: KPtr): KBoolean { + throw new Error('IsArrayExpression was not overloaded by native module initialization'); + } + + _MemInitialize(): void { + throw new Error('MemInitialize was not overloaded by native module initialization'); + } + + _MemFinalize(): void { + throw new Error('MemFinalize was not overloaded by native module initialization'); + } + + _CreateGlobalContext(configPtr: KNativePointer, externalFileList: KStringArrayPtr, fileNum: KInt, + lspUsage: boolean): KNativePointer { + throw new Error('CreateGlobalContext was not overloaded by native module initialization'); + } + + _DestroyGlobalContext(contextPtr: KNativePointer): void { + throw new Error('DestroyGlobalContext was not overloaded by native module initialization'); + } + + _CreateCacheContextFromFile(configPtr: KNativePointer, filename: string, globalContext: KNativePointer, + isExternal: KBoolean): KNativePointer { + throw new Error('CreateCacheContextFromFile was not overloaded by native module initialization'); + } + + _CreateDiagnosticKind(context: KNativePointer, message: string, type: PluginDiagnosticType): KNativePointer { + throw new Error('Not implemented'); + } + + _CreateDiagnosticInfo(context: KNativePointer, kind: KNativePointer, args: string[], + argc: number, pos: KNativePointer): KNativePointer { + throw new Error('Not implemented'); + } + + _CreateSuggestionInfo(context: KNativePointer, kind: KNativePointer, args: string[], + argc: number, substitutionCode: string, title: string, range?: KNativePointer): KNativePointer { + throw new Error('Not implemented'); + } + + _LogDiagnostic(context: KNativePointer, kind: KNativePointer, argv: string[], argc: number, pos: KNativePointer): void { + throw new Error('Not implemented'); + } + + _LogDiagnosticWithSuggestion(context: KNativePointer, diagnosticInfo: KNativePointer, + suggestionInfo?: KNativePointer): void { throw new Error('Not implemented'); } _SetUpSoPath(soPath: string): void { throw new Error('Not implemented'); } + + _MemoryTrackerReset(context: KNativePointer): void { + throw new Error('MemoryTrackerReset was not overloaded by native module initialization'); + } + + _MemoryTrackerGetDelta(context: KNativePointer): void { + throw new Error('MemoryTrackerGetDelta was not overloaded by native module initialization'); + } + + _MemoryTrackerPrintCurrent(context: KNativePointer): void { + throw new Error('MemoryTrackerPrintCurrent was not overloaded by native module initialization'); + } + + _CallExpressionIsTrailingCallConst(context: KNativePointer, node: KNativePointer): boolean { + throw new Error('CallExpressionIsTrailingCallConst was not overloaded by native module initialization'); + } + + _CreateTypeNodeFromTsType(context: KNativePointer, classInstance: KNativePointer): KNativePointer { + throw new Error('Not implemented'); + } + + _JsdocStringFromDeclaration(context: KNativePointer, decl: KNativePointer): KStringPtr { + throw new Error('Not implemented'); + } + + _ProgramSourceFilePathConst(context: KPtr, decl: KPtr): KNativePointer { + throw new Error('Not implemented'); + } } export function initEs2panda(): Es2pandaNativeModule { diff --git a/koala-wrapper/src/arkts-api/class-by-peer.ts b/koala-wrapper/src/arkts-api/class-by-peer.ts index f8d79996c..4306cff12 100644 --- a/koala-wrapper/src/arkts-api/class-by-peer.ts +++ b/koala-wrapper/src/arkts-api/class-by-peer.ts @@ -19,14 +19,15 @@ import { global } from './static/global'; import { KNativePointer, nullptr } from '@koalaui/interop'; import { AstNode, UnsupportedNode } from './peers/AstNode'; -export const nodeByType = new Map([]); +type AstNodeConstructor = new (peer: KNativePointer) => AstNode; +export const nodeByType = new Map([]); const cache = new Map(); export function clearNodeCache(): void { cache.clear(); } -function getOrPut(peer: KNativePointer, create: (peer: KNativePointer) => AstNode): AstNode { +export function getOrPut(peer: KNativePointer, create: (peer: KNativePointer) => AstNode): AstNode { if (cache.has(peer)) { return cache.get(peer)!; } diff --git a/koala-wrapper/src/arkts-api/factory/nodeFactory.ts b/koala-wrapper/src/arkts-api/factory/nodeFactory.ts index f8307a69b..1d67b4fd5 100644 --- a/koala-wrapper/src/arkts-api/factory/nodeFactory.ts +++ b/koala-wrapper/src/arkts-api/factory/nodeFactory.ts @@ -13,7 +13,6 @@ * limitations under the License. */ -import { updateNodeByNode } from '../utilities/private'; import { ArrowFunctionExpression, AssignmentExpression, @@ -22,7 +21,6 @@ import { EtsScript, ExpressionStatement, FunctionDeclaration, - FunctionExpression, IfStatement, MethodDefinition, NumberLiteral, @@ -32,7 +30,6 @@ import { ETSStringLiteralType, } from '../types'; import { MemberExpression } from '../to-be-generated/MemberExpression'; -import { AstNode } from '../peers/AstNode'; import { AnnotationUsage, BinaryExpression, @@ -48,6 +45,7 @@ import { ETSTypeReferencePart, ETSUndefinedType, ETSUnionType, + FunctionExpression, FunctionSignature, Identifier, ImportSpecifier, @@ -75,13 +73,18 @@ import { Property, TemplateLiteral, ArrayExpression, + AnnotationDeclaration, TryStatement, + TSClassImplements, ForUpdateStatement, ForInStatement, ForOfStatement, + SwitchStatement, + SwitchCaseStatement, + SpreadElement, + BreakStatement, + ClassStaticBlock, } from '../../generated'; -import { Es2pandaModifierFlags } from '../../generated/Es2pandaEnums'; -import { classPropertySetOptional, hasModifierFlag } from '../utilities/public'; import { updateIdentifier } from '../node-utilities/Identifier'; import { updateCallExpression } from '../node-utilities/CallExpression'; import { updateExpressionStatement } from '../node-utilities/ExpressionStatement'; @@ -134,328 +137,417 @@ import { updateObjectExpression } from '../node-utilities/ObjectExpression'; import { updateProperty } from '../node-utilities/Property'; import { updateTemplateLiteral } from '../node-utilities/TemplateLiteral'; import { updateArrayExpression } from '../node-utilities/ArrayExpression'; +import { updateAnnotationDeclaration } from '../node-utilities/AnnotationDeclaration'; import { updateTryStatement } from '../node-utilities/TryStatement'; +import { updateTSClassImplements } from '../node-utilities/TSClassImplements'; import { updateForUpdateStatement } from '../node-utilities/ForUpdateStatement'; import { updateForInStatement } from '../node-utilities/ForInStatement'; import { updateForOfStatement } from '../node-utilities/ForOfStatement'; +import { updateSwitchStatement } from '../node-utilities/SwitchStatement'; +import { updateSwitchCaseStatement } from '../node-utilities/SwitchCaseStatement'; +import { updateSpreadElement } from '../node-utilities/SpreadElement'; +import { updateBreakStatement } from '../node-utilities/BreakStatement'; +import { updateClassStaticBlock } from '../node-utilities/ClassStaticBlock'; export const factory = { - get createIdentifier() { - return Identifier.create2Identifier + get createIdentifier(): (...args: Parameters) => Identifier { + return Identifier.create2Identifier; + }, + get updateIdentifier(): (...args: Parameters) => Identifier { + return updateIdentifier; + }, + get createCallExpression(): (...args: Parameters) => CallExpression { + return CallExpression.create; }, - get updateIdentifier() { - return updateIdentifier + get updateCallExpression(): (...args: Parameters) => CallExpression { + return updateCallExpression; }, - get createCallExpression() { - return CallExpression.create + get createExpressionStatement(): (...args: Parameters) => ExpressionStatement { + return ExpressionStatement.create; }, - get updateCallExpression() { - return updateCallExpression + get updateExpressionStatement(): (...args: Parameters) => ExpressionStatement { + return updateExpressionStatement; }, - get createExpressionStatement() { - return ExpressionStatement.create + get createMemberExpression(): (...args: Parameters) => MemberExpression { + return MemberExpression.create; }, - get updateExpressionStatement() { - return updateExpressionStatement + get updateMemberExpression(): (...args: Parameters) => MemberExpression { + return updateMemberExpression; }, - get createMemberExpression() { - return MemberExpression.create + get createEtsScript(): (...args: Parameters) => EtsScript { + return EtsScript.createFromSource; }, - get updateMemberExpression() { - return updateMemberExpression + get updateEtsScript(): (...args: Parameters) => EtsScript { + return EtsScript.updateByStatements; }, - get createEtsScript() { - return EtsScript.createFromSource + get createFunctionDeclaration(): (...args: Parameters) => FunctionDeclaration { + return FunctionDeclaration.create; }, - get updateEtsScript() { - return EtsScript.updateByStatements + get updateFunctionDeclaration(): (...args: Parameters) => FunctionDeclaration { + return updateFunctionDeclaration; }, - get createFunctionDeclaration() { - return FunctionDeclaration.create + get createBlock(): (...args: Parameters) => BlockStatement { + return BlockStatement.createBlockStatement; }, - get updateFunctionDeclaration() { - return updateFunctionDeclaration + get updateBlock(): (...args: Parameters) => BlockStatement { + return updateBlockStatement; }, - get createBlock() { - return BlockStatement.createBlockStatement + get createArrowFunction(): (...args: Parameters) => ArrowFunctionExpression { + return ArrowFunctionExpression.create; }, - get updateBlock() { - return updateBlockStatement + get updateArrowFunction(): (...args: Parameters) => ArrowFunctionExpression { + return updateArrowFunctionExpression; }, - get createArrowFunction() { - return ArrowFunctionExpression.create + get createScriptFunction(): (...args: Parameters) => ScriptFunction { + return ScriptFunction.createScriptFunction; }, - get updateArrowFunction() { - return updateArrowFunctionExpression + get updateScriptFunction(): (...args: Parameters) => ScriptFunction { + return updateScriptFunction; }, - get createScriptFunction() { - return ScriptFunction.createScriptFunction + get createStringLiteral(): (...args: Parameters) => StringLiteral { + return StringLiteral.create1StringLiteral; }, - get updateScriptFunction() { - return updateScriptFunction + get updateStringLiteral(): (...args: Parameters) => StringLiteral { + return updateStringLiteral; }, - get createStringLiteral() { - return StringLiteral.create1StringLiteral + get create1StringLiteral(): (...args: Parameters) => StringLiteral { + return StringLiteral.create1StringLiteral; }, - get updateStringLiteral() { - return updateStringLiteral + get update1StringLiteral(): (...args: Parameters) => StringLiteral { + return updateStringLiteral; }, - get create1StringLiteral() { - return StringLiteral.create1StringLiteral + get createNumericLiteral(): (...args: Parameters) => NumberLiteral { + return NumberLiteral.create; }, - get update1StringLiteral() { - return updateStringLiteral + get updateNumericLiteral(): (...args: Parameters) => NumberLiteral { + return updateNumberLiteral; }, - get createNumericLiteral() { - return NumberLiteral.create + get createParameterDeclaration(): ( + ...args: Parameters + ) => ETSParameterExpression { + return ETSParameterExpression.create; }, - get updateNumericLiteral() { - return updateNumberLiteral + get updateParameterDeclaration(): ( + ...args: Parameters + ) => ETSParameterExpression { + return updateETSParameterExpression; }, - get createParameterDeclaration() { - return ETSParameterExpression.create + get createTypeParameter(): (...args: Parameters) => TSTypeParameter { + return TSTypeParameter.createTSTypeParameter; }, - get updateParameterDeclaration() { - return updateETSParameterExpression + get updateTypeParameter(): (...args: Parameters) => TSTypeParameter { + return updateTSTypeParameter; }, - get createTypeParameter() { - return TSTypeParameter.createTSTypeParameter + get createTypeParameterDeclaration(): ( + ...args: Parameters + ) => TSTypeParameterDeclaration { + return TSTypeParameterDeclaration.createTSTypeParameterDeclaration; }, - get updateTypeParameter() { - return updateTSTypeParameter + get updateTypeParameterDeclaration(): ( + ...args: Parameters + ) => TSTypeParameterDeclaration { + return updateTSTypeParameterDeclaration; }, - get createTypeParameterDeclaration() { - return TSTypeParameterDeclaration.createTSTypeParameterDeclaration + get createPrimitiveType(): ( + ...args: Parameters + ) => ETSPrimitiveType { + return ETSPrimitiveType.createETSPrimitiveType; }, - get updateTypeParameterDeclaration() { - return updateTSTypeParameterDeclaration + get updatePrimitiveType(): (...args: Parameters) => ETSPrimitiveType { + return updateETSPrimitiveType; }, - get createPrimitiveType() { - return ETSPrimitiveType.createETSPrimitiveType + get createTypeReference(): ( + ...args: Parameters + ) => ETSTypeReference { + return ETSTypeReference.createETSTypeReference; }, - get updatePrimitiveType() { - return updateETSPrimitiveType + get updateTypeReference(): (...args: Parameters) => ETSTypeReference { + return updateETSTypeReference; }, - get createTypeReference() { - return ETSTypeReference.createETSTypeReference + get createTypeReferencePart(): ( + ...args: Parameters + ) => ETSTypeReferencePart { + return ETSTypeReferencePart.createETSTypeReferencePart; }, - get updateTypeReference() { - return updateETSTypeReference + get updateTypeReferencePart(): (...args: Parameters) => ETSTypeReferencePart { + return updateETSTypeReferencePart; }, - get createTypeReferencePart() { - return ETSTypeReferencePart.createETSTypeReferencePart + get createImportDeclaration(): ( + ...args: Parameters + ) => ETSImportDeclaration { + return ETSImportDeclaration.createETSImportDeclaration; }, - get updateTypeReferencePart() { - return updateETSTypeReferencePart + get updateImportDeclaration(): (...args: Parameters) => ETSImportDeclaration { + return updateETSImportDeclaration; }, - get createImportDeclaration() { - return ETSImportDeclaration.createETSImportDeclaration + get createImportSpecifier(): ( + ...args: Parameters + ) => ImportSpecifier { + return ImportSpecifier.createImportSpecifier; }, - get updateImportDeclaration() { - return updateETSImportDeclaration + get updateImportSpecifier(): (...args: Parameters) => ImportSpecifier { + return updateImportSpecifier; }, - get createImportSpecifier() { - return ImportSpecifier.createImportSpecifier + get createVariableDeclaration(): (...args: Parameters) => VariableDeclaration { + return VariableDeclaration.create; }, - get updateImportSpecifier() { - return updateImportSpecifier + get updateVariableDeclaration(): (...args: Parameters) => VariableDeclaration { + return updateVariableDeclaration; }, - get createVariableDeclaration() { - return VariableDeclaration.create + get createVariableDeclarator(): (...args: Parameters) => VariableDeclarator { + return VariableDeclarator.create; }, - get updateVariableDeclaration() { - return updateVariableDeclaration + get updateVariableDeclarator(): (...args: Parameters) => VariableDeclarator { + return updateVariableDeclarator; }, - get createVariableDeclarator() { - return VariableDeclarator.create + get createUnionType(): (...args: Parameters) => ETSUnionType { + return ETSUnionType.createETSUnionType; }, - get updateVariableDeclarator() { - return updateVariableDeclarator + get updateUnionType(): (...args: Parameters) => ETSUnionType { + return updateETSUnionType; }, - get createUnionType() { - return ETSUnionType.createETSUnionType + get createReturnStatement(): ( + ...args: Parameters + ) => ReturnStatement { + return ReturnStatement.create1ReturnStatement; }, - get updateUnionType() { - return updateETSUnionType + get updateReturnStatement(): (...args: Parameters) => ReturnStatement { + return updateReturnStatement; }, - get createReturnStatement() { - return ReturnStatement.create1ReturnStatement + get createIfStatement(): (...args: Parameters) => IfStatement { + return IfStatement.create; }, - get updateReturnStatement() { - return updateReturnStatement + get updateIfStatement(): (...args: Parameters) => IfStatement { + return updateIfStatement; }, - get createIfStatement() { - return IfStatement.create + get createBinaryExpression(): ( + ...args: Parameters + ) => BinaryExpression { + return BinaryExpression.createBinaryExpression; }, - get updateIfStatement() { - return updateIfStatement + get updateBinaryExpression(): (...args: Parameters) => BinaryExpression { + return updateBinaryExpression; }, - get createBinaryExpression() { - return BinaryExpression.createBinaryExpression + get createClassDeclaration(): ( + ...args: Parameters + ) => ClassDeclaration { + return ClassDeclaration.createClassDeclaration; }, - get updateBinaryExpression() { - return updateBinaryExpression + get updateClassDeclaration(): (...args: Parameters) => ClassDeclaration { + return updateClassDeclaration; }, - get createClassDeclaration() { - return ClassDeclaration.createClassDeclaration + get createStructDeclaration(): (...args: Parameters) => StructDeclaration { + return StructDeclaration.create; }, - get updateClassDeclaration() { - return updateClassDeclaration + get updateStructDeclaration(): (...args: Parameters) => StructDeclaration { + return updateStructDeclaration; }, - get createStructDeclaration() { - return StructDeclaration.create + get createClassDefinition(): ( + ...args: Parameters + ) => ClassDefinition { + return ClassDefinition.createClassDefinition; }, - get updateStructDeclaration() { - return updateStructDeclaration + get updateClassDefinition(): (...args: Parameters) => ClassDefinition { + return updateClassDefinition; }, - get createClassDefinition() { - return ClassDefinition.createClassDefinition + get createClassProperty(): (...args: Parameters) => ClassProperty { + return ClassProperty.createClassProperty; }, - get updateClassDefinition() { - return updateClassDefinition + get updateClassProperty(): (...args: Parameters) => ClassProperty { + return updateClassProperty; }, - get createClassProperty() { - return ClassProperty.createClassProperty + get createFunctionType(): (...args: Parameters) => ETSFunctionType { + return ETSFunctionType.createETSFunctionType; }, - get updateClassProperty() { - return updateClassProperty + get updateFunctionType(): (...args: Parameters) => ETSFunctionType { + return updateETSFunctionType; }, - get createFunctionType() { - return ETSFunctionType.createETSFunctionType + get createFunctionExpression(): ( + ...args: Parameters + ) => FunctionExpression { + return FunctionExpression.createFunctionExpression; }, - get updateFunctionType() { - return updateETSFunctionType + get updateFunctionExpression(): (...args: Parameters) => FunctionExpression { + return updateFunctionExpression; }, - get createFunctionExpression() { - return FunctionExpression.create + get createMethodDefinition(): (...args: Parameters) => MethodDefinition { + return MethodDefinition.create; }, - get updateFunctionExpression() { - return updateFunctionExpression + get updateMethodDefinition(): (...args: Parameters) => MethodDefinition { + return updateMethodDefinition; }, - get createMethodDefinition() { - return MethodDefinition.create + get createSuperExpression(): ( + ...args: Parameters + ) => SuperExpression { + return SuperExpression.createSuperExpression; }, - get updateMethodDefinition() { - return updateMethodDefinition + get updateSuperExpression(): (...args: Parameters) => SuperExpression { + return updateSuperExpression; }, - get createSuperExpression() { - return SuperExpression.createSuperExpression + get createTSTypeParameterInstantiation(): ( + ...args: Parameters + ) => TSTypeParameterInstantiation { + return TSTypeParameterInstantiation.createTSTypeParameterInstantiation; }, - get updateSuperExpression() { - return updateSuperExpression + get updateTSTypeParameterInstantiation(): ( + ...args: Parameters + ) => TSTypeParameterInstantiation { + return updateTSTypeParameterInstantiation; }, - get createTSTypeParameterInstantiation() { - return TSTypeParameterInstantiation.createTSTypeParameterInstantiation + get createInterfaceDeclaration(): ( + ...args: Parameters + ) => TSInterfaceDeclaration { + return TSInterfaceDeclaration.createTSInterfaceDeclaration; }, - get updateTSTypeParameterInstantiation() { - return updateTSTypeParameterInstantiation + get updateInterfaceDeclaration(): ( + ...args: Parameters + ) => TSInterfaceDeclaration { + return updateTSInterfaceDeclaration; }, - get createInterfaceDeclaration() { - return TSInterfaceDeclaration.createTSInterfaceDeclaration + get createInterfaceBody(): (...args: Parameters) => TSInterfaceBody { + return TSInterfaceBody.createTSInterfaceBody; }, - get updateInterfaceDeclaration() { - return updateTSInterfaceDeclaration + get updateInterfaceBody(): (...args: Parameters) => TSInterfaceBody { + return updateTSInterfaceBody; }, - get createInterfaceBody() { - return TSInterfaceBody.createTSInterfaceBody + get createUndefinedLiteral(): ( + ...args: Parameters + ) => UndefinedLiteral { + return UndefinedLiteral.createUndefinedLiteral; }, - get updateInterfaceBody() { - return updateTSInterfaceBody + get updateUndefinedLiteral(): (...args: Parameters) => UndefinedLiteral { + return updateUndefinedLiteral; }, - get createUndefinedLiteral() { - return UndefinedLiteral.createUndefinedLiteral + get createAnnotationDeclaration(): ( + ...args: Parameters + ) => AnnotationDeclaration { + return AnnotationDeclaration.create1AnnotationDeclaration; }, - get updateUndefinedLiteral() { - return updateUndefinedLiteral + get updateAnnotationDeclaration(): ( + ...args: Parameters + ) => AnnotationDeclaration { + return updateAnnotationDeclaration; }, - get createAnnotationUsage() { - return AnnotationUsage.createAnnotationUsage + get createAnnotationUsage(): ( + ...args: Parameters + ) => AnnotationUsage { + return AnnotationUsage.createAnnotationUsage; }, - get updateAnnotationUsage() { - return updateAnnotationUsage + get updateAnnotationUsage(): (...args: Parameters) => AnnotationUsage { + return updateAnnotationUsage; }, - get create1AnnotationUsage(): (...args: Parameters) => AnnotationUsage { + get create1AnnotationUsage(): ( + ...args: Parameters + ) => AnnotationUsage { return AnnotationUsage.create1AnnotationUsage; }, get update1AnnotationUsage(): (...args: Parameters) => AnnotationUsage { return update1AnnotationUsage; }, - get createAssignmentExpression() { - return AssignmentExpression.create + get createAssignmentExpression(): ( + ...args: Parameters + ) => AssignmentExpression { + return AssignmentExpression.create; }, - get updateAssignmentExpression() { - return updateAssignmentExpression + get updateAssignmentExpression(): (...args: Parameters) => AssignmentExpression { + return updateAssignmentExpression; }, - get createETSUndefinedType() { - return ETSUndefinedType.createETSUndefinedType + get createETSUndefinedType(): ( + ...args: Parameters + ) => ETSUndefinedType { + return ETSUndefinedType.createETSUndefinedType; }, - get updateETSUndefinedType() { - return updateETSUndefinedType + get updateETSUndefinedType(): (...args: Parameters) => ETSUndefinedType { + return updateETSUndefinedType; }, - get createFunctionSignature() { - return FunctionSignature.createFunctionSignature + get createFunctionSignature(): ( + ...args: Parameters + ) => FunctionSignature { + return FunctionSignature.createFunctionSignature; }, - get createConditionalExpression() { - return ConditionalExpression.createConditionalExpression + get createConditionalExpression(): ( + ...args: Parameters + ) => ConditionalExpression { + return ConditionalExpression.createConditionalExpression; }, - get updateConditionalExpression() { - return updateConditionalExpression + get updateConditionalExpression(): ( + ...args: Parameters + ) => ConditionalExpression { + return updateConditionalExpression; }, - get createTSAsExpression() { - return TSAsExpression.createTSAsExpression + get createTSAsExpression(): (...args: Parameters) => TSAsExpression { + return TSAsExpression.createTSAsExpression; }, - get updateTSAsExpression() { - return updateTSAsExpression + get updateTSAsExpression(): (...args: Parameters) => TSAsExpression { + return updateTSAsExpression; }, - get createThisExpression() { - return ThisExpression.createThisExpression + get createThisExpression(): (...args: Parameters) => ThisExpression { + return ThisExpression.createThisExpression; }, - get updateThisExpression() { - return updateThisExpression + get updateThisExpression(): (...args: Parameters) => ThisExpression { + return updateThisExpression; }, - get createTSTypeAliasDeclaration() { - return TSTypeAliasDeclaration.createTSTypeAliasDeclaration + get createTSTypeAliasDeclaration(): ( + ...args: Parameters + ) => TSTypeAliasDeclaration { + return TSTypeAliasDeclaration.createTSTypeAliasDeclaration; }, - get updateTSTypeAliasDeclaration() { - return updateTSTypeAliasDeclaration + get updateTSTypeAliasDeclaration(): ( + ...args: Parameters + ) => TSTypeAliasDeclaration { + return updateTSTypeAliasDeclaration; }, - get createTSNonNullExpression() { - return TSNonNullExpression.createTSNonNullExpression + get createTSNonNullExpression(): ( + ...args: Parameters + ) => TSNonNullExpression { + return TSNonNullExpression.createTSNonNullExpression; }, - get updateTSNonNullExpression() { - return updateTSNonNullExpression + get updateTSNonNullExpression(): (...args: Parameters) => TSNonNullExpression { + return updateTSNonNullExpression; }, - get createChainExpression() { - return ChainExpression.createChainExpression + get createChainExpression(): ( + ...args: Parameters + ) => ChainExpression { + return ChainExpression.createChainExpression; }, - get updateChainExpression() { - return updateChainExpression + get updateChainExpression(): (...args: Parameters) => ChainExpression { + return updateChainExpression; }, - get createBlockExpression() { - return BlockExpression.createBlockExpression + get createBlockExpression(): ( + ...args: Parameters + ) => BlockExpression { + return BlockExpression.createBlockExpression; }, - get updateBlockExpression() { - return updateBlockExpression + get updateBlockExpression(): (...args: Parameters) => BlockExpression { + return updateBlockExpression; }, - get createNullLiteral() { - return NullLiteral.createNullLiteral + get createNullLiteral(): (...args: Parameters) => NullLiteral { + return NullLiteral.createNullLiteral; }, - get updateNullLiteral() { - return updateNullLiteral + get updateNullLiteral(): (...args: Parameters) => NullLiteral { + return updateNullLiteral; }, - get createETSNewClassInstanceExpression() { - return ETSNewClassInstanceExpression.createETSNewClassInstanceExpression + get createETSNewClassInstanceExpression(): ( + ...args: Parameters + ) => ETSNewClassInstanceExpression { + return ETSNewClassInstanceExpression.createETSNewClassInstanceExpression; }, - get updateETSNewClassInstanceExpression() { - return updateETSNewClassInstanceExpression + get updateETSNewClassInstanceExpression(): ( + ...args: Parameters + ) => ETSNewClassInstanceExpression { + return updateETSNewClassInstanceExpression; }, - get createETSStringLiteralType() { + get createETSStringLiteralType(): ( + ...args: Parameters + ) => ETSStringLiteralType { return ETSStringLiteralType.create; }, get createBooleanLiteral(): (...args: Parameters) => BooleanLiteral { return BooleanLiteral.createBooleanLiteral; }, - get createObjectExpression(): (...args: Parameters) => ObjectExpression { + get createObjectExpression(): ( + ...args: Parameters + ) => ObjectExpression { return ObjectExpression.createObjectExpression; }, get updateObjectExpression(): (...args: Parameters) => ObjectExpression { @@ -467,13 +559,17 @@ export const factory = { get updateProperty(): (...args: Parameters) => Property { return updateProperty; }, - get createTemplateLiteral(): (...args: Parameters) => TemplateLiteral { + get createTemplateLiteral(): ( + ...args: Parameters + ) => TemplateLiteral { return TemplateLiteral.createTemplateLiteral; }, get updateTemplateLiteral(): (...args: Parameters) => TemplateLiteral { return updateTemplateLiteral; }, - get createArrayExpression(): (...args: Parameters) => ArrayExpression { + get createArrayExpression(): ( + ...args: Parameters + ) => ArrayExpression { return ArrayExpression.createArrayExpression; }, get updateArrayExpression(): (...args: Parameters) => ArrayExpression { @@ -485,6 +581,14 @@ export const factory = { get updateTryStatement(): (...args: Parameters) => TryStatement { return updateTryStatement; }, + get createTSClassImplements(): ( + ...args: Parameters + ) => TSClassImplements { + return TSClassImplements.createTSClassImplements; + }, + get UpdateTSClassImplements(): (...args: Parameters) => TSClassImplements { + return updateTSClassImplements; + }, get createForUpdateStatement(): ( ...args: Parameters ) => ForUpdateStatement { @@ -505,8 +609,44 @@ export const factory = { get updateForOfStatement(): (...args: Parameters) => ForOfStatement { return updateForOfStatement; }, + get createSwitchStatement(): ( + ...args: Parameters + ) => SwitchStatement { + return SwitchStatement.createSwitchStatement; + }, + get updateSwitchStatement(): (...args: Parameters) => SwitchStatement { + return updateSwitchStatement; + }, + get createSwitchCaseStatement(): ( + ...args: Parameters + ) => SwitchCaseStatement { + return SwitchCaseStatement.createSwitchCaseStatement; + }, + get updateSwitchCaseStatement(): (...args: Parameters) => SwitchCaseStatement { + return updateSwitchCaseStatement; + }, + get createSpreadElement(): (...args: Parameters) => SpreadElement { + return SpreadElement.createSpreadElement; + }, + get updateSpreadElement(): (...args: Parameters) => SpreadElement { + return updateSpreadElement; + }, + get createBreakStatement(): (...args: Parameters) => BreakStatement { + return BreakStatement.createBreakStatement; + }, + get updateBreakStatement(): (...args: Parameters) => BreakStatement { + return updateBreakStatement; + }, + get createClassStaticBlock(): ( + ...args: Parameters + ) => ClassStaticBlock { + return ClassStaticBlock.createClassStaticBlock; + }, + get updateClassStaticBlock(): (...args: Parameters) => ClassStaticBlock { + return updateClassStaticBlock; + }, /** @deprecated */ createTypeParameter1_(name: Identifier, constraint?: TypeNode, defaultType?: TypeNode) { - return TSTypeParameter.createTSTypeParameter(Identifier.create1Identifier(name.name), constraint, defaultType) + return TSTypeParameter.createTSTypeParameter(Identifier.create1Identifier(name.name), constraint, defaultType); }, -} +}; diff --git a/koala-wrapper/src/arkts-api/factory/nodeTests.ts b/koala-wrapper/src/arkts-api/factory/nodeTests.ts index 62c02275a..6b8653469 100644 --- a/koala-wrapper/src/arkts-api/factory/nodeTests.ts +++ b/koala-wrapper/src/arkts-api/factory/nodeTests.ts @@ -21,10 +21,8 @@ import { EtsScript, ExpressionStatement, FunctionDeclaration, - FunctionExpression, IfStatement, MethodDefinition, - // ScriptFunction, StructDeclaration, VariableDeclaration, VariableDeclarator, @@ -35,15 +33,15 @@ import { MemberExpression } from "../to-be-generated/MemberExpression" import { AstNode } from "../peers/AstNode" export function isCallExpression(node: AstNode): node is CallExpression { - return node instanceof CallExpression + return node instanceof CallExpression; } export function isMemberExpression(node: AstNode): node is MemberExpression { - return node instanceof MemberExpression + return node instanceof MemberExpression; } export function isFunctionDeclaration(node: AstNode): node is FunctionDeclaration { - return node instanceof FunctionDeclaration + return node instanceof FunctionDeclaration; } export function isMethodDefinition(node: AstNode): node is MethodDefinition { @@ -51,43 +49,35 @@ export function isMethodDefinition(node: AstNode): node is MethodDefinition { } export function isEtsScript(node: AstNode): node is EtsScript { - return node instanceof EtsScript + return node instanceof EtsScript; } export function isExpressionStatement(node: AstNode): node is ExpressionStatement { - return node instanceof ExpressionStatement + return node instanceof ExpressionStatement; } export function isArrowFunctionExpression(node: AstNode): node is ArrowFunctionExpression { - return node instanceof ArrowFunctionExpression + return node instanceof ArrowFunctionExpression; } export function isStructDeclaration(node: AstNode): node is StructDeclaration { - return node instanceof StructDeclaration -} - -export function isFunctionExpression(node: AstNode): node is FunctionExpression { - return node instanceof FunctionExpression + return node instanceof StructDeclaration; } export function isEtsParameterExpression(node: AstNode): node is ETSParameterExpression { - return node instanceof ETSParameterExpression + return node instanceof ETSParameterExpression; } export function isVariableDeclaration(node: AstNode): node is VariableDeclaration { - return node instanceof VariableDeclaration + return node instanceof VariableDeclaration; } -// export function isScriptFunction(node: AstNode): node is ScriptFunction { -// return node instanceof ScriptFunction -// } - export function isIfStatement(node: AstNode): node is IfStatement { - return node instanceof IfStatement + return node instanceof IfStatement; } export function isVariableDeclarator(node: AstNode): node is VariableDeclarator { - return node instanceof VariableDeclarator + return node instanceof VariableDeclarator; } export function isAssignmentExpression(node: AstNode): node is AssignmentExpression { diff --git a/koala-wrapper/src/arkts-api/index.ts b/koala-wrapper/src/arkts-api/index.ts index 5976735bf..647c8e2ee 100644 --- a/koala-wrapper/src/arkts-api/index.ts +++ b/koala-wrapper/src/arkts-api/index.ts @@ -15,6 +15,7 @@ export * from '../Es2pandaEnums'; export * from '../generated/Es2pandaEnums'; +export * from '../generated/peers/AnnotationDeclaration'; export * from '../generated/peers/AnnotationUsage'; export * from '../generated/peers/BlockStatement'; export * from '../generated/peers/ETSPrimitiveType'; @@ -60,12 +61,23 @@ export * from '../generated/peers/TSClassImplements'; export * from '../generated/peers/BooleanLiteral'; export * from '../generated/peers/TSArrayType'; export * from '../generated/peers/ArrayExpression'; +export * from '../generated/peers/TryStatement'; export * from '../generated/peers/ETSNullType'; +export * from '../generated/peers/ETSTuple'; +export * from '../generated/peers/ImportDeclaration'; +export * from '../generated/peers/WhileStatement'; +export * from '../generated/peers/BreakStatement'; +export * from '../generated/peers/ContinueStatement'; +export * from '../generated/peers/SwitchCaseStatement'; +export * from '../generated/peers/SwitchStatement'; +export * from '../generated/peers/ClassStaticBlock'; +export * from '../generated/peers/FunctionExpression'; export * from './types'; export * from './utilities/private'; export * from './utilities/public'; export * from './utilities/performance'; +export * from './utilities/nodeCache'; export * from './factory/nodeFactory'; export * from './factory/nodeTests'; export * from './visitor'; @@ -78,6 +90,10 @@ export * from './peers/Program'; export * from './peers/ImportPathManager'; export * from './peers/SourcePosition'; export * from './peers/SourceRange'; +export * from './peers/Diagnostic'; +export * from './peers/DiagnosticInfo'; +export * from './peers/DiagnosticKind'; +export * from './peers/SuggestionInfo'; export * from './to-be-generated/MemberExpression'; export * from './static/globalUtils'; export { global as arktsGlobal } from './static/global'; diff --git a/koala-wrapper/src/arkts-api/node-utilities/AnnotationDeclaration.ts b/koala-wrapper/src/arkts-api/node-utilities/AnnotationDeclaration.ts new file mode 100644 index 000000000..345d95f21 --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/AnnotationDeclaration.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AnnotationDeclaration, Expression } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; +import { AstNode } from '../peers/AstNode'; + +export function updateAnnotationDeclaration( + original: AnnotationDeclaration, + expr: Expression | undefined, + properties: readonly AstNode[] +): AnnotationDeclaration { + if (isSameNativeObject(expr, original.expr) && isSameNativeObject(properties, original.properties)) { + return original; + } + + const update = updateThenAttach(AnnotationDeclaration.update1AnnotationDeclaration, attachModifiers); + return update(original, expr, properties); +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/ArrowFunctionExpression.ts b/koala-wrapper/src/arkts-api/node-utilities/ArrowFunctionExpression.ts index 9ad9e11af..a787698d7 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ArrowFunctionExpression.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ArrowFunctionExpression.ts @@ -16,6 +16,7 @@ import { ScriptFunction } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { ArrowFunctionExpression } from '../types'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateArrowFunctionExpression( @@ -31,5 +32,9 @@ export function updateArrowFunctionExpression( attachModifiers, (node: ArrowFunctionExpression, original: ArrowFunctionExpression) => node.setAnnotations(original.annotations) ); - return update(original, func); + const newNode = update(original, func); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/BreakStatement.ts b/koala-wrapper/src/arkts-api/node-utilities/BreakStatement.ts new file mode 100644 index 000000000..03ed1b141 --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/BreakStatement.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BreakStatement } from '../../generated'; +import { NodeCache } from '../utilities/nodeCache'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateBreakStatement(original: BreakStatement): BreakStatement { + const update = updateThenAttach(BreakStatement.updateBreakStatement, attachModifiers); + const newNode = update(original); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/CallExpression.ts b/koala-wrapper/src/arkts-api/node-utilities/CallExpression.ts index d1489441a..843798827 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/CallExpression.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/CallExpression.ts @@ -17,6 +17,7 @@ import { TypeNode } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { AstNode } from '../peers/AstNode'; import { CallExpression } from '../types'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateCallExpression( @@ -41,5 +42,9 @@ export function updateCallExpression( (node: CallExpression, original: CallExpression) => !!original.trailingBlock ? node.setTralingBlock(original.trailingBlock) : node ); - return update(original, expression, typeArguments, args, isOptional, trailingComma); + const newNode = update(original, expression, typeArguments, args, isOptional, trailingComma); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ClassDefinition.ts b/koala-wrapper/src/arkts-api/node-utilities/ClassDefinition.ts index 1e9ae0ffb..793c00935 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ClassDefinition.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ClassDefinition.ts @@ -27,35 +27,38 @@ import { MethodDefinition } from '../types'; import { updateThenAttach } from '../utilities/private'; import { Es2pandaClassDefinitionModifiers, Es2pandaModifierFlags } from '../../generated/Es2pandaEnums'; import { classDefinitionFlags } from '../utilities/public'; +import { Es2pandaLanguage } from '..'; export function updateClassDefinition( original: ClassDefinition, ident: Identifier | undefined, typeParams: TSTypeParameterDeclaration | undefined, superTypeParams: TSTypeParameterInstantiation | undefined, - _implements: readonly TSClassImplements[], + classImplements: readonly TSClassImplements[], ctor: MethodDefinition | undefined, superClass: Expression | undefined, body: readonly AstNode[], modifiers: Es2pandaClassDefinitionModifiers, - flags: Es2pandaModifierFlags + flags: Es2pandaModifierFlags, + lang?: Es2pandaLanguage ): ClassDefinition { if ( isSameNativeObject(ident, original.ident) && isSameNativeObject(typeParams, original.typeParams) && isSameNativeObject(superTypeParams, original.superTypeParams) && - isSameNativeObject(_implements, original.implements) && + isSameNativeObject(classImplements, original.implements) && isSameNativeObject(superClass, original.super) && isSameNativeObject(body, original.body) && isSameNativeObject(modifiers, original.modifiers) && - isSameNativeObject(flags, classDefinitionFlags(original)) + isSameNativeObject(flags, classDefinitionFlags(original)) && + (!lang || isSameNativeObject(lang, original.lang)) /* TODO: no getter for ctor */ ) { return original; } const update = updateThenAttach( - ClassDefinition.updateClassDefinition, + ClassDefinition.update3ClassDefinition, (node: ClassDefinition, original: ClassDefinition) => node.setAnnotations(original.annotations) ); return update( @@ -63,11 +66,13 @@ export function updateClassDefinition( ident, typeParams, superTypeParams, - _implements, + classImplements, undefined, superClass, body, modifiers, - flags + flags, + lang ?? original.lang ); } + \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/node-utilities/ClassProperty.ts b/koala-wrapper/src/arkts-api/node-utilities/ClassProperty.ts index 06450c0cb..1d31c2858 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ClassProperty.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ClassProperty.ts @@ -18,6 +18,7 @@ import { isSameNativeObject } from '../peers/ArktsObject'; import { updateThenAttach } from '../utilities/private'; import { classPropertySetOptional, hasModifierFlag } from '../utilities/public'; import { Es2pandaModifierFlags } from '../../generated/Es2pandaEnums'; +import { NodeCache } from '../utilities/nodeCache'; export function updateClassProperty( original: ClassProperty, @@ -47,5 +48,9 @@ export function updateClassProperty( return node; } ); - return update(original, key, value, typeAnnotation, modifiers, isComputed); + const newNode = update(original, key, value, typeAnnotation, modifiers, isComputed); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ClassStaticBlock.ts b/koala-wrapper/src/arkts-api/node-utilities/ClassStaticBlock.ts new file mode 100644 index 000000000..9a49d2274 --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/ClassStaticBlock.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ClassStaticBlock, Expression } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateClassStaticBlock(original: ClassStaticBlock, value?: Expression): ClassStaticBlock { + if (isSameNativeObject(value, original.value)) { + return original; + } + + const update = updateThenAttach(ClassStaticBlock.updateClassStaticBlock, attachModifiers); + const newNode = update(original, value); + return newNode; +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/ETSFunctionType.ts b/koala-wrapper/src/arkts-api/node-utilities/ETSFunctionType.ts index a0e522040..c4e441974 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ETSFunctionType.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ETSFunctionType.ts @@ -17,6 +17,7 @@ import { ETSFunctionType, FunctionSignature } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { attachModifiers, updateThenAttach } from '../utilities/private'; import { Es2pandaScriptFunctionFlags } from '../../generated/Es2pandaEnums'; +import { NodeCache } from '../utilities/nodeCache'; export function updateETSFunctionType( original: ETSFunctionType, @@ -38,5 +39,9 @@ export function updateETSFunctionType( attachModifiers, (node: ETSFunctionType, original: ETSFunctionType) => node.setAnnotations(original.annotations) ); - return update(original, signature, funcFlags); + const newNode = update(original, signature, funcFlags); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ETSParameterExpression.ts b/koala-wrapper/src/arkts-api/node-utilities/ETSParameterExpression.ts index b9e8f0514..23923cd75 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ETSParameterExpression.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ETSParameterExpression.ts @@ -17,6 +17,7 @@ import { Identifier } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { AstNode } from '../peers/AstNode'; import { ETSParameterExpression } from '../types'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateETSParameterExpression( @@ -37,7 +38,11 @@ export function updateETSParameterExpression( (node: ETSParameterExpression, original: ETSParameterExpression) => { node.annotations = original.annotations; return node; - } + }, ); - return update(original, identifier, initializer); + const newNode = update(original, identifier, initializer); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ETSUnionType.ts b/koala-wrapper/src/arkts-api/node-utilities/ETSUnionType.ts index c8dc20720..07ab40282 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ETSUnionType.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ETSUnionType.ts @@ -15,6 +15,7 @@ import { ETSUnionType, TypeNode } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateETSUnionType(original: ETSUnionType, types: readonly TypeNode[]): ETSUnionType { @@ -23,5 +24,9 @@ export function updateETSUnionType(original: ETSUnionType, types: readonly TypeN } const update = updateThenAttach(ETSUnionType.updateETSUnionType, attachModifiers); - return update(original, types); + const newNode = update(original, types); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/FunctionExpression.ts b/koala-wrapper/src/arkts-api/node-utilities/FunctionExpression.ts index ba2135801..b601e9f46 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/FunctionExpression.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/FunctionExpression.ts @@ -13,16 +13,15 @@ * limitations under the License. */ -import { ScriptFunction } from '../../generated'; +import { ScriptFunction, FunctionExpression } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; -import { FunctionExpression } from '../types'; import { attachModifiers, updateThenAttach } from '../utilities/private'; -export function updateFunctionExpression(original: FunctionExpression, expression: ScriptFunction): FunctionExpression { - if (isSameNativeObject(expression, original.scriptFunction)) { +export function updateFunctionExpression(original: FunctionExpression, func?: ScriptFunction): FunctionExpression { + if (isSameNativeObject(func, original.function)) { return original; } - const update = updateThenAttach(FunctionExpression.update, attachModifiers); - return update(original, expression); + const update = updateThenAttach(FunctionExpression.updateFunctionExpression, attachModifiers); + return update(original, func); } diff --git a/koala-wrapper/src/arkts-api/node-utilities/Identifier.ts b/koala-wrapper/src/arkts-api/node-utilities/Identifier.ts index f31d25fc6..3a1fff783 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/Identifier.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/Identifier.ts @@ -15,6 +15,7 @@ import { Identifier, TypeNode } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateIdentifier(original: Identifier, name: string, typeAnnotation?: TypeNode): Identifier { @@ -23,5 +24,9 @@ export function updateIdentifier(original: Identifier, name: string, typeAnnotat } const update = updateThenAttach(Identifier.update2Identifier, attachModifiers); - return update(original, name, typeAnnotation); + const newNode = update(original, name, typeAnnotation); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/MethodDefinition.ts b/koala-wrapper/src/arkts-api/node-utilities/MethodDefinition.ts index 42006a300..6318a0c84 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/MethodDefinition.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/MethodDefinition.ts @@ -20,6 +20,7 @@ import { MethodDefinition } from '../types'; import { updateThenAttach } from '../utilities/private'; import { Es2pandaMethodDefinitionKind } from '../../generated/Es2pandaEnums'; import { ScriptFunction } from '../../generated'; +import { NodeCache } from '../utilities/nodeCache'; export function updateMethodDefinition( original: MethodDefinition, @@ -42,5 +43,9 @@ export function updateMethodDefinition( const update = updateThenAttach(MethodDefinition.update, (node: MethodDefinition, original: MethodDefinition) => node.setOverloads(original.overloads) ); - return update(original, kind, key, value, modifiers, isComputed); + const newNode = update(original, kind, key, value, modifiers, isComputed); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/Property.ts b/koala-wrapper/src/arkts-api/node-utilities/Property.ts index 4b0a33c5b..fdb1443c5 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/Property.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/Property.ts @@ -15,6 +15,7 @@ import { Expression, Property } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateProperty(original: Property, key?: Expression, value?: Expression): Property { @@ -23,5 +24,10 @@ export function updateProperty(original: Property, key?: Expression, value?: Exp } const update = updateThenAttach(Property.updateProperty, attachModifiers); - return update(original, key, value); + const newNode = update(original, key, value); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; + } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ReturnStatement.ts b/koala-wrapper/src/arkts-api/node-utilities/ReturnStatement.ts index 9fc05a371..984e0aa5d 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ReturnStatement.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ReturnStatement.ts @@ -15,6 +15,7 @@ import { Expression, ReturnStatement } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateReturnStatement(original: ReturnStatement, argument?: Expression): ReturnStatement { @@ -23,5 +24,9 @@ export function updateReturnStatement(original: ReturnStatement, argument?: Expr } const update = updateThenAttach(ReturnStatement.update1ReturnStatement, attachModifiers); - return update(original, argument); + const newNode = update(original, argument); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ScriptFunction.ts b/koala-wrapper/src/arkts-api/node-utilities/ScriptFunction.ts index 745a5398c..16e30342a 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ScriptFunction.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ScriptFunction.ts @@ -16,6 +16,7 @@ import { FunctionSignature, ScriptFunction } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { AstNode } from '../peers/AstNode'; +import { NodeCache } from '../utilities/nodeCache'; import { updateThenAttach } from '../utilities/private'; export function updateScriptFunction( @@ -42,5 +43,9 @@ export function updateScriptFunction( (node: ScriptFunction, original: ScriptFunction) => (!!original.id ? node.setIdent(original.id) : node), (node: ScriptFunction, original: ScriptFunction) => node.setAnnotations(original.annotations) ); - return update(original, databody, datasignature, datafuncFlags, dataflags); + const newNode = update(original, databody, datasignature, datafuncFlags, dataflags); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/SpreadElement.ts b/koala-wrapper/src/arkts-api/node-utilities/SpreadElement.ts new file mode 100644 index 000000000..f8e24176d --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/SpreadElement.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Expression, SpreadElement } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; +import { Es2pandaAstNodeType } from '../../Es2pandaEnums'; +import { global } from '../static/global'; + +export function updateSpreadElement( + original: SpreadElement, + nodeType: Es2pandaAstNodeType, + argument?: Expression +): SpreadElement { + const originalNodeType = global.generatedEs2panda._AstNodeTypeConst(global.context, original.peer); + if (isSameNativeObject(argument, original.argument) && isSameNativeObject(nodeType, originalNodeType)) { + return original; + } + + const update = updateThenAttach(SpreadElement.updateSpreadElement, attachModifiers); + return update(original, nodeType, argument); +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/SwitchCaseStatement.ts b/koala-wrapper/src/arkts-api/node-utilities/SwitchCaseStatement.ts new file mode 100644 index 000000000..4e3e528ef --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/SwitchCaseStatement.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Expression, Statement, SwitchCaseStatement } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateSwitchCaseStatement( + original: SwitchCaseStatement, + test: Expression | undefined, + consequent: readonly Statement[] +): SwitchCaseStatement { + if (isSameNativeObject(test, original.test) && isSameNativeObject(consequent, original.consequent)) { + return original; + } + + const update = updateThenAttach(SwitchCaseStatement.updateSwitchCaseStatement, attachModifiers); + return update(original, test, consequent); +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/SwitchStatement.ts b/koala-wrapper/src/arkts-api/node-utilities/SwitchStatement.ts new file mode 100644 index 000000000..cf601ee8a --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/SwitchStatement.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Expression, SwitchCaseStatement, SwitchStatement } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateSwitchStatement( + original: SwitchStatement, + discriminant: Expression | undefined, + cases: readonly SwitchCaseStatement[] +): SwitchStatement { + if (isSameNativeObject(discriminant, original.discriminant) && isSameNativeObject(cases, original.cases)) { + return original; + } + + const update = updateThenAttach(SwitchStatement.updateSwitchStatement, attachModifiers); + return update(original, discriminant, cases); +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/TSClassImplements.ts b/koala-wrapper/src/arkts-api/node-utilities/TSClassImplements.ts new file mode 100644 index 000000000..1e51e9c44 --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/TSClassImplements.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Expression, TSClassImplements, TSTypeParameterInstantiation } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateTSClassImplements( + original: TSClassImplements, + expression?: Expression, + typeParameters?: TSTypeParameterInstantiation +): TSClassImplements { + if (isSameNativeObject(expression, original.expr) && isSameNativeObject(typeParameters, original.typeParameters)) { + return original; + } + + const update = updateThenAttach(TSClassImplements.updateTSClassImplements, attachModifiers); + return update(original, expression, typeParameters); +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/TSInterfaceDeclaration.ts b/koala-wrapper/src/arkts-api/node-utilities/TSInterfaceDeclaration.ts index d268c5d82..1d607b245 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/TSInterfaceDeclaration.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/TSInterfaceDeclaration.ts @@ -38,6 +38,10 @@ export function updateTSInterfaceDeclaration( return original; } - const update = updateThenAttach(TSInterfaceDeclaration.updateTSInterfaceDeclaration, attachModifiers); + const update = updateThenAttach( + TSInterfaceDeclaration.updateTSInterfaceDeclaration, + attachModifiers, + (node: TSInterfaceDeclaration, original: TSInterfaceDeclaration) => node.setAnnotations(original.annotations) + ); return update(original, _extends, id, typeParams, body, isStatic, isExternal); } diff --git a/koala-wrapper/src/arkts-api/node-utilities/TSTypeAliasDeclaration.ts b/koala-wrapper/src/arkts-api/node-utilities/TSTypeAliasDeclaration.ts index 3531ee3a9..a1d47e0e1 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/TSTypeAliasDeclaration.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/TSTypeAliasDeclaration.ts @@ -15,6 +15,7 @@ import { Identifier, TSTypeAliasDeclaration, TSTypeParameterDeclaration, TypeNode } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateTSTypeAliasDeclaration( @@ -36,5 +37,9 @@ export function updateTSTypeAliasDeclaration( attachModifiers, (node: TSTypeAliasDeclaration, original: TSTypeAliasDeclaration) => node.setAnnotations(original.annotations) ); - return update(original, id, typeParams, typeAnnotation); + const newNode = update(original, id, typeParams, typeAnnotation); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/VariableDeclarator.ts b/koala-wrapper/src/arkts-api/node-utilities/VariableDeclarator.ts index 52c002fd3..1f1739da2 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/VariableDeclarator.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/VariableDeclarator.ts @@ -19,6 +19,7 @@ import { VariableDeclarator } from '../types'; import { attachModifiers, updateThenAttach } from '../utilities/private'; import { Es2pandaVariableDeclaratorFlag } from '../../generated/Es2pandaEnums'; import { AstNode } from '../peers/AstNode'; +import { NodeCache } from '../utilities/nodeCache'; export function updateVariableDeclarator( original: VariableDeclarator, @@ -35,5 +36,9 @@ export function updateVariableDeclarator( } const update = updateThenAttach(VariableDeclarator.update, attachModifiers); - return update(original, flag, name, initializer); + const newNode = update(original, flag, name, initializer); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/peers/AstNode.ts b/koala-wrapper/src/arkts-api/peers/AstNode.ts index 62a0b78d2..6d49f5da9 100644 --- a/koala-wrapper/src/arkts-api/peers/AstNode.ts +++ b/koala-wrapper/src/arkts-api/peers/AstNode.ts @@ -13,122 +13,149 @@ * limitations under the License. */ -import { isNullPtr, KInt, KNativePointer as KPtr, KNativePointer, nullptr } from "@koalaui/interop" -import { global } from "../static/global" -import { allFlags, nodeType, unpackNodeArray, unpackNonNullableNode, unpackString } from "../utilities/private" -import { throwError } from "../../utils" -import { Es2pandaModifierFlags } from "../../generated/Es2pandaEnums" -import { ArktsObject } from "./ArktsObject" -import { Es2pandaAstNodeType } from "../../Es2pandaEnums" +import { isNullPtr, KInt, KNativePointer as KPtr, KNativePointer, nullptr } from '@koalaui/interop'; +import { global } from '../static/global'; +import { allFlags, unpackNode, unpackNodeArray, unpackNonNullableNode, unpackString } from '../utilities/private'; +import { throwError } from '../../utils'; +import { Es2pandaModifierFlags } from '../../generated/Es2pandaEnums'; +import { ArktsObject } from './ArktsObject'; +import { SourcePosition } from './SourcePosition'; export abstract class AstNode extends ArktsObject { protected constructor(peer: KNativePointer) { if (isNullPtr(peer)) { - throwError(`attempted to create AstNode from nullptr`) + throwError(`attempted to create AstNode from nullptr`); } - super(peer) - this.updateChildren() + super(peer); + this.updateChildren(); } public get originalPeer(): KNativePointer { - const result = global.generatedEs2panda._AstNodeOriginalNodeConst(global.context, this.peer) + const result = global.generatedEs2panda._AstNodeOriginalNodeConst(global.context, this.peer); if (result === nullptr) { - this.originalPeer = this.peer - return this.peer + this.originalPeer = this.peer; + return this.peer; } - return result + return result; } public set originalPeer(peer: KNativePointer) { - global.generatedEs2panda._AstNodeSetOriginalNode(global.context, this.peer, peer) + global.generatedEs2panda._AstNodeSetOriginalNode(global.context, this.peer, peer); } public getChildren(): readonly AstNode[] { - return unpackNodeArray(global.es2panda._AstNodeChildren(global.context, this.peer)) + return unpackNodeArray(global.es2panda._AstNodeChildren(global.context, this.peer)); } public getSubtree(): readonly AstNode[] { return this.getChildren().reduce( (prev: readonly AstNode[], curr) => { - return prev.concat(curr.getSubtree()) + return prev.concat(curr.getSubtree()); }, [this] - ) + ); } public updateChildren(): void { if (this.peer === nullptr) { - throwError('updateChildren called on NULLPTR') + throwError('updateChildren called on NULLPTR'); } - global.es2panda._AstNodeUpdateChildren(global.context, this.peer) + global.es2panda._AstNodeUpdateChildren(global.context, this.peer); } public updateModifiers(modifierFlags: KInt | undefined): this { - global.generatedEs2panda._AstNodeClearModifier(global.context, this.peer, allFlags) - global.generatedEs2panda._AstNodeAddModifier(global.context, this.peer, modifierFlags ?? Es2pandaModifierFlags.MODIFIER_FLAGS_NONE) - return this + global.generatedEs2panda._AstNodeClearModifier(global.context, this.peer, allFlags); + global.generatedEs2panda._AstNodeAddModifier( + global.context, + this.peer, + modifierFlags ?? Es2pandaModifierFlags.MODIFIER_FLAGS_NONE + ); + return this; } public dump(indentation: number = 0): string { - const children = this.getChildren() - .map((it) => it.dump(indentation + 1)) - const msg = - `${indentation}_` - + ` ` - + this.dumpMessage() - return "> " + " ".repeat(4 * indentation) + msg + "\n" + children.join("") + const children = this.getChildren().map((it) => it.dump(indentation + 1)); + const msg = `${indentation}_ ${this.dumpMessage()}`; + return '> ' + ' '.repeat(4 * indentation) + msg + '\n' + children.join(''); } protected dumpMessage(): string { - return `` + return ``; } public dumpJson(): string { - return unpackString(global.generatedEs2panda._AstNodeDumpJSONConst(global.context, this.peer)) + return unpackString(global.generatedEs2panda._AstNodeDumpJSONConst(global.context, this.peer)); } public dumpSrc(): string { - return unpackString(global.generatedEs2panda._AstNodeDumpEtsSrcConst(global.context, this.peer)) + return unpackString(global.generatedEs2panda._AstNodeDumpEtsSrcConst(global.context, this.peer)); } public dumpModifiers(): string { - return unpackString(global.es2panda._AstNodeDumpModifiers(global.context, this.peer)) + return unpackString(global.es2panda._AstNodeDumpModifiers(global.context, this.peer)); } public clone(): this { - return unpackNonNullableNode(global.generatedEs2panda._AstNodeClone(global.context, this.peer, this.parent.peer)); + const clonedNode = unpackNonNullableNode( + global.generatedEs2panda._AstNodeClone(global.context, this.peer, this.parent?.peer ?? nullptr) + ); + clonedNode.parent = undefined; + return clonedNode as this; } - public get parent(): AstNode { - const parent = global.generatedEs2panda._AstNodeParent(global.context, this.peer) - if (parent === nullptr) { - throwError(`no parent`) - } - return unpackNonNullableNode(parent) + public get parent(): AstNode | undefined { + const parent = global.generatedEs2panda._AstNodeParent(global.context, this.peer); + return unpackNode(parent); } - public set parent(node: AstNode) { - global.generatedEs2panda._AstNodeSetParent(global.context, this.peer, node.peer) + public set parent(node: AstNode | undefined) { + global.generatedEs2panda._AstNodeSetParent(global.context, this.peer, node?.peer ?? nullptr); } public get modifiers(): KInt { - return global.generatedEs2panda._AstNodeModifiers(global.context, this.peer) + return global.generatedEs2panda._AstNodeModifiers(global.context, this.peer); } public set modifiers(flags: KInt | undefined) { - global.generatedEs2panda._AstNodeClearModifier(global.context, this.peer, allFlags) - global.generatedEs2panda._AstNodeAddModifier(global.context, this.peer, flags ?? Es2pandaModifierFlags.MODIFIER_FLAGS_NONE) + global.generatedEs2panda._AstNodeClearModifier(global.context, this.peer, allFlags); + global.generatedEs2panda._AstNodeAddModifier( + global.context, + this.peer, + flags ?? Es2pandaModifierFlags.MODIFIER_FLAGS_NONE + ); + } + + public get isExport(): boolean { + return global.generatedEs2panda._AstNodeIsExportedConst(global.context, this.peer); + } + + public get isDefaultExport(): boolean { + return global.generatedEs2panda._AstNodeIsDefaultExportedConst(global.context, this.peer); } public get isStatic(): boolean { - return global.generatedEs2panda._AstNodeIsStaticConst(global.context, this.peer) + return global.generatedEs2panda._AstNodeIsStaticConst(global.context, this.peer); + } + + public get startPosition(): SourcePosition { + return new SourcePosition(global.es2panda._AstNodeStartConst(global.context, this.peer)); + } + + public set startPosition(start: SourcePosition) { + global.es2panda._AstNodeSetStart(global.context, this.peer, start.peer); } -} + public get endPosition(): SourcePosition { + return new SourcePosition(global.es2panda._AstNodeEndConst(global.context, this.peer)); + } + + public set endPosition(end: SourcePosition) { + global.es2panda._AstNodeSetEnd(global.context, this.peer, end.peer); + } +} export class UnsupportedNode extends AstNode { constructor(peer: KPtr) { - super(peer) - + super(peer); } } diff --git a/koala-wrapper/src/arkts-api/peers/Context.ts b/koala-wrapper/src/arkts-api/peers/Context.ts index 48f86043a..865e83408 100644 --- a/koala-wrapper/src/arkts-api/peers/Context.ts +++ b/koala-wrapper/src/arkts-api/peers/Context.ts @@ -33,13 +33,25 @@ export class Context extends ArktsObject { global.es2panda._CreateContextFromString(global.config, passString(source), passString(global.filePath)) ); } - static destroyAndRecreate(ast: AstNode): Context { - console.log('[TS WRAPPER] DESTROY AND RECREATE'); - const source = filterSource(ast.dumpSrc()); - global.es2panda._DestroyContext(global.context); - global.compilerContext = Context.createFromString(source); - return new Context(global.context); + static createFromStringWithHistory(source: string): Context { + if (!global.configIsInitialized()) { + throwError(`Config not initialized`); + } + return new Context( + global.es2panda._CreateContextFromStringWithHistory(global.config, passString(source), passString(global.filePath)) + ); + } + + static createCacheContextFromFile( + configPtr: KNativePointer, + fileName: string, + globalContextPtr: KNativePointer, + isExternal: KBoolean + ): Context { + return new Context( + global.es2panda._CreateCacheContextFromFile(configPtr, passString(fileName), globalContextPtr, isExternal) + ); } static createContextGenerateAbcForExternalSourceFiles( @@ -52,6 +64,16 @@ export class Context extends ArktsObject { ); } + + static destroyAndRecreate(ast: AstNode): Context { + console.log('[TS WRAPPER] DESTROY AND RECREATE'); + const source = filterSource(ast.dumpSrc()); + global.es2panda._DestroyContext(global.context); + global.compilerContext = Context.createFromString(source); + + return new Context(global.context); + } + get program(): Program { return new Program(global.es2panda._ContextProgram(this.peer)); } diff --git a/koala-wrapper/src/arkts-api/peers/Diagnostic.ts b/koala-wrapper/src/arkts-api/peers/Diagnostic.ts new file mode 100644 index 000000000..08882031a --- /dev/null +++ b/koala-wrapper/src/arkts-api/peers/Diagnostic.ts @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ArktsObject } from './ArktsObject'; +import { global } from '../static/global'; +import { KNativePointer } from '@koalaui/interop'; +import { DiagnosticKind } from './DiagnosticKind'; +import { passStringArray } from '../utilities/private'; +import { SourcePosition } from './SourcePosition'; +import { SourceRange } from './SourceRange'; +import { DiagnosticInfo } from './DiagnosticInfo'; +import { SuggestionInfo } from './SuggestionInfo'; + +export class Diagnostic extends ArktsObject { + constructor(peer: KNativePointer) { + super(peer); + } + static logDiagnostic(kind: DiagnosticKind, pos: SourcePosition, ...args: string[]): void { + global.es2panda._LogDiagnostic(global.context, kind.peer, passStringArray(args), args.length, pos.peer); + } + + static logDiagnosticWithSuggestion(diagnosticInfo: DiagnosticInfo, suggestionInfo: SuggestionInfo): void { + global.es2panda._LogDiagnosticWithSuggestion(global.context, diagnosticInfo.peer, suggestionInfo.peer); + } + +} \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/peers/DiagnosticInfo.ts b/koala-wrapper/src/arkts-api/peers/DiagnosticInfo.ts new file mode 100644 index 000000000..de35eed7c --- /dev/null +++ b/koala-wrapper/src/arkts-api/peers/DiagnosticInfo.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ArktsObject } from './ArktsObject'; +import { global } from '../static/global'; +import { KNativePointer } from '@koalaui/interop'; +import { DiagnosticKind } from './DiagnosticKind'; +import { SourcePosition } from './SourcePosition'; +import { passStringArray } from '../utilities/private'; + +export class DiagnosticInfo extends ArktsObject { + constructor(peer: KNativePointer) { + super(peer); + } + + static create(kind: DiagnosticKind, pos: SourcePosition, ...args: string[]): DiagnosticInfo { + return new DiagnosticInfo( + global.es2panda._CreateDiagnosticInfo(global.context, kind.peer, passStringArray(args), args.length, pos.peer) + ); + } + +} \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/peers/DiagnosticKind.ts b/koala-wrapper/src/arkts-api/peers/DiagnosticKind.ts new file mode 100644 index 000000000..615765493 --- /dev/null +++ b/koala-wrapper/src/arkts-api/peers/DiagnosticKind.ts @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ArktsObject } from './ArktsObject'; +import { global } from '../static/global'; +import { KNativePointer } from '@koalaui/interop'; + +export enum PluginDiagnosticType { + ES2PANDA_PLUGIN_WARNING = 0, + ES2PANDA_PLUGIN_ERROR, + ES2PANDA_PLUGIN_SUGGESTION, +}; + +export class DiagnosticKind extends ArktsObject { + constructor(peer: KNativePointer) { + super(peer); + } + + static create(message: string, type: PluginDiagnosticType): DiagnosticKind { + return new DiagnosticKind( + global.es2panda._CreateDiagnosticKind(global.context, message, type) + ); + } +} diff --git a/koala-wrapper/src/arkts-api/peers/ImportPathManager.ts b/koala-wrapper/src/arkts-api/peers/ImportPathManager.ts index eeeecf1dc..23e4ab782 100644 --- a/koala-wrapper/src/arkts-api/peers/ImportPathManager.ts +++ b/koala-wrapper/src/arkts-api/peers/ImportPathManager.ts @@ -29,8 +29,4 @@ export class ImportPathManager extends ArktsObject { global.es2panda._ETSParserGetImportPathManager(global.context) ); } - - resolvePath(currentModulePath: string, importPath: string): string { - return ''; // TODO: no longer support this. - } } \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/peers/Program.ts b/koala-wrapper/src/arkts-api/peers/Program.ts index 5f3afbed2..6cdcc883b 100644 --- a/koala-wrapper/src/arkts-api/peers/Program.ts +++ b/koala-wrapper/src/arkts-api/peers/Program.ts @@ -19,9 +19,18 @@ import { acceptNativeObjectArrayResult, unpackString } from "../utilities/privat import { KNativePointer } from "@koalaui/interop" import { EtsScript } from "../types" +enum SkipPhaseResult { + NOT_COMPUTED, + CAN_SKIP, + CANNOT_SKIP, +} + export class Program extends ArktsObject { + private canSkipPhaseResult: SkipPhaseResult; + constructor(peer: KNativePointer) { super(peer); + this.canSkipPhaseResult = SkipPhaseResult.NOT_COMPUTED; } get astNode(): EtsScript { @@ -35,17 +44,58 @@ export class Program extends ArktsObject { ); } - get programFileName(): string { + get directExternalSources(): ExternalSource[] { + return acceptNativeObjectArrayResult( + global.es2panda._ProgramDirectExternalSources(global.context, this.peer), + (instance: KNativePointer) => new ExternalSource(instance) + ); + } + + get fileName(): string { return unpackString(global.es2panda._ProgramFileNameConst(global.context, this.peer)); } - get programFileNameWithExtension(): string { + get fileNameWithExtension(): string { return unpackString(global.es2panda._ProgramFileNameWithExtensionConst(global.context, this.peer)); } - get programGlobalAbsName(): string { + /** + * @deprecated + */ + get globalAbsName(): string { return unpackString(global.es2panda._ETSParserGetGlobalProgramAbsName(global.context)); } + + get absName(): string { + return unpackString(global.es2panda._ProgramAbsoluteNameConst(global.context, this.peer)); + } + + get moduleName(): string { + return unpackString(global.es2panda._ProgramModuleNameConst(global.context, this.peer)); + } + + get sourceFilePath(): string { + return unpackString(global.es2panda._ProgramSourceFilePathConst(global.context, this.peer)); + } + + isASTLowered(): boolean { + return global.es2panda._ProgramIsASTLoweredConst(global.context, this.peer); + } + + canSkipPhases(): boolean { + if (this.canSkipPhaseResult === SkipPhaseResult.CAN_SKIP) { + return true; + } else if (this.canSkipPhaseResult === SkipPhaseResult.CANNOT_SKIP) { + return false; + } + if (global.es2panda._ProgramCanSkipPhases(global.context, this.peer)) { + this.canSkipPhaseResult = SkipPhaseResult.CAN_SKIP; + return true; + } else { + this.canSkipPhaseResult = SkipPhaseResult.CANNOT_SKIP; + return false; + } + } } export class ExternalSource extends ArktsObject { diff --git a/koala-wrapper/src/arkts-api/peers/SourcePosition.ts b/koala-wrapper/src/arkts-api/peers/SourcePosition.ts index 9d84847a6..ff5abb25e 100644 --- a/koala-wrapper/src/arkts-api/peers/SourcePosition.ts +++ b/koala-wrapper/src/arkts-api/peers/SourcePosition.ts @@ -35,4 +35,8 @@ export class SourcePosition extends ArktsObject { line(): number { return global.es2panda._SourcePositionLine(global.context, this.peer); } + + col(): number { + return global.es2panda._SourcePositionCol(global.context, this.peer); + } } \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/peers/SuggestionInfo.ts b/koala-wrapper/src/arkts-api/peers/SuggestionInfo.ts new file mode 100644 index 000000000..2155597bf --- /dev/null +++ b/koala-wrapper/src/arkts-api/peers/SuggestionInfo.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ArktsObject } from './ArktsObject'; +import { global } from '../static/global'; +import { KNativePointer } from '@koalaui/interop'; +import { DiagnosticKind } from './DiagnosticKind'; +import { SourceRange } from './SourceRange'; +import { passStringArray } from '../utilities/private'; + +export class SuggestionInfo extends ArktsObject { + constructor(peer: KNativePointer) { + super(peer); + } + + static create(kind: DiagnosticKind, substitutionCode: string, title: string, range: SourceRange, ...args: string[]): SuggestionInfo { + return new SuggestionInfo( + global.es2panda._CreateSuggestionInfo(global.context, kind.peer, passStringArray(args), + args.length, substitutionCode, title, range.peer) + ); + } + +} \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/static/global.ts b/koala-wrapper/src/arkts-api/static/global.ts index 551b6c83f..fe5e598a2 100644 --- a/koala-wrapper/src/arkts-api/static/global.ts +++ b/koala-wrapper/src/arkts-api/static/global.ts @@ -67,7 +67,9 @@ export class global { private static _interop: InteropNativeModule | undefined = undefined public static get interop(): InteropNativeModule { - if (this._interop === undefined) this._interop = initInterop() + if (this._interop === undefined) { + this._interop = initInterop() + } return this._interop } diff --git a/koala-wrapper/src/arkts-api/types.ts b/koala-wrapper/src/arkts-api/types.ts index b58793902..fe5a2bbc4 100644 --- a/koala-wrapper/src/arkts-api/types.ts +++ b/koala-wrapper/src/arkts-api/types.ts @@ -27,7 +27,6 @@ import { allFlags, arrayOfNullptr, assertValidPeer, - nodeType, passNode, passNodeArray, passString, @@ -90,7 +89,7 @@ export class EtsScript extends AstNode { global.config = Config.createDefault().peer; } global.compilerContext = Context.createFromString(source); - proceedToState(state); + proceedToState(state, global.context); return new EtsScript( global.es2panda._ProgramAst(global.context, global.es2panda._ContextProgram(global.context)) ); @@ -121,6 +120,10 @@ export class EtsScript extends AstNode { nodes.length ); } + + get isNamespace(): boolean { + return global.generatedEs2panda._ETSModuleIsNamespaceConst(global.context, this.peer); + } } export class ExpressionStatement extends AstNode { @@ -241,6 +244,14 @@ export class CallExpression extends Expression { return this; } + get hasTrailingComma(): boolean { + return global.generatedEs2panda._CallExpressionHasTrailingCommaConst(global.context, this.peer); + } + + get isTrailingCall(): boolean { + return global.es2panda._CallExpressionIsTrailingCallConst(global.context, this.peer); + } + readonly expression: AstNode; // Expression readonly typeArguments: readonly TypeNode[] | undefined; readonly arguments: readonly Expression[]; @@ -473,7 +484,7 @@ export class FunctionDeclaration extends AstNode { readonly isAnon: boolean; } -export class FunctionExpression extends AstNode { +class FunctionExpression extends AstNode { constructor(peer: KPtr) { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_FUNCTION_EXPRESSION); super(peer); diff --git a/koala-wrapper/src/arkts-api/utilities/nodeCache.ts b/koala-wrapper/src/arkts-api/utilities/nodeCache.ts new file mode 100644 index 000000000..046b51536 --- /dev/null +++ b/koala-wrapper/src/arkts-api/utilities/nodeCache.ts @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { KNativePointer } from '@koalaui/interop'; +import { Es2pandaAstNodeType } from '../../Es2pandaEnums'; +import { AstNode, UnsupportedNode } from '../peers/AstNode'; +import { global } from '../static/global'; +import { getOrPut, nodeByType } from '../class-by-peer'; + +export interface AstNodeCacheValue { + peer: KNativePointer; + type: Es2pandaAstNodeType; + metadata?: AstNodeCacheValueMetadata; +} + +export interface AstNodeCacheValueMetadata { + callName?: string; + hasReceiver?: boolean; + isSetter?: boolean; + isGetter?: boolean; + forbidTypeRewrite?: boolean; + isWithinTypeParams?: boolean; + hasMemoSkip?: boolean; + hasMemoIntrinsic?: boolean; + hasMemoEntry?: boolean; +} + +export class NodeCache { + private _isCollected: boolean = false; + private cacheMap: Map; + private static instance: NodeCache; + + private constructor() { + this.cacheMap = new Map(); + } + + static getInstance(): NodeCache { + if (!this.instance) { + this.instance = new NodeCache(); + } + return this.instance; + } + + collect(node: AstNode, metadata?: AstNodeCacheValueMetadata): void { + const peer = node.peer; + const type = global.generatedEs2panda._AstNodeTypeConst(global.context, node.peer); + let currMetadata: AstNodeCacheValueMetadata | undefined = metadata ?? {}; + if (this.cacheMap.has(peer)) { + const oldMetadata = this.cacheMap.get(peer)!.metadata ?? {}; + currMetadata = { ...oldMetadata, ...currMetadata }; + } + currMetadata = Object.keys(currMetadata).length === 0 ? undefined : currMetadata; + this.cacheMap.set(peer, { peer, type, metadata: currMetadata }); + this._isCollected = true; + } + + refresh(original: AstNode, node: AstNode): void { + let metadata: AstNodeCacheValueMetadata | undefined; + if (this.has(original)) { + metadata = this.get(original)?.metadata; + this.cacheMap.delete(original.peer); + } + this.collect(node, metadata); + } + + isCollected(): boolean { + return this._isCollected; + } + + has(node: AstNode): boolean { + return this.cacheMap.has(node.peer); + } + + get(node: AstNode): AstNodeCacheValue | undefined { + return this.cacheMap.get(node.peer); + } + + clear(): void { + this.cacheMap.clear(); + this._isCollected = false; + } + + visualize(): void { + Array.from(this.cacheMap.values()).forEach(({ peer, type, metadata }) => { + const node = nodeByType.get(type) ?? UnsupportedNode; + const newNode = getOrPut(peer, (peer) => new node(peer)) as AstNode; + console.log( + `[NODE CACHE] ptr ${peer}, type: ${type}, metadata: ${JSON.stringify(metadata)}, node: `, + newNode.dumpSrc() + ); + }); + } +} diff --git a/koala-wrapper/src/arkts-api/utilities/performance.ts b/koala-wrapper/src/arkts-api/utilities/performance.ts index bafe9df5d..9aaa414e8 100644 --- a/koala-wrapper/src/arkts-api/utilities/performance.ts +++ b/koala-wrapper/src/arkts-api/utilities/performance.ts @@ -13,6 +13,22 @@ * limitations under the License. */ +import * as process from 'process'; +import { global as localGlobal} from '../static/global'; + +const BYTES_PER_KIBIBYTE = 1024; + +interface MemoryContext { + startTime: number; + startMemory: { + rss: number; + heapTotal: number; + heapUsed: number; + external: number; + arrayBuffers: number; + }; +} + interface Event { name: string, startTime: number, @@ -34,17 +50,26 @@ function pad(value: number, length: number): string { return value.toString().padStart(length, '0'); } +function round(value: number, index: number = 2): number { + const factor = Math.pow(10, index); + return Math.round(value * factor) / factor; +} + export class Performance { private static instance: Performance; private events: Map; + private historyEvents = new Map(); private scopes: string[]; private shouldSkip: boolean; private totalDuration: number; - + private memoryContexts = new Map(); + private memoryTrackerEnable: boolean; private constructor() { this.events = new Map(); + this.historyEvents = new Map(); this.scopes = []; this.shouldSkip = true; + this.memoryTrackerEnable = false; this.totalDuration = 0; } @@ -59,8 +84,14 @@ export class Performance { this.shouldSkip = shouldSkip; } + enableMemoryTracker(enableMemoryTracker: boolean = false): void { + this.memoryTrackerEnable = enableMemoryTracker; + } + createEvent(name: string): Event { - if (this.shouldSkip) return { name: '', startTime: 0 }; + if (this.shouldSkip) { + return { name: '', startTime: 0 }; + } const startTime: number = performance.now(); const newEvent: Event = { name, startTime }; this.events.set(name, newEvent); @@ -69,7 +100,9 @@ export class Performance { } stopEvent(name: string, shouldLog: boolean = false): Event { - if (this.shouldSkip) return { name: '', startTime: 0 }; + if (this.shouldSkip) { + return { name: '', startTime: 0 }; + } if (!this.events.has(name) || this.scopes.length === 0) { throw new Error(`Event ${name} is not created.`); } @@ -82,19 +115,26 @@ export class Performance { const endTime: number = performance.now(); const parentEvent: string = this.scopes[this.scopes.length - 1]; const duration: number = endTime - event.startTime; - this.totalDuration += duration; + if (!parentEvent) { + this.totalDuration += duration; + } if (shouldLog) { console.log( - `[PERFORMANCE] name: ${event.name}, parent: ${parentEvent}, duration: ${formatTime(duration)}, total: ${formatTime(this.totalDuration)}` + `[PERFORMANCE] name: ${event.name}, parent: ${parentEvent}, duration: ${formatTime(duration)}(${round(duration)}), total: ${formatTime(this.totalDuration)}(${round(this.totalDuration)})` ); } - return { ...event, endTime, parentEvent, duration }; + const newEvent = { ...event, endTime, parentEvent, duration }; + const history = this.historyEvents.get(parentEvent ?? null) || []; + this.historyEvents.set(parentEvent ?? null, [...history, newEvent]); + return newEvent; } stopLastEvent(shouldLog: boolean = false): Event { - if (this.shouldSkip) return { name: '', startTime: 0 }; + if (this.shouldSkip) { + return { name: '', startTime: 0 }; + } if (this.scopes.length === 0) { throw new Error("No last event"); } @@ -107,19 +147,26 @@ export class Performance { const endTime: number = performance.now(); const parentEvent: string = this.scopes[this.scopes.length - 1]; const duration: number = endTime - event.startTime; - this.totalDuration += duration; + if (!parentEvent) { + this.totalDuration += duration; + } if (shouldLog) { console.log( - `[PERFORMANCE] name: ${event.name}, parent: ${parentEvent}, duration: ${formatTime(duration)}, total: ${formatTime(this.totalDuration)}` + `[PERFORMANCE] name: ${event.name}, parent: ${parentEvent}, duration: ${formatTime(duration)}(${round(duration)}), total: ${formatTime(this.totalDuration)}(${round(this.totalDuration)})` ); } - return { ...event, endTime, parentEvent, duration }; + const newEvent = { ...event, endTime, parentEvent, duration }; + const history = this.historyEvents.get(parentEvent ?? null) || []; + this.historyEvents.set(parentEvent ?? null, [...history, newEvent]); + return newEvent; } clearAllEvents(shouldLog: boolean = false): void { - if (this.shouldSkip) return; + if (this.shouldSkip) { + return; + } for (let i = 0; i < this.scopes.length; i ++) { this.stopLastEvent(shouldLog); } @@ -129,4 +176,134 @@ export class Performance { clearTotalDuration(): void { this.totalDuration = 0; } + + clearHistory(): void { + this.historyEvents = new Map(); + } + + visualizeEvents(shouldLog: boolean = false): void { + if (this.shouldSkip) { + return; + } + const that = this; + function buildVisualization(parentKey: string | null, indentLevel: number): [string, number] { + const children = that.historyEvents.get(parentKey) || []; + let result = ''; + + children.forEach(child => { + const indent = ' '.repeat(indentLevel); + const duration = child.duration ?? 0; + const [_result, count] = buildVisualization(child.name, indentLevel + 1); + result += `${indent}- ${child.name}: ${formatTime(duration)}(${round(duration)}), ${count}\n`; + result += _result; + }); + + return [result, children.length]; + } + + const [finalResult, _] = buildVisualization(null, 0); + if (shouldLog) { + console.log(`[PERFORMANCE] ===== FINAL RESULT ====`); + console.log(`TOTAL: ${formatTime(this.totalDuration)}(${round(this.totalDuration)})`); + console.log(finalResult.trimEnd()); + console.log(`[PERFORMANCE] ===== FINAL RESULT ====`); + } + } + + startMemRecord(label: string = `measurement-${Date.now()}`): void { + // 强制进行垃圾回收(需要 Node.js 启动时添加 --expose-gc 参数) + if (!this.memoryTrackerEnable) { + return; + } + if (global.gc) { + (global as any).gc(); + } + const startMemory = process.memoryUsage(); + this.memoryContexts.set(label, { + startTime: Date.now(), + startMemory: { + rss: startMemory.rss / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE), + heapTotal: startMemory.heapTotal / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE), + heapUsed: startMemory.heapUsed / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE), + external: startMemory.external / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE), + arrayBuffers: (startMemory.arrayBuffers || 0) / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE) + } + }); + + return; + } + + stopMemRecord(label: string = `measurement-${Date.now()}`, runGc: boolean = false): void { + if (!this.memoryTrackerEnable) { + return; + } + const context = this.memoryContexts.get(label); + + if (!context) { + console.error(`未找到标签为 "${label}" 的内存测量上下文`); + return; + } + + // 可选:在测量结束前执行垃圾回收 + if (runGc && global.gc) { + (global as any).gc(); + } + + // 记录结束时的内存使用情况 + const endTime = Date.now(); + const endMemory = process.memoryUsage(); + + // 计算内存使用增量 + const memoryDiff = { + rss: endMemory.rss / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE) - context.startMemory.rss, + heapTotal: endMemory.heapTotal / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE) - context.startMemory.heapTotal, + heapUsed: endMemory.heapUsed / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE) - context.startMemory.heapUsed, + external: endMemory.external / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE) - context.startMemory.external, + arrayBuffers: ((endMemory.arrayBuffers || 0) / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE)) - context.startMemory.arrayBuffers + }; + const duration = endTime - context.startTime; + + console.log('[PERFORMANCE]', `内存测量结果 [标签: ${label}]`); + console.log('[PERFORMANCE]', `执行时间: ${duration}ms`); + console.log('---------------------------------------------------------------'); + console.log('[PERFORMANCE]', `内存类型 | 开始值(MB) | 结束值(MB) | 增量(MB)`); + console.log('---------------------------------------------------------------'); + console.log('[PERFORMANCE]', `RSS | ${context.startMemory.rss.toFixed(2)} | ${(endMemory.rss / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE)).toFixed(2)} | ${memoryDiff.rss.toFixed(2)}`); + console.log('[PERFORMANCE]', `Heap Total | ${context.startMemory.heapTotal.toFixed(2)} | ${(endMemory.heapTotal / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE)).toFixed(2)} | ${memoryDiff.heapTotal.toFixed(2)}`); + console.log('[PERFORMANCE]', `Heap Used | ${context.startMemory.heapUsed.toFixed(2)} | ${(endMemory.heapUsed / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE)).toFixed(2)} | ${memoryDiff.heapUsed.toFixed(2)}`); + console.log('[PERFORMANCE]', `External | ${context.startMemory.external.toFixed(2)} | ${(endMemory.external / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE)).toFixed(2)} | ${memoryDiff.external.toFixed(2)}`); + if (endMemory.arrayBuffers !== undefined) { + console.log(`Array Buffers | ${context.startMemory.arrayBuffers.toFixed(2)} | ${((endMemory.arrayBuffers || 0) / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE)).toFixed(2)} | ${memoryDiff.arrayBuffers.toFixed(2)}`); + } + console.log('---------------------------------------------------------------'); + this.memoryContexts.delete(label); + return; + } + + memoryTrackerReset(): void { + if (!this.memoryTrackerEnable) { + return; + } + localGlobal.es2panda._MemoryTrackerReset(localGlobal.context); + } + + memoryTrackerGetDelta(tag: string): void { + if (!this.memoryTrackerEnable) { + return; + } + console.log('---------------------------------------------------------------'); + console.log('[PERFORMANCE] Increamental memory:', tag); + localGlobal.es2panda._MemoryTrackerGetDelta(localGlobal.context); + console.log('---------------------------------------------------------------'); + } + + memoryTrackerPrintCurrent(tag: string): void { + if (!this.memoryTrackerEnable) { + return; + } + console.log('---------------------------------------------------------------'); + console.log('[PERFORMANCE] Current total memory:', tag); + localGlobal.es2panda._MemoryTrackerPrintCurrent(localGlobal.context); + console.log('---------------------------------------------------------------'); + } } \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/utilities/private.ts b/koala-wrapper/src/arkts-api/utilities/private.ts index 30db3e472..c343fb8e2 100644 --- a/koala-wrapper/src/arkts-api/utilities/private.ts +++ b/koala-wrapper/src/arkts-api/utilities/private.ts @@ -168,10 +168,6 @@ export function updateNodeByNode(node: T, original: AstNode): return node; } -export function nodeType(node: AstNode): Es2pandaAstNodeType { - return global.generatedEs2panda._AstNodeTypeConst(global.context, passNode(node)); -} - /** * @deprecated */ diff --git a/koala-wrapper/src/arkts-api/utilities/public.ts b/koala-wrapper/src/arkts-api/utilities/public.ts index 6c9f010d4..bf15ef63b 100644 --- a/koala-wrapper/src/arkts-api/utilities/public.ts +++ b/koala-wrapper/src/arkts-api/utilities/public.ts @@ -15,8 +15,8 @@ import { global } from '../static/global'; import { isNumber, throwError, getEnumName } from '../../utils'; -import { KNativePointer, KInt, nullptr, withStringResult } from '@koalaui/interop'; -import { passNode, passString, unpackNodeArray, unpackNonNullableNode } from './private'; +import { KNativePointer, KInt, nullptr, withStringResult, KStringArrayPtr } from '@koalaui/interop' +import { passNode, passString, passStringArray, unpackNodeArray, unpackNonNullableNode } from './private'; import { isFunctionDeclaration, isMemberExpression, isMethodDefinition, isNumberLiteral } from '../factory/nodeTests'; import { Es2pandaContextState, @@ -42,43 +42,46 @@ import { Program } from '../peers/Program'; import { clearNodeCache } from '../class-by-peer'; import { SourcePosition } from '../peers/SourcePosition'; import { MemberExpression } from '../to-be-generated/MemberExpression'; +import { Es2pandaAstNodeType } from '../../Es2pandaEnums'; -export function proceedToState(state: Es2pandaContextState, forceDtsEmit = false): void { +export function proceedToState(state: Es2pandaContextState, context: KNativePointer, forceDtsEmit = false): void { console.log('[TS WRAPPER] PROCEED TO STATE: ', getEnumName(Es2pandaContextState, state)); - if (global.es2panda._ContextState(global.context) === Es2pandaContextState.ES2PANDA_STATE_ERROR) { - processErrorState(state, forceDtsEmit); + if (global.es2panda._ContextState(context) === Es2pandaContextState.ES2PANDA_STATE_ERROR) { + clearNodeCache(); + processErrorState(state, context, forceDtsEmit); } - if (state <= global.es2panda._ContextState(global.context)) { + if (state <= global.es2panda._ContextState(context)) { console.log('[TS WRAPPER] PROCEED TO STATE: SKIPPING'); return; } clearNodeCache(); - global.es2panda._ProceedToState(global.context, state); - processErrorState(state, forceDtsEmit); + global.es2panda._ProceedToState(context, state); + processErrorState(state, context, forceDtsEmit); } -function processErrorState(state: Es2pandaContextState, forceDtsEmit = false): void { +function processErrorState(state: Es2pandaContextState, context: KNativePointer, forceDtsEmit = false): void { try { - if ( - global.es2panda._ContextState(global.context) === Es2pandaContextState.ES2PANDA_STATE_ERROR && - !forceDtsEmit - ) { - const errorMessage = withStringResult(global.es2panda._ContextErrorMessage(global.context)); + if (global.es2panda._ContextState(context) === Es2pandaContextState.ES2PANDA_STATE_ERROR && !forceDtsEmit) { + const errorMessage = withStringResult(global.es2panda._ContextErrorMessage(context)); if (errorMessage === undefined) { throwError(`Could not get ContextErrorMessage`); } - const allErrorMessages = withStringResult(global.es2panda._GetAllErrorMessages(global.context)); + const allErrorMessages = withStringResult(global.es2panda._GetAllErrorMessages(context)); if (allErrorMessages === undefined) { throwError(`Could not get AllErrorMessages`); } throwError([`Failed to proceed to ${Es2pandaContextState[state]}`, errorMessage, allErrorMessages].join(`\n`)); } } catch (e) { - global.es2panda._DestroyContext(global.context); + global.es2panda._DestroyContext(context); throw e; } } +export function nodeType(node: AstNode): Es2pandaAstNodeType { + return global.generatedEs2panda._AstNodeTypeConst(global.context, passNode(node)); +} + export function startChecker(): boolean { return global.es2panda._CheckerStartChecker(global.context); } @@ -102,7 +105,7 @@ export function getDecl(node: AstNode): AstNode | undefined { if (!!decl) { return decl; } - if (isProperty(node.parent)) { + if (!!node.parent && isProperty(node.parent)) { return getDeclFromProperty(node.parent); } return undefined; @@ -112,7 +115,7 @@ function getDeclFromProperty(node: Property): AstNode | undefined { if (!node.key) { return undefined; } - if (!isObjectExpression(node.parent)) { + if (!!node.parent && !isObjectExpression(node.parent)) { return getPeerDecl(passNode(node.key)); } return getDeclFromObjectExpressionProperty(node); @@ -214,6 +217,10 @@ export function importDeclarationInsert(node: ETSImportDeclaration, program: Pro global.es2panda._InsertETSImportDeclarationAndParse(global.context, program.peer, node.peer); } +export function getProgramFromAstNode(node: AstNode): Program { + return new Program(global.es2panda._AstNodeProgram(global.context, node.peer)); +} + export function hasModifierFlag(node: AstNode, flag: Es2pandaModifierFlags): boolean { if (!node) return false; @@ -250,7 +257,7 @@ export function destroyConfig(config: KNativePointer): void { global.resetConfig(); } -export function setAllParents(ast: AstNode) { +export function setAllParents(ast: AstNode): void { global.es2panda._AstNodeUpdateAll(global.context, ast.peer); } @@ -258,6 +265,7 @@ export function generateTsDeclarationsFromContext( outputDeclEts: string, outputEts: string, exportAll: boolean, + isolated: boolean, recordFile: string ): KInt { return global.es2panda._GenerateTsDeclarationsFromContext( @@ -265,10 +273,18 @@ export function generateTsDeclarationsFromContext( passString(outputDeclEts), passString(outputEts), exportAll, + isolated, passString(recordFile) ); } +export function generateStaticDeclarationsFromContext(outputPath: string): KInt { + return global.es2panda._GenerateStaticDeclarationsFromContext( + global.context, + passString(outputPath) + ); +} + export function isDefaultAccessModifierClassProperty(property: ClassProperty): boolean { return global.es2panda._ClassPropertyIsDefaultAccessModifierConst(global.context, property.peer); } @@ -280,3 +296,45 @@ export function getStartPosition(node: AstNode): SourcePosition { export function getEndPosition(node: AstNode): SourcePosition { return new SourcePosition(global.es2panda._AstNodeEndConst(global.context, node.peer)); } + +export function MemInitialize(): void { + global.es2panda._MemInitialize(); +} + +export function MemFinalize(): void { + global.es2panda._MemFinalize(); +} + +export function CreateGlobalContext( + config: KNativePointer, + externalFileList: string[], + fileNum: KInt, + lspUsage: boolean +): KNativePointer { + return global.es2panda._CreateGlobalContext(config, passStringArray(externalFileList), fileNum, lspUsage); +} + +export function DestroyGlobalContext(context: KNativePointer): void { + global.es2panda._DestroyGlobalContext(context); +} + +export function CreateCacheContextFromFile( + configPtr: KNativePointer, + filename: string, + globalContext: KNativePointer, + isExternal: Boolean +): KNativePointer { + return global.es2panda._CreateCacheContextFromFile(configPtr, passString(filename), globalContext, isExternal); +} + +export function createTypeNodeFromTsType(node: AstNode): AstNode | undefined { + const typeAnnotation = global.es2panda._CreateTypeNodeFromTsType(global.context, node.peer); + if (typeAnnotation === nullptr) { + return undefined; + } + return unpackNonNullableNode(typeAnnotation); +} + +export function getJsdocStringFromDeclaration(decl: AstNode): string { + return withStringResult(global.es2panda._JsdocStringFromDeclaration(global.context, decl.peer)) ?? throwError(`failed to unpack (peer shouldn't be NULLPTR)`); +} \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/visitor.ts b/koala-wrapper/src/arkts-api/visitor.ts index e6ebd25db..7898501f2 100644 --- a/koala-wrapper/src/arkts-api/visitor.ts +++ b/koala-wrapper/src/arkts-api/visitor.ts @@ -52,7 +52,14 @@ import { isForUpdateStatement, isForOfStatement, isTSTypeAliasDeclaration, + isETSParameterExpression, isETSFunctionType, + isSwitchStatement, + isSwitchCaseStatement, + isSpreadElement, + isClassStaticBlock, + isFunctionExpression, + FunctionExpression, } from '../generated'; import { isEtsScript, @@ -72,67 +79,10 @@ import { } from './factory/nodeTests'; import { classDefinitionFlags } from './utilities/public'; import { Es2pandaAstNodeType } from '../Es2pandaEnums'; +import { updateFunctionExpression } from './node-utilities/FunctionExpression'; type Visitor = (node: AstNode) => AstNode; -export interface StructVariableMetadata { - name: string; - properties: string[]; - modifiers: Es2pandaModifierFlags; - hasStateManagementType?: boolean; -} - -export class StructInfo { - metadata: Record = {}; - initializeBody: AstNode[] = []; - updateBody: AstNode[] = []; - isReusable: boolean = false; - toRecordBody: Property[] = []; -} - -export class GlobalInfo { - private _structCollection: Set; - private static instance: GlobalInfo; - private _structMap: Map; - - private constructor() { - this._structCollection = new Set(); - this._structMap = new Map(); - } - - public static getInfoInstance(): GlobalInfo { - if (!this.instance) { - this.instance = new GlobalInfo(); - } - return this.instance; - } - - public add(str: string): void { - this._structCollection.add(str); - } - - public getStructCollection(): Set { - return this._structCollection; - } - - public getStructInfo(structName: string): StructInfo { - const structInfo = this._structMap.get(structName); - if (!structInfo) { - return new StructInfo(); - } - return structInfo; - } - - public setStructInfo(structName: string, info: StructInfo): void { - this._structMap.set(structName, info); - } - - public reset(): void { - this._structMap.clear(); - this._structCollection.clear(); - } -} - // TODO: rethink (remove as) function nodeVisitor(node: T, visitor: Visitor): T { if (node === undefined) { @@ -163,6 +113,7 @@ export function visitEachChild(node: AstNode, visitor: Visitor): AstNode { script = visitDefinitionBody(script, visitor); script = visitStatement(script, visitor); script = visitForLoopStatement(script, visitor); + script = visitSwitchCaseStatement(script, visitor); script = visitOuterExpression(script, visitor); script = visitInnerExpression(script, visitor); script = visitTrivialExpression(script, visitor); @@ -204,7 +155,7 @@ function visitOuterExpression(node: AstNode, visitor: Visitor): AstNode { updated = true; return factory.updateETSNewClassInstanceExpression( node, - node.getTypeRef, + nodeVisitor(node.getTypeRef, visitor), nodesVisitor(node.getArguments, visitor) ); } @@ -278,6 +229,10 @@ function visitTrivialExpression(node: AstNode, visitor: Visitor): AstNode { node.operatorType ); } + if (isSpreadElement(node)) { + const nodeType = global.generatedEs2panda._AstNodeTypeConst(global.context, node.peer); + return factory.updateSpreadElement(node, nodeType, nodeVisitor(node.argument, visitor)); + } // TODO return node; } @@ -454,6 +409,23 @@ function visitForLoopStatement(node: AstNode, visitor: Visitor): AstNode { return node; } +function visitSwitchCaseStatement(node: AstNode, visitor: Visitor): AstNode { + if (updated) { + return node; + } + if (isSwitchStatement(node)) { + return factory.updateSwitchStatement( + node, + nodeVisitor(node.discriminant, visitor), + nodesVisitor(node.cases, visitor) + ); + } + if (isSwitchCaseStatement(node)) { + return factory.updateSwitchCaseStatement(node, node.test, nodesVisitor(node.consequent, visitor)); + } + return node; +} + function visitETSModule(node: AstNode, visitor: Visitor): AstNode { if (updated) { return node; @@ -495,6 +467,16 @@ function visitDefinitionBody(node: AstNode, visitor: Visitor): AstNode { node.isComputed ); } + if (isClassStaticBlock(node) && !!node.value) { + updated = true; + return factory.updateClassStaticBlock( + node, + updateFunctionExpression( + node.value as FunctionExpression, + nodeVisitor(node.function, visitor) + ) + ); + } // TODO return node; } diff --git a/koala-wrapper/src/generated/Es2pandaEnums.ts b/koala-wrapper/src/generated/Es2pandaEnums.ts index 1e5ee0d2c..249570dde 100644 --- a/koala-wrapper/src/generated/Es2pandaEnums.ts +++ b/koala-wrapper/src/generated/Es2pandaEnums.ts @@ -24,174 +24,7 @@ export enum Es2pandaContextState { ES2PANDA_STATE_BIN_GENERATED = 6, ES2PANDA_STATE_ERROR = 7 } -// export enum Es2pandaAstNodeType { -// AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION = 0, -// AST_NODE_TYPE_ANNOTATION_DECLARATION = 1, -// AST_NODE_TYPE_ANNOTATION_USAGE = 2, -// AST_NODE_TYPE_ASSERT_STATEMENT = 3, -// AST_NODE_TYPE_AWAIT_EXPRESSION = 4, -// AST_NODE_TYPE_BIGINT_LITERAL = 5, -// AST_NODE_TYPE_BINARY_EXPRESSION = 6, -// AST_NODE_TYPE_BLOCK_STATEMENT = 7, -// AST_NODE_TYPE_BOOLEAN_LITERAL = 8, -// AST_NODE_TYPE_BREAK_STATEMENT = 9, -// AST_NODE_TYPE_CALL_EXPRESSION = 10, -// AST_NODE_TYPE_CATCH_CLAUSE = 11, -// AST_NODE_TYPE_CHAIN_EXPRESSION = 12, -// AST_NODE_TYPE_CHAR_LITERAL = 13, -// AST_NODE_TYPE_CLASS_DEFINITION = 14, -// AST_NODE_TYPE_CLASS_DECLARATION = 15, -// AST_NODE_TYPE_CLASS_EXPRESSION = 16, -// AST_NODE_TYPE_CLASS_PROPERTY = 17, -// AST_NODE_TYPE_CLASS_STATIC_BLOCK = 18, -// AST_NODE_TYPE_CONDITIONAL_EXPRESSION = 19, -// AST_NODE_TYPE_CONTINUE_STATEMENT = 20, -// AST_NODE_TYPE_DEBUGGER_STATEMENT = 21, -// AST_NODE_TYPE_DECORATOR = 22, -// AST_NODE_TYPE_DIRECT_EVAL = 23, -// AST_NODE_TYPE_DO_WHILE_STATEMENT = 24, -// AST_NODE_TYPE_EMPTY_STATEMENT = 25, -// AST_NODE_TYPE_EXPORT_ALL_DECLARATION = 26, -// AST_NODE_TYPE_EXPORT_DEFAULT_DECLARATION = 27, -// AST_NODE_TYPE_EXPORT_NAMED_DECLARATION = 28, -// AST_NODE_TYPE_EXPORT_SPECIFIER = 29, -// AST_NODE_TYPE_EXPRESSION_STATEMENT = 30, -// AST_NODE_TYPE_FOR_IN_STATEMENT = 31, -// AST_NODE_TYPE_FOR_OF_STATEMENT = 32, -// AST_NODE_TYPE_FOR_UPDATE_STATEMENT = 33, -// AST_NODE_TYPE_FUNCTION_DECLARATION = 34, -// AST_NODE_TYPE_FUNCTION_EXPRESSION = 35, -// AST_NODE_TYPE_IDENTIFIER = 36, -// AST_NODE_TYPE_DUMMYNODE = 37, -// AST_NODE_TYPE_IF_STATEMENT = 38, -// AST_NODE_TYPE_IMPORT_DECLARATION = 39, -// AST_NODE_TYPE_IMPORT_EXPRESSION = 40, -// AST_NODE_TYPE_IMPORT_DEFAULT_SPECIFIER = 41, -// AST_NODE_TYPE_IMPORT_NAMESPACE_SPECIFIER = 42, -// AST_NODE_TYPE_IMPORT_SPECIFIER = 43, -// AST_NODE_TYPE_LABELLED_STATEMENT = 44, -// AST_NODE_TYPE_MEMBER_EXPRESSION = 45, -// AST_NODE_TYPE_META_PROPERTY_EXPRESSION = 46, -// AST_NODE_TYPE_METHOD_DEFINITION = 47, -// AST_NODE_TYPE_NAMED_TYPE = 48, -// AST_NODE_TYPE_NAMESPACE_DECLARATION = 49, -// AST_NODE_TYPE_NAMESPACE_DEFINITION = 50, -// AST_NODE_TYPE_NEW_EXPRESSION = 51, -// AST_NODE_TYPE_NULL_LITERAL = 52, -// AST_NODE_TYPE_UNDEFINED_LITERAL = 53, -// AST_NODE_TYPE_NUMBER_LITERAL = 54, -// AST_NODE_TYPE_OMITTED_EXPRESSION = 55, -// AST_NODE_TYPE_PREFIX_ASSERTION_EXPRESSION = 56, -// AST_NODE_TYPE_PROPERTY = 57, -// AST_NODE_TYPE_REGEXP_LITERAL = 58, -// AST_NODE_TYPE_REEXPORT_STATEMENT = 59, -// AST_NODE_TYPE_RETURN_STATEMENT = 60, -// AST_NODE_TYPE_SCRIPT_FUNCTION = 61, -// AST_NODE_TYPE_SEQUENCE_EXPRESSION = 62, -// AST_NODE_TYPE_STRING_LITERAL = 63, -// AST_NODE_TYPE_ETS_NULL_TYPE = 64, -// AST_NODE_TYPE_ETS_UNDEFINED_TYPE = 65, -// AST_NODE_TYPE_ETS_NEVER_TYPE = 66, -// AST_NODE_TYPE_ETS_STRING_LITERAL_TYPE = 67, -// AST_NODE_TYPE_ETS_FUNCTION_TYPE = 68, -// AST_NODE_TYPE_ETS_WILDCARD_TYPE = 69, -// AST_NODE_TYPE_ETS_PRIMITIVE_TYPE = 70, -// AST_NODE_TYPE_ETS_PACKAGE_DECLARATION = 71, -// AST_NODE_TYPE_ETS_CLASS_LITERAL = 72, -// AST_NODE_TYPE_ETS_TYPE_REFERENCE = 73, -// AST_NODE_TYPE_ETS_TYPE_REFERENCE_PART = 74, -// AST_NODE_TYPE_ETS_UNION_TYPE = 75, -// AST_NODE_TYPE_ETS_LAUNCH_EXPRESSION = 76, -// AST_NODE_TYPE_ETS_NEW_ARRAY_INSTANCE_EXPRESSION = 77, -// AST_NODE_TYPE_ETS_NEW_MULTI_DIM_ARRAY_INSTANCE_EXPRESSION = 78, -// AST_NODE_TYPE_ETS_NEW_CLASS_INSTANCE_EXPRESSION = 79, -// AST_NODE_TYPE_ETS_IMPORT_DECLARATION = 80, -// AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION = 81, -// AST_NODE_TYPE_ETS_TUPLE = 82, -// AST_NODE_TYPE_ETS_SCRIPT = 83, -// AST_NODE_TYPE_SUPER_EXPRESSION = 84, -// AST_NODE_TYPE_STRUCT_DECLARATION = 85, -// AST_NODE_TYPE_SWITCH_CASE_STATEMENT = 86, -// AST_NODE_TYPE_SWITCH_STATEMENT = 87, -// AST_NODE_TYPE_TS_ENUM_DECLARATION = 88, -// AST_NODE_TYPE_TS_ENUM_MEMBER = 89, -// AST_NODE_TYPE_TS_EXTERNAL_MODULE_REFERENCE = 90, -// AST_NODE_TYPE_TS_NUMBER_KEYWORD = 91, -// AST_NODE_TYPE_TS_ANY_KEYWORD = 92, -// AST_NODE_TYPE_TS_STRING_KEYWORD = 93, -// AST_NODE_TYPE_TS_BOOLEAN_KEYWORD = 94, -// AST_NODE_TYPE_TS_VOID_KEYWORD = 95, -// AST_NODE_TYPE_TS_UNDEFINED_KEYWORD = 96, -// AST_NODE_TYPE_TS_UNKNOWN_KEYWORD = 97, -// AST_NODE_TYPE_TS_OBJECT_KEYWORD = 98, -// AST_NODE_TYPE_TS_BIGINT_KEYWORD = 99, -// AST_NODE_TYPE_TS_NEVER_KEYWORD = 100, -// AST_NODE_TYPE_TS_NON_NULL_EXPRESSION = 101, -// AST_NODE_TYPE_TS_NULL_KEYWORD = 102, -// AST_NODE_TYPE_TS_ARRAY_TYPE = 103, -// AST_NODE_TYPE_TS_UNION_TYPE = 104, -// AST_NODE_TYPE_TS_TYPE_LITERAL = 105, -// AST_NODE_TYPE_TS_PROPERTY_SIGNATURE = 106, -// AST_NODE_TYPE_TS_METHOD_SIGNATURE = 107, -// AST_NODE_TYPE_TS_SIGNATURE_DECLARATION = 108, -// AST_NODE_TYPE_TS_PARENT_TYPE = 109, -// AST_NODE_TYPE_TS_LITERAL_TYPE = 110, -// AST_NODE_TYPE_TS_INFER_TYPE = 111, -// AST_NODE_TYPE_TS_CONDITIONAL_TYPE = 112, -// AST_NODE_TYPE_TS_IMPORT_TYPE = 113, -// AST_NODE_TYPE_TS_INTERSECTION_TYPE = 114, -// AST_NODE_TYPE_TS_MAPPED_TYPE = 115, -// AST_NODE_TYPE_TS_MODULE_BLOCK = 116, -// AST_NODE_TYPE_TS_THIS_TYPE = 117, -// AST_NODE_TYPE_TS_TYPE_OPERATOR = 118, -// AST_NODE_TYPE_TS_TYPE_PARAMETER = 119, -// AST_NODE_TYPE_TS_TYPE_PARAMETER_DECLARATION = 120, -// AST_NODE_TYPE_TS_TYPE_PARAMETER_INSTANTIATION = 121, -// AST_NODE_TYPE_TS_TYPE_PREDICATE = 122, -// AST_NODE_TYPE_TS_PARAMETER_PROPERTY = 123, -// AST_NODE_TYPE_TS_MODULE_DECLARATION = 124, -// AST_NODE_TYPE_TS_IMPORT_EQUALS_DECLARATION = 125, -// AST_NODE_TYPE_TS_FUNCTION_TYPE = 126, -// AST_NODE_TYPE_TS_CONSTRUCTOR_TYPE = 127, -// AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION = 128, -// AST_NODE_TYPE_TS_TYPE_REFERENCE = 129, -// AST_NODE_TYPE_TS_QUALIFIED_NAME = 130, -// AST_NODE_TYPE_TS_INDEXED_ACCESS_TYPE = 131, -// AST_NODE_TYPE_TS_INTERFACE_DECLARATION = 132, -// AST_NODE_TYPE_TS_INTERFACE_BODY = 133, -// AST_NODE_TYPE_TS_INTERFACE_HERITAGE = 134, -// AST_NODE_TYPE_TS_TUPLE_TYPE = 135, -// AST_NODE_TYPE_TS_NAMED_TUPLE_MEMBER = 136, -// AST_NODE_TYPE_TS_INDEX_SIGNATURE = 137, -// AST_NODE_TYPE_TS_TYPE_QUERY = 138, -// AST_NODE_TYPE_TS_AS_EXPRESSION = 139, -// AST_NODE_TYPE_TS_CLASS_IMPLEMENTS = 140, -// AST_NODE_TYPE_TS_TYPE_ASSERTION = 141, -// AST_NODE_TYPE_TAGGED_TEMPLATE_EXPRESSION = 142, -// AST_NODE_TYPE_TEMPLATE_ELEMENT = 143, -// AST_NODE_TYPE_TEMPLATE_LITERAL = 144, -// AST_NODE_TYPE_THIS_EXPRESSION = 145, -// AST_NODE_TYPE_TYPEOF_EXPRESSION = 146, -// AST_NODE_TYPE_THROW_STATEMENT = 147, -// AST_NODE_TYPE_TRY_STATEMENT = 148, -// AST_NODE_TYPE_UNARY_EXPRESSION = 149, -// AST_NODE_TYPE_UPDATE_EXPRESSION = 150, -// AST_NODE_TYPE_VARIABLE_DECLARATION = 151, -// AST_NODE_TYPE_VARIABLE_DECLARATOR = 152, -// AST_NODE_TYPE_WHILE_STATEMENT = 153, -// AST_NODE_TYPE_YIELD_EXPRESSION = 154, -// AST_NODE_TYPE_OPAQUE_TYPE_NODE = 155, -// AST_NODE_TYPE_BLOCK_EXPRESSION = 156, -// AST_NODE_TYPE_ERROR_TYPE_NODE = 157, -// AST_NODE_TYPE_ARRAY_EXPRESSION = 158, -// AST_NODE_TYPE_ARRAY_PATTERN = 159, -// AST_NODE_TYPE_ASSIGNMENT_EXPRESSION = 160, -// AST_NODE_TYPE_ASSIGNMENT_PATTERN = 161, -// AST_NODE_TYPE_OBJECT_EXPRESSION = 162, -// AST_NODE_TYPE_OBJECT_PATTERN = 163, -// AST_NODE_TYPE_SPREAD_ELEMENT = 164, -// AST_NODE_TYPE_REST_ELEMENT = 165 -// } + export enum Es2pandaScopeType { SCOPE_TYPE_PARAM = 0, SCOPE_TYPE_CATCH_PARAM = 1, @@ -1261,4 +1094,4 @@ export enum Es2pandaCheckDecision { export enum Es2pandaCheckAction { CHECK_ACTION_CONTINUE = 0, CHECK_ACTION_SKIP_SUBTREE = 1 -} +} \ No newline at end of file diff --git a/koala-wrapper/src/generated/Es2pandaNativeModule.ts b/koala-wrapper/src/generated/Es2pandaNativeModule.ts index 4502c145f..78bf7b0e8 100644 --- a/koala-wrapper/src/generated/Es2pandaNativeModule.ts +++ b/koala-wrapper/src/generated/Es2pandaNativeModule.ts @@ -115,12 +115,6 @@ export class Es2pandaNativeModule { _ETSFunctionTypeIrFlags(context: KNativePointer, receiver: KNativePointer): KInt { throw new Error("'ETSFunctionTypeIrFlags was not overloaded by native module initialization") } - _ETSFunctionTypeIrIsThrowingConst(context: KNativePointer, receiver: KNativePointer): KBoolean { - throw new Error("'ETSFunctionTypeIrIsThrowingConst was not overloaded by native module initialization") - } - _ETSFunctionTypeIrIsRethrowingConst(context: KNativePointer, receiver: KNativePointer): KBoolean { - throw new Error("'ETSFunctionTypeIrIsRethrowingConst was not overloaded by native module initialization") - } _ETSFunctionTypeIrIsExtensionFunctionConst(context: KNativePointer, receiver: KNativePointer): KBoolean { throw new Error("'ETSFunctionTypeIrIsExtensionFunctionConst was not overloaded by native module initialization") } @@ -877,9 +871,6 @@ export class Es2pandaNativeModule { _TSTypeAliasDeclarationSetTypeParameters(context: KNativePointer, receiver: KNativePointer, typeParams: KNativePointer): void { throw new Error("'TSTypeAliasDeclarationSetTypeParameters was not overloaded by native module initialization") } - _TSTypeAliasDeclarationAnnotations(context: KNativePointer, receiver: KNativePointer): KNativePointer { - throw new Error("'TSTypeAliasDeclarationAnnotations was not overloaded by native module initialization") - } _TSTypeAliasDeclarationAnnotationsConst(context: KNativePointer, receiver: KNativePointer): KNativePointer { throw new Error("'TSTypeAliasDeclarationAnnotationsConst was not overloaded by native module initialization") } @@ -1051,12 +1042,6 @@ export class Es2pandaNativeModule { _ScriptFunctionHasThrowStatementConst(context: KNativePointer, receiver: KNativePointer): KBoolean { throw new Error("'ScriptFunctionHasThrowStatementConst was not overloaded by native module initialization") } - _ScriptFunctionIsThrowingConst(context: KNativePointer, receiver: KNativePointer): KBoolean { - throw new Error("'ScriptFunctionIsThrowingConst was not overloaded by native module initialization") - } - _ScriptFunctionIsRethrowingConst(context: KNativePointer, receiver: KNativePointer): KBoolean { - throw new Error("'ScriptFunctionIsRethrowingConst was not overloaded by native module initialization") - } _ScriptFunctionIsDynamicConst(context: KNativePointer, receiver: KNativePointer): KBoolean { throw new Error("'ScriptFunctionIsDynamicConst was not overloaded by native module initialization") } @@ -1075,9 +1060,6 @@ export class Es2pandaNativeModule { _ScriptFunctionAddFlag(context: KNativePointer, receiver: KNativePointer, flags: KInt): void { throw new Error("'ScriptFunctionAddFlag was not overloaded by native module initialization") } - _ScriptFunctionAddModifier(context: KNativePointer, receiver: KNativePointer, flags: KInt): void { - throw new Error("'ScriptFunctionAddModifier was not overloaded by native module initialization") - } _ScriptFunctionFormalParamsLengthConst(context: KNativePointer, receiver: KNativePointer): KUInt { throw new Error("'ScriptFunctionFormalParamsLengthConst was not overloaded by native module initialization") } @@ -1498,6 +1480,18 @@ export class Es2pandaNativeModule { _ETSTupleSetTypeAnnotationsList(context: KNativePointer, receiver: KNativePointer, typeNodeList: BigUint64Array, typeNodeListSequenceLength: KUInt): void { throw new Error("'ETSTupleSetTypeAnnotationsList was not overloaded by native module initialization") } + _CreateTryStatement(context: KNativePointer, block: KNativePointer, catchClauses: BigUint64Array, catchClausesSequenceLength: KUInt, finalizer: KNativePointer, finalizerInsertionsLabelPair: BigUint64Array, finalizerInsertionsLabelPairSequenceLength: KUInt, finalizerInsertionsStatement: BigUint64Array, finalizerInsertionsStatementSequenceLength: KUInt): KNativePointer { + throw new Error("'CreateTryStatement was not overloaded by native module initialization") + } + _UpdateTryStatement(context: KNativePointer, original: KNativePointer, block: KNativePointer, catchClauses: BigUint64Array, catchClausesSequenceLength: KUInt, finalizer: KNativePointer, finalizerInsertionsLabelPair: BigUint64Array, finalizerInsertionsLabelPairSequenceLength: KUInt, finalizerInsertionsStatement: BigUint64Array, finalizerInsertionsStatementSequenceLength: KUInt): KNativePointer { + throw new Error("'UpdateTryStatement was not overloaded by native module initialization") + } + _CreateTryStatement1(context: KNativePointer, other: KNativePointer): KNativePointer { + throw new Error("'CreateTryStatement1 was not overloaded by native module initialization") + } + _UpdateTryStatement1(context: KNativePointer, original: KNativePointer, other: KNativePointer): KNativePointer { + throw new Error("'UpdateTryStatement1 was not overloaded by native module initialization") + } _TryStatementFinallyBlockConst(context: KNativePointer, receiver: KNativePointer): KNativePointer { throw new Error("'TryStatementFinallyBlockConst was not overloaded by native module initialization") } @@ -2116,9 +2110,6 @@ export class Es2pandaNativeModule { _ImportDeclarationSpecifiersConst(context: KNativePointer, receiver: KNativePointer): KNativePointer { throw new Error("'ImportDeclarationSpecifiersConst was not overloaded by native module initialization") } - _ImportDeclarationSpecifiers(context: KNativePointer, receiver: KNativePointer): KNativePointer { - throw new Error("'ImportDeclarationSpecifiers was not overloaded by native module initialization") - } _ImportDeclarationIsTypeKindConst(context: KNativePointer, receiver: KNativePointer): KBoolean { throw new Error("'ImportDeclarationIsTypeKindConst was not overloaded by native module initialization") } @@ -2152,9 +2143,6 @@ export class Es2pandaNativeModule { _ETSImportDeclarationIsPureDynamicConst(context: KNativePointer, receiver: KNativePointer): KBoolean { throw new Error("'ETSImportDeclarationIsPureDynamicConst was not overloaded by native module initialization") } - _ETSImportDeclarationAssemblerName(context: KNativePointer, receiver: KNativePointer): KStringPtr { - throw new Error("'ETSImportDeclarationAssemblerName was not overloaded by native module initialization") - } _ETSImportDeclarationAssemblerNameConst(context: KNativePointer, receiver: KNativePointer): KStringPtr { throw new Error("'ETSImportDeclarationAssemblerNameConst was not overloaded by native module initialization") } @@ -3707,16 +3695,4 @@ export class Es2pandaNativeModule { _CreateFunctionDecl(context: KNativePointer, name: KStringPtr, node: KNativePointer): KNativePointer { throw new Error("'CreateFunctionDecl was not overloaded by native module initialization") } - _CreateTryStatement(context: KNativePointer, block: KNativePointer, catchClauses: BigUint64Array, catchClausesSequenceLength: KUInt, finalizer: KNativePointer, finalizerInsertionsLabelPair: BigUint64Array, finalizerInsertionsLabelPairSequenceLength: KUInt, finalizerInsertionsStatement: BigUint64Array, finalizerInsertionsStatementSequenceLength: KUInt): KNativePointer { - throw new Error("'CreateTryStatement was not overloaded by native module initialization") - } - _UpdateTryStatement(context: KNativePointer, original: KNativePointer, block: KNativePointer, catchClauses: BigUint64Array, catchClausesSequenceLength: KUInt, finalizer: KNativePointer, finalizerInsertionsLabelPair: BigUint64Array, finalizerInsertionsLabelPairSequenceLength: KUInt, finalizerInsertionsStatement: BigUint64Array, finalizerInsertionsStatementSequenceLength: KUInt): KNativePointer { - throw new Error("'UpdateTryStatement was not overloaded by native module initialization") - } - _CreateTryStatement1(context: KNativePointer, other: KNativePointer): KNativePointer { - throw new Error("'CreateTryStatement1 was not overloaded by native module initialization") - } - _UpdateTryStatement1(context: KNativePointer, original: KNativePointer, other: KNativePointer): KNativePointer { - throw new Error("'UpdateTryStatement1 was not overloaded by native module initialization") - } } diff --git a/koala-wrapper/src/generated/index.ts b/koala-wrapper/src/generated/index.ts index 0416a0691..81fc18f94 100644 --- a/koala-wrapper/src/generated/index.ts +++ b/koala-wrapper/src/generated/index.ts @@ -189,3 +189,4 @@ export * from "./peers/ETSDynamicFunctionType" export * from "./peers/InterfaceDecl" export * from "./peers/FunctionDecl" export * from "./peers/Context" +export * from "./peers/TSClassImplements"; diff --git a/koala-wrapper/src/generated/peers/AnnotatedAstNode.ts b/koala-wrapper/src/generated/peers/AnnotatedAstNode.ts index f1199de4d..13274b55f 100644 --- a/koala-wrapper/src/generated/peers/AnnotatedAstNode.ts +++ b/koala-wrapper/src/generated/peers/AnnotatedAstNode.ts @@ -14,19 +14,8 @@ */ import { - global, - passNode, - passNodeArray, - unpackNonNullableNode, - unpackNode, - unpackNodeArray, - assertValidPeer, AstNode, - Es2pandaAstNodeType, - KNativePointer, - nodeByType, - ArktsObject, - unpackString + KNativePointer } from "../../reexport-for-generated" export class AnnotatedAstNode extends AstNode { diff --git a/koala-wrapper/src/generated/peers/AnnotatedExpression.ts b/koala-wrapper/src/generated/peers/AnnotatedExpression.ts index 2a5924eb9..66718209b 100644 --- a/koala-wrapper/src/generated/peers/AnnotatedExpression.ts +++ b/koala-wrapper/src/generated/peers/AnnotatedExpression.ts @@ -16,17 +16,9 @@ import { global, passNode, - passNodeArray, - unpackNonNullableNode, unpackNode, - unpackNodeArray, - assertValidPeer, AstNode, - Es2pandaAstNodeType, - KNativePointer, - nodeByType, - ArktsObject, - unpackString + KNativePointer } from "../../reexport-for-generated" import { Expression } from "./Expression" diff --git a/koala-wrapper/src/generated/peers/BreakStatement.ts b/koala-wrapper/src/generated/peers/BreakStatement.ts index 77b609b0a..b897f67a9 100644 --- a/koala-wrapper/src/generated/peers/BreakStatement.ts +++ b/koala-wrapper/src/generated/peers/BreakStatement.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Identifier } from "./Identifier" export class BreakStatement extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_STATEMENT) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_BREAK_STATEMENT) super(pointer) } @@ -64,6 +64,6 @@ export class BreakStatement extends Statement { export function isBreakStatement(node: AstNode): node is BreakStatement { return node instanceof BreakStatement } -if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_STATEMENT)) { - nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_STATEMENT, BreakStatement) -} +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_BREAK_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_BREAK_STATEMENT, BreakStatement) +} \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/ClassDefinition.ts b/koala-wrapper/src/generated/peers/ClassDefinition.ts index fe51f912b..2da37bb63 100644 --- a/koala-wrapper/src/generated/peers/ClassDefinition.ts +++ b/koala-wrapper/src/generated/peers/ClassDefinition.ts @@ -42,6 +42,7 @@ import { TSEnumDeclaration } from "./TSEnumDeclaration" import { ClassDeclaration } from "./ClassDeclaration" import { FunctionExpression } from "./FunctionExpression" import { AnnotationUsage } from "./AnnotationUsage" +import { Es2pandaLanguage } from "../../Es2pandaEnums" export class ClassDefinition extends TypedAstNode { constructor(pointer: KNativePointer) { assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_DEFINITION) @@ -66,6 +67,12 @@ export class ClassDefinition extends TypedAstNode { static update2ClassDefinition(original: ClassDefinition | undefined, ident: Identifier | undefined, modifiers: Es2pandaClassDefinitionModifiers, flags: Es2pandaModifierFlags): ClassDefinition { return new ClassDefinition(global.generatedEs2panda._UpdateClassDefinition2(global.context, passNode(original), passNode(ident), modifiers, flags)) } + static create3ClassDefinition(ident: Identifier | undefined, typeParams: TSTypeParameterDeclaration | undefined, superTypeParams: TSTypeParameterInstantiation | undefined, _implements: readonly TSClassImplements[], ctor: MethodDefinition | undefined, superClass: Expression | undefined, body: readonly AstNode[], modifiers: Es2pandaClassDefinitionModifiers, flags: Es2pandaModifierFlags, lang: Es2pandaLanguage): ClassDefinition { + return new ClassDefinition(global.es2panda._CreateClassDefinition3(global.context, passNode(ident), passNode(typeParams), passNode(superTypeParams), passNodeArray(_implements), _implements.length, passNode(ctor), passNode(superClass), passNodeArray(body), body.length, modifiers, flags, lang)) + } + static update3ClassDefinition(original: ClassDefinition | undefined, ident: Identifier | undefined, typeParams: TSTypeParameterDeclaration | undefined, superTypeParams: TSTypeParameterInstantiation | undefined, _implements: readonly TSClassImplements[], ctor: MethodDefinition | undefined, superClass: Expression | undefined, body: readonly AstNode[], modifiers: Es2pandaClassDefinitionModifiers, flags: Es2pandaModifierFlags, lang: Es2pandaLanguage): ClassDefinition { + return new ClassDefinition(global.es2panda._UpdateClassDefinition3(global.context, passNode(original), passNode(ident), passNode(typeParams), passNode(superTypeParams), passNodeArray(_implements), _implements.length, passNode(ctor), passNode(superClass), passNodeArray(body), body.length, modifiers, flags, lang)) + } get ident(): Identifier | undefined { return unpackNode(global.generatedEs2panda._ClassDefinitionIdentConst(global.context, this.peer)) } @@ -213,6 +220,9 @@ export class ClassDefinition extends TypedAstNode { global.generatedEs2panda._ClassDefinitionSetAnnotations(global.context, this.peer, passNodeArray(annotations), annotations.length) return this } + get lang(): Es2pandaLanguage { + return global.es2panda._ClassDefinitionLanguageConst(global.context, this.peer); + } } export function isClassDefinition(node: AstNode): node is ClassDefinition { return node instanceof ClassDefinition diff --git a/koala-wrapper/src/generated/peers/ClassStaticBlock.ts b/koala-wrapper/src/generated/peers/ClassStaticBlock.ts index 5b2bef593..eeb2a2a2d 100644 --- a/koala-wrapper/src/generated/peers/ClassStaticBlock.ts +++ b/koala-wrapper/src/generated/peers/ClassStaticBlock.ts @@ -34,20 +34,27 @@ import { Expression } from "./Expression" import { ScriptFunction } from "./ScriptFunction" export class ClassStaticBlock extends ClassElement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_STATIC_BLOCK) - super(pointer) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_STATIC_BLOCK); + super(pointer); } static createClassStaticBlock(value?: Expression): ClassStaticBlock { - return new ClassStaticBlock(global.generatedEs2panda._CreateClassStaticBlock(global.context, passNode(value))) + return new ClassStaticBlock(global.generatedEs2panda._CreateClassStaticBlock(global.context, passNode(value))); } static updateClassStaticBlock(original?: ClassStaticBlock, value?: Expression): ClassStaticBlock { - return new ClassStaticBlock(global.generatedEs2panda._UpdateClassStaticBlock(global.context, passNode(original), passNode(value))) + return new ClassStaticBlock(global.generatedEs2panda._UpdateClassStaticBlock(global.context, passNode(original), passNode(value))); } + get function(): ScriptFunction | undefined { + return unpackNode(global.generatedEs2panda._ClassStaticBlockFunction(global.context, this.peer)); + } + get name(): string { + return unpackString(global.generatedEs2panda._ClassStaticBlockNameConst(global.context, this.peer)); + } + protected readonly brandClassStaticBlock: undefined; } export function isClassStaticBlock(node: AstNode): node is ClassStaticBlock { - return node instanceof ClassStaticBlock + return node instanceof ClassStaticBlock; } if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_STATIC_BLOCK)) { - nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_STATIC_BLOCK, ClassStaticBlock) + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_STATIC_BLOCK, ClassStaticBlock); } diff --git a/koala-wrapper/src/generated/peers/ETSFunctionType.ts b/koala-wrapper/src/generated/peers/ETSFunctionType.ts index 5a630295c..ed8d5f7ed 100644 --- a/koala-wrapper/src/generated/peers/ETSFunctionType.ts +++ b/koala-wrapper/src/generated/peers/ETSFunctionType.ts @@ -67,12 +67,6 @@ export class ETSFunctionType extends TypeNode { get flags(): Es2pandaScriptFunctionFlags { return global.generatedEs2panda._ETSFunctionTypeIrFlags(global.context, this.peer) } - get isThrowing(): boolean { - return global.generatedEs2panda._ETSFunctionTypeIrIsThrowingConst(global.context, this.peer) - } - get isRethrowing(): boolean { - return global.generatedEs2panda._ETSFunctionTypeIrIsRethrowingConst(global.context, this.peer) - } get isExtensionFunction(): boolean { return global.generatedEs2panda._ETSFunctionTypeIrIsExtensionFunctionConst(global.context, this.peer) } diff --git a/koala-wrapper/src/generated/peers/ScriptFunction.ts b/koala-wrapper/src/generated/peers/ScriptFunction.ts index 42c4c9343..d04c69b5e 100644 --- a/koala-wrapper/src/generated/peers/ScriptFunction.ts +++ b/koala-wrapper/src/generated/peers/ScriptFunction.ts @@ -149,12 +149,6 @@ export class ScriptFunction extends AstNode { get hasThrowStatement(): boolean { return global.generatedEs2panda._ScriptFunctionHasThrowStatementConst(global.context, this.peer) } - get isThrowing(): boolean { - return global.generatedEs2panda._ScriptFunctionIsThrowingConst(global.context, this.peer) - } - get isRethrowing(): boolean { - return global.generatedEs2panda._ScriptFunctionIsRethrowingConst(global.context, this.peer) - } get isDynamic(): boolean { return global.generatedEs2panda._ScriptFunctionIsDynamicConst(global.context, this.peer) } @@ -179,7 +173,7 @@ export class ScriptFunction extends AstNode { } /** @deprecated */ addModifier(flags: Es2pandaModifierFlags): this { - global.generatedEs2panda._ScriptFunctionAddModifier(global.context, this.peer, flags) + global.generatedEs2panda._AstNodeAddModifier(global.context, this.peer, flags) return this } get annotations(): readonly AnnotationUsage[] { diff --git a/koala-wrapper/src/generated/peers/SpreadElement.ts b/koala-wrapper/src/generated/peers/SpreadElement.ts index 4b721b993..99aa74efe 100644 --- a/koala-wrapper/src/generated/peers/SpreadElement.ts +++ b/koala-wrapper/src/generated/peers/SpreadElement.ts @@ -22,46 +22,77 @@ import { unpackNodeArray, assertValidPeer, AstNode, - Es2pandaAstNodeType, KNativePointer, nodeByType, ArktsObject, - unpackString -} from "../../reexport-for-generated" + unpackString, + Es2pandaAstNodeType, +} from '../../reexport-for-generated'; + +import { AnnotatedExpression } from './AnnotatedExpression'; +import { Decorator } from './Decorator'; +import { Expression } from './Expression'; +import { TypeNode } from './TypeNode'; +import { ValidationInfo } from './ValidationInfo'; -import { AnnotatedExpression } from "./AnnotatedExpression" -import { Expression } from "./Expression" -import { Decorator } from "./Decorator" -import { ValidationInfo } from "./ValidationInfo" -import { TypeNode } from "./TypeNode" export class SpreadElement extends AnnotatedExpression { - constructor(pointer: KNativePointer) { - super(pointer) - + constructor(pointer: KNativePointer) { + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_SPREAD_ELEMENT); + super(pointer); + } + static createSpreadElement(nodeType: Es2pandaAstNodeType, argument?: Expression): SpreadElement { + const result: SpreadElement = new SpreadElement( + global.generatedEs2panda._CreateSpreadElement(global.context, nodeType, passNode(argument)) + ); + result.updateChildren(); + return result; + } + static updateSpreadElement( + original: SpreadElement | undefined, + nodeType: Es2pandaAstNodeType, + argument?: Expression + ): SpreadElement { + const result: SpreadElement = new SpreadElement( + global.generatedEs2panda._UpdateSpreadElement( + global.context, + passNode(original), + nodeType, + passNode(argument) + ) + ); + result.updateChildren(); + return result; } get argument(): Expression | undefined { - return unpackNode(global.generatedEs2panda._SpreadElementArgumentConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._SpreadElementArgument(global.context, this.peer)); } get isOptional(): boolean { - return global.generatedEs2panda._SpreadElementIsOptionalConst(global.context, this.peer) + return global.generatedEs2panda._SpreadElementIsOptionalConst(global.context, this.peer); } get decorators(): readonly Decorator[] { - return unpackNodeArray(global.generatedEs2panda._SpreadElementDecoratorsConst(global.context, this.peer)) + return unpackNodeArray(global.generatedEs2panda._SpreadElementDecoratorsConst(global.context, this.peer)); } /** @deprecated */ setOptional(optional_arg: boolean): this { - global.generatedEs2panda._SpreadElementSetOptional(global.context, this.peer, optional_arg) - return this + global.generatedEs2panda._SpreadElementSetOptional(global.context, this.peer, optional_arg); + return this; + } + get validateExpression(): ValidationInfo | undefined { + return new ValidationInfo(global.generatedEs2panda._SpreadElementValidateExpression(global.context, this.peer)); } get typeAnnotation(): TypeNode | undefined { - return unpackNode(global.generatedEs2panda._SpreadElementTypeAnnotationConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._SpreadElementTypeAnnotationConst(global.context, this.peer)); } /** @deprecated */ - setTsTypeAnnotation(typeAnnotation: TypeNode): this { - global.generatedEs2panda._SpreadElementSetTsTypeAnnotation(global.context, this.peer, passNode(typeAnnotation)) - return this + setTsTypeAnnotation(typeAnnotation?: TypeNode): this { + global.generatedEs2panda._SpreadElementSetTsTypeAnnotation(global.context, this.peer, passNode(typeAnnotation)); + return this; } + protected readonly brandSpreadElement: undefined; +} +export function isSpreadElement(node: object | undefined): node is SpreadElement { + return node instanceof SpreadElement; +} +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_SPREAD_ELEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_SPREAD_ELEMENT, SpreadElement); } -export function isSpreadElement(node: AstNode): node is SpreadElement { - return node instanceof SpreadElement -} \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/TryStatement.ts b/koala-wrapper/src/generated/peers/TryStatement.ts index 72141cddc..d6519a347 100644 --- a/koala-wrapper/src/generated/peers/TryStatement.ts +++ b/koala-wrapper/src/generated/peers/TryStatement.ts @@ -26,7 +26,7 @@ import { nodeByType, ArktsObject, unpackString, - Es2pandaAstNodeType + Es2pandaAstNodeType, } from '../../reexport-for-generated'; import { BlockStatement } from './BlockStatement'; import { CatchClause } from './CatchClause'; -- Gitee