diff --git a/OAT.xml b/OAT.xml
index 29eae41eedee99a9e500aa1f4e53b08ccda5e501..956a5add37c4e7ed742a1a2a5c81dc634e9eb89e 100644
--- a/OAT.xml
+++ b/OAT.xml
@@ -16,6 +16,7 @@
+
@@ -35,6 +36,7 @@
+
@@ -57,6 +59,7 @@
+
diff --git a/example/.eslintrc b/example/.eslintrc
new file mode 100644
index 0000000000000000000000000000000000000000..76cd1cdca17f5f45ea5a75fd4b493c47655cbe67
--- /dev/null
+++ b/example/.eslintrc
@@ -0,0 +1,13 @@
+{
+ "extends": "@react-native",
+ "rules": {
+ "react-native/no-inline-styles": "off",
+ "react/no-unstable-nested-components": "off",
+ "react/react-in-jsx-scope": "off",
+ "@typescript-eslint/no-unused-vars": "warn",
+ "react-hooks/exhaustive-deps": "off",
+ "radix": "off",
+ "prettier/prettier": "warn",
+ "max-lines": "off"
+ }
+}
\ No newline at end of file
diff --git a/example/.gitignore b/example/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7613cdedd309866629ce9ae75eb3855ea0849f0a
--- /dev/null
+++ b/example/.gitignore
@@ -0,0 +1,71 @@
+.husky
+bundle.harmony.js
+package-lock.json
+*.hbc
+lintCppResult.txt
+
+# ---
+
+# OSX
+#
+.DS_Store
+
+# Xcode
+#
+build/
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata
+*.xccheckout
+*.moved-aside
+DerivedData
+*.hmap
+*.ipa
+*.xcuserstate
+
+# Android/IntelliJ
+#
+build/
+.idea
+.gradle
+local.properties
+*.iml
+*.hprof
+.cxx/
+*.keystore
+!debug.keystore
+BuildProfile.ets
+
+# node.js
+#
+node_modules/
+npm-debug.log
+yarn-error.log
+
+# fastlane
+#
+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
+# screenshots whenever they are needed.
+# For more information about the recommended setup visit:
+# https://docs.fastlane.tools/best-practices/source-control/
+
+**/fastlane/report.xml
+**/fastlane/Preview.html
+**/fastlane/screenshots
+**/fastlane/test_output
+
+# Bundle artifact
+*.jsbundle
+
+# Ruby / CocoaPods
+/vendor/bundle/
+
+# Temporary files created by Metro to check the health of the file watcher
+.metro-health-check*
+*.htrace
\ No newline at end of file
diff --git a/example/.node-version b/example/.node-version
new file mode 100644
index 0000000000000000000000000000000000000000..3c032078a4a21c5c51d3c93d91717c1dabbb8cd0
--- /dev/null
+++ b/example/.node-version
@@ -0,0 +1 @@
+18
diff --git a/example/.prettierrc.js b/example/.prettierrc.js
new file mode 100644
index 0000000000000000000000000000000000000000..2ae7b381ed9590ddf3ba512a496dc8d53dadeea0
--- /dev/null
+++ b/example/.prettierrc.js
@@ -0,0 +1,7 @@
+module.exports = {
+ arrowParens: 'avoid',
+ bracketSameLine: true,
+ bracketSpacing: true,
+ singleQuote: true,
+ trailingComma: 'all',
+};
diff --git a/example/.watchmanconfig b/example/.watchmanconfig
new file mode 100644
index 0000000000000000000000000000000000000000..9e26dfeeb6e641a33dae4961196235bdb965b21b
--- /dev/null
+++ b/example/.watchmanconfig
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/example/app.json b/example/app.json
new file mode 100644
index 0000000000000000000000000000000000000000..7586f6fa99a6680a35a27fac7255ba721880d461
--- /dev/null
+++ b/example/app.json
@@ -0,0 +1,4 @@
+{
+ "name": "app_name",
+ "displayName": "tester"
+}
\ No newline at end of file
diff --git a/example/babel.config.js b/example/babel.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..481136144aa7bed704ad81628802d8a43930a6a1
--- /dev/null
+++ b/example/babel.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ presets: ['module:metro-react-native-babel-preset'],
+ plugins: [
+ ],
+};
diff --git a/example/contexts.ts b/example/contexts.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9437598452691ccc56d19f5532607475aa5d33b4
--- /dev/null
+++ b/example/contexts.ts
@@ -0,0 +1,3 @@
+import React from 'react';
+
+export const AppParamsContext = React.createContext(undefined);
diff --git a/example/harmony/.gitignore b/example/harmony/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..72d73fb4b6240ccd907882c931ce08ee45ca62ba
--- /dev/null
+++ b/example/harmony/.gitignore
@@ -0,0 +1,24 @@
+# it may cause some issues when building the project when switching branches
+package-lock.json
+# we add this because we want to keep the signing configs out of git
+/build-profile.json5
+
+rnoh_modules
+
+**/oh-package-lock.json5
+# ---
+
+/node_modules
+/local.properties
+/.idea
+**/build
+/.hvigor
+.cxx
+/.clangd
+/.clang-format
+/.clang-tidy
+/oh_modules
+hvigorw
+hvigorw.bat
+hvigor/hvigor-wrapper.js
+
diff --git a/example/harmony/AppScope/app.json5 b/example/harmony/AppScope/app.json5
new file mode 100644
index 0000000000000000000000000000000000000000..058bd391dac60928a1857aa31e4b0f712a7bc743
--- /dev/null
+++ b/example/harmony/AppScope/app.json5
@@ -0,0 +1,10 @@
+{
+ "app": {
+ "bundleName": "com.harmony.wechat.lib.demo",
+ "vendor": "example",
+ "versionCode": 1000000,
+ "versionName": "1.0.0",
+ "icon": "$media:app_icon",
+ "label": "$string:app_name"
+ }
+}
diff --git a/example/harmony/AppScope/resources/base/element/string.json b/example/harmony/AppScope/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..698a720065342ae0dadad63eb87d45fec8725f36
--- /dev/null
+++ b/example/harmony/AppScope/resources/base/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "app_name",
+ "value": "RN Tester"
+ }
+ ]
+}
diff --git a/example/harmony/AppScope/resources/base/media/app_icon.png b/example/harmony/AppScope/resources/base/media/app_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c
Binary files /dev/null and b/example/harmony/AppScope/resources/base/media/app_icon.png differ
diff --git a/example/harmony/build-profile.template.json5 b/example/harmony/build-profile.template.json5
new file mode 100644
index 0000000000000000000000000000000000000000..261441975aaea2bd1ae88a5b063a5b2878efe0e9
--- /dev/null
+++ b/example/harmony/build-profile.template.json5
@@ -0,0 +1,40 @@
+{
+ "app": {
+ "products": [
+ {
+ "name": 'default',
+ "signingConfig": 'default',
+ "compileSdkVersion": '5.0.0(12)',
+ "compatibleSdkVersion": '5.0.0(12)',
+ "runtimeOS": 'HarmonyOS'
+ },
+ ],
+ "buildModeSet": [
+ {
+ "name": 'debug',
+ },
+ {
+ "name": 'release',
+ },
+ ],
+ "signingConfigs": []
+ },
+ "modules": [
+ {
+ "name": 'entry',
+ "srcPath": './entry',
+ "targets": [
+ {
+ "name": 'default',
+ "applyToProducts": [
+ 'default'
+ ],
+ },
+ ],
+ },
+ {
+ "name": "fs",
+ "srcPath": './fs'
+ }
+ ],
+}
\ No newline at end of file
diff --git a/example/harmony/codelinter.json b/example/harmony/codelinter.json
new file mode 100644
index 0000000000000000000000000000000000000000..e7f91acb037aeda939967a042f16033f4c6e21b9
--- /dev/null
+++ b/example/harmony/codelinter.json
@@ -0,0 +1,32 @@
+{
+ "files": ["**/*.ts", "**/*.ets"],
+ "ignore": [
+ "**/ohosTest/**/*",
+ "**/node_modules/**/*",
+ "**/hvigorfile.ts",
+ "**/node_modules/**/*",
+ "**/oh_modules/**/*",
+ "**/build/**/*",
+ "**/.preview/**/*"
+ ],
+ "plugins": ["@typescript-eslint"],
+ "ruleSet": [],
+ "rules": {
+ "@typescript-eslint/await-thenable": "warn",
+ "@typescript-eslint/consistent-type-imports": "warn",
+ "@typescript-eslint/explicit-function-return-type": "warn",
+ "@typescript-eslint/explicit-module-boundary-types": "warn",
+ "@typescript-eslint/no-dynamic-delete": "warn",
+ "@typescript-eslint/no-explicit-any": "warn",
+ "@typescript-eslint/no-for-in-array": "warn",
+ "@typescript-eslint/no-this-alias": "warn",
+ "@typescript-eslint/no-unnecessary-type-constraint": "warn",
+ "@typescript-eslint/no-unsafe-argument": "warn",
+ "@typescript-eslint/no-unsafe-assignment": "warn",
+ "@typescript-eslint/no-unsafe-call": "warn",
+ "@typescript-eslint/no-unsafe-member-access": "warn",
+ "@typescript-eslint/no-unsafe-return": "warn",
+ "@typescript-eslint/prefer-literal-enum-member": "warn"
+ },
+ "overrides": []
+}
diff --git a/example/harmony/entry/.gitignore b/example/harmony/entry/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..bbc049cfeb689c61d6c2922810f652cbb19aad21
--- /dev/null
+++ b/example/harmony/entry/.gitignore
@@ -0,0 +1,7 @@
+/node_modules
+/.preview
+/build
+/.cxx
+package-lock.json
+/oh_modules
+/assets
diff --git a/example/harmony/entry/build-profile.json5 b/example/harmony/entry/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..2408c163287e825139a36f35cbec83e9772c49db
--- /dev/null
+++ b/example/harmony/entry/build-profile.json5
@@ -0,0 +1,19 @@
+{
+ "apiType": 'stageMode',
+ "buildOption": {
+ "externalNativeOptions": {
+ "path": "./src/main/cpp/CMakeLists.txt",
+ "arguments": "",
+ "cppFlags": "-s",
+ },
+ },
+ "targets": [
+ {
+ "name": "default",
+ "runtimeOS": "HarmonyOS"
+ },
+ {
+ "name": "ohosTest",
+ }
+ ]
+}
\ No newline at end of file
diff --git a/example/harmony/entry/hvigorfile.ts b/example/harmony/entry/hvigorfile.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aced8afddf910b99762b7eff3a8df92cb9f30c2b
--- /dev/null
+++ b/example/harmony/entry/hvigorfile.ts
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
+export { hapTasks } from '@ohos/hvigor-ohos-plugin';
diff --git a/example/harmony/entry/oh-package.json5 b/example/harmony/entry/oh-package.json5
new file mode 100644
index 0000000000000000000000000000000000000000..4e2abcf914291f9b6ee5803b5654c004e4aa8456
--- /dev/null
+++ b/example/harmony/entry/oh-package.json5
@@ -0,0 +1,12 @@
+{
+ "license": "ISC",
+ "devDependencies": {},
+ "name": "entry",
+ "description": "example description",
+ "version": "1.0.0",
+ "dependencies": {
+ "@rnoh/react-native-openharmony": "0.72.38",
+ "@react-native-ohos/react-native-fs": "file:../fs"
+// "@react-native-ohos/react-native-fs": "file:../../node_modules/@react-native-ohos/react-native-fs/harmony/fs.har",
+ }
+}
\ No newline at end of file
diff --git a/example/harmony/entry/src/main/cpp/.gitignore b/example/harmony/entry/src/main/cpp/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..881dde5479f322de9db2b2bb98fd94a95f73715e
--- /dev/null
+++ b/example/harmony/entry/src/main/cpp/.gitignore
@@ -0,0 +1,2 @@
+jsbundle.h
+generated/
diff --git a/example/harmony/entry/src/main/cpp/CMakeLists.txt b/example/harmony/entry/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..46c611b05f9f6775bd001f0979edd63d7acef3dd
--- /dev/null
+++ b/example/harmony/entry/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,35 @@
+project(rnapp)
+cmake_minimum_required(VERSION 3.4.1)
+set(CMAKE_SKIP_BUILD_RPATH TRUE)
+set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules")
+set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
+set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules/@rnoh/react-native-openharmony/src/main/cpp")
+set(RNOH_GENERATED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated")
+set(LOG_VERBOSITY_LEVEL 1)
+set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments")
+set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie")
+set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
+
+set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use
+add_compile_definitions(WITH_HITRACE_SYSTRACE)
+
+add_subdirectory("${RNOH_CPP_DIR}" ./rn)
+
+# RNOH_BEGIN: manual_package_linking_1
+add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-fs/src/main/cpp" ./fs)
+# RNOH_END: manual_package_linking_1
+
+file(GLOB GENERATED_CPP_FILES "${CMAKE_CURRENT_SOURCE_DIR}/generated/*.cpp") # this line is needed by codegen v1
+
+add_library(rnoh_app SHARED
+ ${GENERATED_CPP_FILES}
+ "./PackageProvider.cpp"
+ "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
+)
+target_link_libraries(rnoh_app PUBLIC rnoh)
+
+# RNOH_BEGIN: manual_package_linking_2
+target_link_libraries(rnoh_app PUBLIC rnoh_fs)
+
+# RNOH_END: manual_package_linking_2
diff --git a/example/harmony/entry/src/main/cpp/PackageProvider.cpp b/example/harmony/entry/src/main/cpp/PackageProvider.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d84358255f2ada250a8171bf83b0bf6264076b76
--- /dev/null
+++ b/example/harmony/entry/src/main/cpp/PackageProvider.cpp
@@ -0,0 +1,12 @@
+#include "RNOH/PackageProvider.h"
+#include "generated/RNOHGeneratedPackage.h"
+#include "RNFSPackage.h"
+
+using namespace rnoh;
+
+std::vector> PackageProvider::getPackages(Package::Context ctx) {
+ return {
+ std::make_shared(ctx), // generated by codegen v1
+ std::make_shared(ctx)
+ };
+}
\ No newline at end of file
diff --git a/example/harmony/entry/src/main/ets/RNPackagesFactory.ets b/example/harmony/entry/src/main/ets/RNPackagesFactory.ets
new file mode 100644
index 0000000000000000000000000000000000000000..6fc249c5903e5fe697b10e81cc1383e7ac66c0b9
--- /dev/null
+++ b/example/harmony/entry/src/main/ets/RNPackagesFactory.ets
@@ -0,0 +1,32 @@
+/**
+ * MIT License
+ *
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+import type {RNPackageContext, RNPackage} from '@rnoh/react-native-openharmony/ts';
+import { FsPackage } from '@react-native-ohos/react-native-fs/ts';
+
+export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
+ return [
+ new FsPackage(ctx)
+ ];
+}
diff --git a/example/harmony/entry/src/main/ets/assets/fonts/Pacifico-Regular.ttf b/example/harmony/entry/src/main/ets/assets/fonts/Pacifico-Regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..e7def95d3f44c82086f6e74d93fc0aadac7c454a
Binary files /dev/null and b/example/harmony/entry/src/main/ets/assets/fonts/Pacifico-Regular.ttf differ
diff --git a/example/harmony/entry/src/main/ets/assets/fonts/StintUltraCondensed-Regular.ttf b/example/harmony/entry/src/main/ets/assets/fonts/StintUltraCondensed-Regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..25c749e167bd5beef18360b13ae660f1c10ebc62
Binary files /dev/null and b/example/harmony/entry/src/main/ets/assets/fonts/StintUltraCondensed-Regular.ttf differ
diff --git a/example/harmony/entry/src/main/ets/entryability/EntryAbility.ets b/example/harmony/entry/src/main/ets/entryability/EntryAbility.ets
new file mode 100644
index 0000000000000000000000000000000000000000..35746494ac1dda87b830c77681493aed4f389b61
--- /dev/null
+++ b/example/harmony/entry/src/main/ets/entryability/EntryAbility.ets
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
+ * Use of this source code is governed by a MIT license that can be
+ * found in the LICENSE file.
+ */
+
+import {RNAbility} from '@rnoh/react-native-openharmony';
+import { AbilityConstant, Want } from '@kit.AbilityKit';
+
+export default class EntryAbility extends RNAbility {
+
+ onCreate(want: Want) {
+ super.onCreate(want)
+ }
+
+ getPagePath() {
+ return 'pages/Index';
+ }
+}
diff --git a/example/harmony/entry/src/main/ets/pages/Index.ets b/example/harmony/entry/src/main/ets/pages/Index.ets
new file mode 100644
index 0000000000000000000000000000000000000000..5d4aa1e47485efbe3ddcffd5503e622c99463a46
--- /dev/null
+++ b/example/harmony/entry/src/main/ets/pages/Index.ets
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
+ * Use of this source code is governed by a MIT license that can be
+ * found in the LICENSE file.
+ */
+
+import {
+ AnyJSBundleProvider,
+ ComponentBuilderContext,
+ FileJSBundleProvider,
+ MetroJSBundleProvider,
+ ResourceJSBundleProvider,
+ RNApp,
+ RNOHErrorDialog,
+ RNOHLogger,
+ TraceJSBundleProviderDecorator,
+ RNOHCoreContext
+} from '@rnoh/react-native-openharmony';
+import font from '@ohos.font';
+import { createRNPackages } from '../RNPackagesFactory';
+
+const arkTsComponentNames: Array = [
+];
+
+@Builder
+export function buildCustomRNComponent(ctx: ComponentBuilderContext) {
+ // There seems to be a problem with the placement of ArkTS components in mixed mode. Nested Stack temporarily avoided.
+ Stack() {
+ }
+ .position({ x: 0, y: 0 })
+
+}
+
+const wrappedCustomRNComponentBuilder = wrapBuilder(buildCustomRNComponent)
+
+/**
+ * If you want to use custom fonts, you need to register them here.
+ * We should support react-native-asset to handle registering fonts automatically.
+ */
+const fonts: font.FontOptions[] = [
+ {
+ familyName: 'Pacifico-Regular',
+ familySrc: '/assets/fonts/Pacifico-Regular.ttf'
+ },
+ {
+ familyName: 'StintUltraCondensed-Regular',
+ familySrc: '/assets/fonts/StintUltraCondensed-Regular.ttf'
+ }
+]
+
+@Entry
+@Component
+struct Index {
+ @StorageLink('RNOHCoreContext') private rnohCoreContext: RNOHCoreContext | undefined = undefined
+ @State shouldShow: boolean = false
+ private logger!: RNOHLogger
+ bundlePath: string = 'bunlde.harmony.js'
+ @State hasBundle: boolean = false
+
+ aboutToAppear() {
+ this.logger = this.rnohCoreContext!.logger.clone("Index")
+ const stopTracing = this.logger.clone("aboutToAppear").startTracing()
+ for (const customFont of fonts) {
+ font.registerFont(customFont)
+ }
+
+ this.shouldShow = true
+ stopTracing()
+ }
+
+ onBackPress(): boolean | undefined {
+ // NOTE: this is required since `Ability`'s `onBackPressed` function always
+ // terminates or puts the app in the background, but we want Ark to ignore it completely
+ // when handled by RN
+ this.rnohCoreContext!.dispatchBackPress()
+ return true
+ }
+
+ build() {
+ Column() {
+ if (this.rnohCoreContext && this.shouldShow) {
+ if (this.rnohCoreContext?.isDebugModeEnabled) {
+ RNOHErrorDialog({ ctx: this.rnohCoreContext })
+ }
+ RNApp({
+ rnInstanceConfig: {
+ createRNPackages,
+ enableNDKTextMeasuring: true,
+ enableBackgroundExecutor: false,
+ enableCAPIArchitecture: true,
+ arkTsComponentNames: arkTsComponentNames,
+ },
+ initialProps: { "foo": "bar" } as Record,
+ appKey: "app_name",
+ wrappedCustomRNComponentBuilder: wrappedCustomRNComponentBuilder,
+ onSetUp: (rnInstance) => {
+ rnInstance.enableFeatureFlag("ENABLE_RN_INSTANCE_CLEAN_UP")
+ },
+ jsBundleProvider: new TraceJSBundleProviderDecorator(
+ new AnyJSBundleProvider([
+ new MetroJSBundleProvider(),
+ // NOTE: to load the bundle from file, place it in
+ // `/data/app/el2/100/base/com.rnoh.tester/files/bundle.harmony.js`
+ // on your device. The path mismatch is due to app sandboxing on HarmonyOS
+ new FileJSBundleProvider('/data/storage/el2/base/files/bundle.harmony.js'),
+ // new FileJSBundleProvider(context.filesDir + '/' + this.bundlePath),
+ new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'hermes_bundle.hbc'),
+ new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'bundle.harmony.js')
+ ]),
+ this.rnohCoreContext.logger),
+ })
+ }
+ Text("1233333333122 1212323")
+ }
+ .height('100%')
+ .width('100%')
+ }
+}
diff --git a/example/harmony/entry/src/main/ets/pages/SurfaceDeadlockTest.ets b/example/harmony/entry/src/main/ets/pages/SurfaceDeadlockTest.ets
new file mode 100644
index 0000000000000000000000000000000000000000..88e4439681c4981d7ab7e1b87a46653a9e5a9c35
--- /dev/null
+++ b/example/harmony/entry/src/main/ets/pages/SurfaceDeadlockTest.ets
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
+ * Use of this source code is governed by a MIT license that can be
+ * found in the LICENSE file.
+ */
+import { RNInstance, JSBundleProvider, RNAbility, RNSurface } from '@rnoh/react-native-openharmony'
+import { CustomComponentBuilder } from "@rnoh/react-native-openharmony/src/main/ets/RNOHCorePackage"
+import { SurfaceConfig2 } from '@rnoh/react-native-openharmony/src/main/ets/RNSurface'
+
+
+@Component
+export struct SurfaceDeadlockTest {
+ public jsBundleProvider: JSBundleProvider | undefined = undefined
+ public appKeys: string[] = []
+ public numberOfIterations: number = 1
+ @BuilderParam public buildCustomComponent!: CustomComponentBuilder
+ // -------------------------------------------------------------------------------------------------------------------
+ @StorageLink('RNAbility') private rnAbility: RNAbility = {} as RNAbility
+ private rnInstance!: RNInstance
+ @State private shouldShow: boolean = false
+ private shouldDestroyRNInstance: boolean = false
+ private cleanUpCallbacks: (() => void)[] = []
+
+ aboutToAppear() {
+ this.getOrCreateRNInstance().then(rnInstance => {
+ this.rnInstance = rnInstance
+ const jsBundleExecutionStatus = this.rnInstance.getBundleExecutionStatus(this.jsBundleProvider?.getURL())
+ if (this.jsBundleProvider && jsBundleExecutionStatus === undefined) {
+ this.rnInstance.runJSBundle(this.jsBundleProvider).then(() => {
+ this.shouldShow = true
+ })
+ return;
+ }
+ }).catch((reason: string | Error) => {
+ if (typeof reason === "string")
+ this.rnAbility.getLogger().error(reason)
+ else if (reason instanceof Error) {
+ this.rnAbility.getLogger().error(reason.message)
+ } else {
+ this.rnAbility.getLogger().error("Fatal exception")
+ }
+ })
+ }
+
+ aboutToDisappear() {
+ if (this.shouldDestroyRNInstance)
+ this.rnAbility.destroyAndUnregisterRNInstance(this.rnInstance)
+ this.cleanUpCallbacks.forEach(cleanUp => cleanUp())
+ }
+
+ private getOrCreateRNInstance(): Promise {
+ return this.rnAbility.createAndRegisterRNInstance({ createRNPackages: () => [] })
+ }
+
+ build() {
+ Stack() {
+ if (this.shouldShow) {
+ ForEach(this.appKeys, (appKey: string, idx) => {
+ Stack() {
+ Blinker({
+ minDelayInMs: 1000,
+ maxDelayInMs: 2000,
+ blinksCount: this.numberOfIterations,
+ randomnessPrecisionInMs: 500
+ }) {
+ RNSurface({
+ ctx: this.rnAbility.createRNOHContext({ rnInstance: this.rnInstance }),
+ surfaceConfig: {
+ initialProps: {},
+ appKey: appKey,
+ } as SurfaceConfig2,
+ buildCustomComponent: this.buildCustomComponent,
+ })
+ }
+ }.height(`${100 / this.appKeys.length}%`)
+ .position({ x: 0, y: `${(idx / this.appKeys.length) * 100}%` })
+ })
+ }
+ }.width("100%")
+ .height("100%")
+ }
+}
+
+
+@Component
+struct Blinker {
+ public minDelayInMs: number = 0
+ public maxDelayInMs: number = 1000
+ public blinksCount: number = 0
+ public randomnessPrecisionInMs: number = 250
+ @BuilderParam public renderChildren: () => void
+ private currentBlinksCount = 0
+ @State private isVisible: boolean = false
+ private timeout: number = 0
+
+ aboutToAppear() {
+ this.blink(this.minDelayInMs)
+ }
+
+ aboutToDisappear() {
+ clearTimeout(this.timeout)
+ }
+
+ private blink(ms: number) {
+ this.isVisible = !this.isVisible
+ this.currentBlinksCount += 1
+ if (this.currentBlinksCount >= this.blinksCount) {
+ if (this.timeout) {
+ clearTimeout(this.timeout)
+ }
+ this.isVisible = true
+ return;
+ }
+ this.timeout = setTimeout(() => {
+ this.blink(this.getNextDelay())
+ }, ms)
+ }
+
+ private getNextDelay(): number {
+ return ((Math.floor(Math.random() * (Number.MAX_VALUE / this.randomnessPrecisionInMs)) * this.randomnessPrecisionInMs) % this.maxDelayInMs) + this.minDelayInMs
+ }
+
+ build() {
+ Stack() {
+ if (this.isVisible) {
+ this.renderChildren()
+ }
+ }
+ }
+}
+
+
+
+
+
diff --git a/example/harmony/entry/src/main/ets/pages/TouchDisplayer.ets b/example/harmony/entry/src/main/ets/pages/TouchDisplayer.ets
new file mode 100644
index 0000000000000000000000000000000000000000..2c62df2983416a0fd5eca7e2e730283d139e0671
--- /dev/null
+++ b/example/harmony/entry/src/main/ets/pages/TouchDisplayer.ets
@@ -0,0 +1,37 @@
+@Component
+export struct TouchDisplayer {
+ @State currentTouches: TouchObject[] = []
+ @State touchIndicatorOpacity: number = 0
+ @BuilderParam buildChildren: () => void
+ build() {
+ Stack() {
+ this.buildChildren()
+ ForEach(this.currentTouches, (activeTouch: TouchObject) => {
+ Stack() {
+ }
+ .width(64)
+ .height(64)
+ .backgroundColor("blue")
+ .borderWidth(2)
+ .borderColor("white")
+ .opacity(this.touchIndicatorOpacity)
+ .position({ x: activeTouch.x - 32, y: activeTouch.y - 32 })
+ .borderRadius(1000)
+ .hitTestBehavior(HitTestMode.Transparent)
+ })
+ }
+ .width("100%")
+ .height("100%")
+ .hitTestBehavior(HitTestMode.Transparent)
+ .onTouch(e => {
+ this.currentTouches = e.touches
+ this.touchIndicatorOpacity = 0.5
+ animateTo({
+ duration: 500,
+ curve: Curve.Linear,
+ }, () => {
+ this.touchIndicatorOpacity = 0
+ })
+ })
+ }
+}
\ No newline at end of file
diff --git a/example/harmony/entry/src/main/module.json5 b/example/harmony/entry/src/main/module.json5
new file mode 100644
index 0000000000000000000000000000000000000000..c7700c2c5f348cd7d73ed4cf3b8d7112a46295b9
--- /dev/null
+++ b/example/harmony/entry/src/main/module.json5
@@ -0,0 +1,52 @@
+{
+ "module": {
+ "name": "entry",
+ "type": "entry",
+ "description": "$string:module_desc",
+ "mainElement": "EntryAbility",
+ "deviceTypes": [
+ "default"
+ ],
+ "querySchemes": [
+ "weixin",
+ ],
+ "deliveryWithInstall": true,
+ "installationFree": false,
+ "pages": "$profile:main_pages",
+ // below property is supported from 5.0.0 - it is needed by bundleManager.canOpenLink to check if the app can open some url
+ // "querySchemes": ["maps", "http", "https", "customDomain"],
+ "requestPermissions": [
+ {
+ "name": "ohos.permission.INTERNET"
+ },
+ ],
+ "metadata": [
+ {
+ "name": "OPTLazyForEach",
+ "value": "true",
+ }
+ ],
+ "abilities": [
+ {
+ "name": "EntryAbility",
+ "srcEntry": "./ets/entryability/EntryAbility.ets",
+ "description": "$string:EntryAbility_desc",
+ "icon": "$media:icon",
+ "label": "$string:EntryAbility_label",
+ "startWindowIcon": "$media:icon",
+ "startWindowBackground": "$color:start_window_background",
+ "visible": true,
+ "skills": [
+ {
+ "entities": [
+ "entity.system.home"
+ ],
+ "actions": [
+ "action.system.home"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/example/harmony/entry/src/main/resources/.gitignore b/example/harmony/entry/src/main/resources/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..037cea9e70bef8c906560a871a394f748ec2325d
--- /dev/null
+++ b/example/harmony/entry/src/main/resources/.gitignore
@@ -0,0 +1 @@
+rawfile/assets
diff --git a/example/harmony/entry/src/main/resources/base/element/color.json b/example/harmony/entry/src/main/resources/base/element/color.json
new file mode 100644
index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02
--- /dev/null
+++ b/example/harmony/entry/src/main/resources/base/element/color.json
@@ -0,0 +1,8 @@
+{
+ "color": [
+ {
+ "name": "start_window_background",
+ "value": "#FFFFFF"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/example/harmony/entry/src/main/resources/base/element/string.json b/example/harmony/entry/src/main/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..074b2173f51352f71629252715526554911d44bb
--- /dev/null
+++ b/example/harmony/entry/src/main/resources/base/element/string.json
@@ -0,0 +1,16 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "module description"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "RN Tester"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/example/harmony/entry/src/main/resources/base/media/icon.png b/example/harmony/entry/src/main/resources/base/media/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..c0f05abe7eb110a3c4958a48f71ea15b28bae5d3
Binary files /dev/null and b/example/harmony/entry/src/main/resources/base/media/icon.png differ
diff --git a/example/harmony/entry/src/main/resources/rawfile/1.txt b/example/harmony/entry/src/main/resources/rawfile/1.txt
new file mode 100644
index 0000000000000000000000000000000000000000..71f6ee3dfcd13fad21fc73e697c7c5a3c31ec039
--- /dev/null
+++ b/example/harmony/entry/src/main/resources/rawfile/1.txt
@@ -0,0 +1 @@
+text test
\ No newline at end of file
diff --git a/example/harmony/format.ps1 b/example/harmony/format.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..7735881fded75cc61ee05ec623e0848b85f6d639
--- /dev/null
+++ b/example/harmony/format.ps1
@@ -0,0 +1,12 @@
+$directoryPath = Split-Path -Parent $MyInvocation.MyCommand.Path
+$filePaths = Get-ChildItem $directoryPath -Recurse -Include *.h, *.cpp |
+Where-Object {
+ $_.DirectoryName -notmatch 'third-party' -and
+ $_.DirectoryName -notmatch 'patches' -and
+ $_.DirectoryName -notmatch 'node_modules' -and
+ $_.DirectoryName -notmatch '.cxx' -and
+ $_.DirectoryName -notmatch 'build'
+}
+foreach ($filePath in $filePaths) {
+ & "clang-format.exe" -style=file -i $filePath.FullName
+}
\ No newline at end of file
diff --git a/example/harmony/fs/.gitignore b/example/harmony/fs/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5
--- /dev/null
+++ b/example/harmony/fs/.gitignore
@@ -0,0 +1,6 @@
+/node_modules
+/oh_modules
+/.preview
+/build
+/.cxx
+/.test
\ No newline at end of file
diff --git a/example/harmony/fs/build-profile.json5 b/example/harmony/fs/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..9c5ce9bd0b25854f15872fb125f89096a34a21e3
--- /dev/null
+++ b/example/harmony/fs/build-profile.json5
@@ -0,0 +1,12 @@
+{
+ "apiType": "stageMode",
+
+ "targets": [
+ {
+ "name": "default"
+ },
+ {
+ "name": "ohosTest",
+ }
+ ]
+}
\ No newline at end of file
diff --git a/example/harmony/fs/hvigorfile.ts b/example/harmony/fs/hvigorfile.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c4045ef241b4759680eeba205dcf9c1c839c2c51
--- /dev/null
+++ b/example/harmony/fs/hvigorfile.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
+ * Use of this source code is governed by a MIT license that can be
+ * found in the LICENSE file.
+ */
+export { harTasks } from '@ohos/hvigor-ohos-plugin';
\ No newline at end of file
diff --git a/example/harmony/fs/index.ets b/example/harmony/fs/index.ets
new file mode 100644
index 0000000000000000000000000000000000000000..d6579ae7ad61ee8d78fdcc747adb18d441c1bbd5
--- /dev/null
+++ b/example/harmony/fs/index.ets
@@ -0,0 +1,7 @@
+/*
+ * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
+ * Use of this source code is governed by a MIT license that can be
+ * found in the LICENSE file.
+ */
+
+export * from './ts'
\ No newline at end of file
diff --git a/example/harmony/fs/oh-package.json5 b/example/harmony/fs/oh-package.json5
new file mode 100644
index 0000000000000000000000000000000000000000..7611d7c4e2745db53ad2937022d3d351b5bae5d9
--- /dev/null
+++ b/example/harmony/fs/oh-package.json5
@@ -0,0 +1,10 @@
+{
+ "name": "@react-native-ohos/react-native-fs",
+ "version": "2.20.0",
+ "description": "Please describe the basic information.",
+ "main": "index.ets",
+ "types": "module",
+ "dependencies": {
+ "@rnoh/react-native-openharmony": "0.72.38"
+ }
+}
\ No newline at end of file
diff --git a/example/harmony/fs/src/main/cpp/CMakeLists.txt b/example/harmony/fs/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..607ba12b098ac8a6f030238184fcde72b4f1b226
--- /dev/null
+++ b/example/harmony/fs/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 3.13)
+set(CMAKE_VERBOSE_MAKEFILE on)
+set(rnoh_fs_generated_dir "${CMAKE_CURRENT_SOURCE_DIR}/generated")
+
+file(GLOB rnoh_fs_SRC CONFIGURE_DEPENDS *.cpp)
+file(GLOB_RECURSE rnoh_fs_generated_SRC "${rnoh_fs_generated_dir}/**/*.cpp")
+
+add_library(rnoh_fs SHARED ${rnoh_fs_SRC} ${rnoh_fs_generated_SRC})
+target_include_directories(rnoh_fs PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${rnoh_fs_generated_dir}
+)
+
+target_link_libraries(rnoh_fs PUBLIC rnoh)
\ No newline at end of file
diff --git a/example/harmony/fs/src/main/cpp/RNFSPackage.h b/example/harmony/fs/src/main/cpp/RNFSPackage.h
new file mode 100644
index 0000000000000000000000000000000000000000..dab1595c9b6dd269426ffb4e313cebd896113024
--- /dev/null
+++ b/example/harmony/fs/src/main/cpp/RNFSPackage.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
+ * Use of this source code is governed by a MIT license that can be
+ * found in the LICENSE file.
+ */
+#pragma once
+
+#include "RNOH/generated/BaseReactNativeFsPackage.h"
+
+namespace rnoh {
+
+class RNFSPackage : public BaseReactNativeFsPackage {
+ using Super = BaseReactNativeFsPackage;
+
+public:
+ RNFSPackage(Package::Context ctx) : Super(ctx) {}
+};
+} // namespace rnoh
\ No newline at end of file
diff --git a/example/harmony/fs/src/main/cpp/generated/RNOH/generated/BaseReactNativeFsPackage.h b/example/harmony/fs/src/main/cpp/generated/RNOH/generated/BaseReactNativeFsPackage.h
new file mode 100644
index 0000000000000000000000000000000000000000..56c68b9e96abf22d9937a2adf90f7456e7b5dc1a
--- /dev/null
+++ b/example/harmony/fs/src/main/cpp/generated/RNOH/generated/BaseReactNativeFsPackage.h
@@ -0,0 +1,65 @@
+/**
+ * This code was generated by "react-native codegen-lib-harmony"
+ */
+
+#pragma once
+
+#include "RNOH/Package.h"
+#include "RNOH/ArkTSTurboModule.h"
+#include "RNOH/generated/turbo_modules/ReactNativeFs.h"
+
+namespace rnoh {
+
+class BaseReactNativeFsPackageTurboModuleFactoryDelegate : public TurboModuleFactoryDelegate {
+ public:
+ SharedTurboModule createTurboModule(Context ctx, const std::string &name) const override {
+ if (name == "ReactNativeFs") {
+ return std::make_shared(ctx, name);
+ }
+ return nullptr;
+ };
+};
+
+class BaseReactNativeFsPackageEventEmitRequestHandler : public EventEmitRequestHandler {
+ public:
+ void handleEvent(Context const &ctx) override {
+ auto eventEmitter = ctx.shadowViewRegistry->getEventEmitter(ctx.tag);
+ if (eventEmitter == nullptr) {
+ return;
+ }
+
+ std::vector supportedEventNames = {
+ };
+ if (std::find(supportedEventNames.begin(), supportedEventNames.end(), ctx.eventName) != supportedEventNames.end()) {
+ eventEmitter->dispatchEvent(ctx.eventName, ArkJS(ctx.env).getDynamic(ctx.payload));
+ }
+ }
+};
+
+
+class BaseReactNativeFsPackage : public Package {
+ public:
+ BaseReactNativeFsPackage(Package::Context ctx) : Package(ctx){};
+
+ std::unique_ptr createTurboModuleFactoryDelegate() override {
+ return std::make_unique();
+ }
+
+ std::vector createComponentDescriptorProviders() override {
+ return {
+ };
+ }
+
+ ComponentJSIBinderByString createComponentJSIBinderByName() override {
+ return {
+ };
+ };
+
+ EventEmitRequestHandlers createEventEmitRequestHandlers() override {
+ return {
+ std::make_shared(),
+ };
+ }
+};
+
+} // namespace rnoh
diff --git a/example/harmony/fs/src/main/cpp/generated/RNOH/generated/turbo_modules/ReactNativeFs.cpp b/example/harmony/fs/src/main/cpp/generated/RNOH/generated/turbo_modules/ReactNativeFs.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..84c80e0fa115eb3946fbed2a757a642c3eb2173b
--- /dev/null
+++ b/example/harmony/fs/src/main/cpp/generated/RNOH/generated/turbo_modules/ReactNativeFs.cpp
@@ -0,0 +1,35 @@
+/**
+ * This code was generated by "react-native codegen-lib-harmony"
+ */
+
+#include "ReactNativeFs.h"
+
+namespace rnoh {
+using namespace facebook;
+
+ReactNativeFs::ReactNativeFs(const ArkTSTurboModule::Context ctx, const std::string name) : ArkTSTurboModule(ctx, name) {
+ methodMap_ = {
+ ARK_METHOD_METADATA(getConstants, 0),
+ ARK_ASYNC_METHOD_METADATA(readFile, 1),
+ ARK_ASYNC_METHOD_METADATA(exists, 1),
+ ARK_ASYNC_METHOD_METADATA(mkdir, 2),
+ ARK_ASYNC_METHOD_METADATA(appendFile, 3),
+ ARK_ASYNC_METHOD_METADATA(writeFile, 3),
+ ARK_ASYNC_METHOD_METADATA(readFileAssets, 1),
+ ARK_ASYNC_METHOD_METADATA(copyFile, 2),
+ ARK_ASYNC_METHOD_METADATA(unlink, 1),
+ ARK_ASYNC_METHOD_METADATA(hash, 2),
+ ARK_ASYNC_METHOD_METADATA(moveFile, 2),
+ ARK_ASYNC_METHOD_METADATA(read, 3),
+ ARK_ASYNC_METHOD_METADATA(write, 3),
+ ARK_ASYNC_METHOD_METADATA(stat, 1),
+ ARK_ASYNC_METHOD_METADATA(touch, 3),
+ ARK_ASYNC_METHOD_METADATA(downloadFile, 1),
+ ARK_ASYNC_METHOD_METADATA(readDir, 1),
+ ARK_ASYNC_METHOD_METADATA(existsAssets, 1),
+ ARK_METHOD_METADATA(addListener, 1),
+ ARK_METHOD_METADATA(removeListeners, 1),
+ };
+}
+
+} // namespace rnoh
diff --git a/example/harmony/fs/src/main/cpp/generated/RNOH/generated/turbo_modules/ReactNativeFs.h b/example/harmony/fs/src/main/cpp/generated/RNOH/generated/turbo_modules/ReactNativeFs.h
new file mode 100644
index 0000000000000000000000000000000000000000..fd1b48ab460a0d481c0fe3102e03f35654f05aae
--- /dev/null
+++ b/example/harmony/fs/src/main/cpp/generated/RNOH/generated/turbo_modules/ReactNativeFs.h
@@ -0,0 +1,16 @@
+/**
+ * This code was generated by "react-native codegen-lib-harmony"
+ */
+
+#pragma once
+
+#include "RNOH/ArkTSTurboModule.h"
+
+namespace rnoh {
+
+class JSI_EXPORT ReactNativeFs : public ArkTSTurboModule {
+ public:
+ ReactNativeFs(const ArkTSTurboModule::Context ctx, const std::string name);
+};
+
+} // namespace rnoh
diff --git a/example/harmony/fs/src/main/ets/FsPackage.ts b/example/harmony/fs/src/main/ets/FsPackage.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a58cb55d178a8c7e2776bb2c5c17a735882ca6d9
--- /dev/null
+++ b/example/harmony/fs/src/main/ets/FsPackage.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
+ * Use of this source code is governed by a MIT license that can be
+ * found in the LICENSE file.
+ */
+
+import { RNPackage, TurboModulesFactory } from '@rnoh/react-native-openharmony/ts';
+import type { TurboModule, TurboModuleContext } from '@rnoh/react-native-openharmony/ts';
+import { FsTurboModule } from './FsTurboModule';
+
+class FsTurboModulesFactory extends TurboModulesFactory {
+ createTurboModule(name: string): TurboModule | null {
+ if (name === 'ReactNativeFs') {
+ // 上下文对象
+ globalThis.uiAbilityContext = this.ctx.uiAbilityContext;
+ return new FsTurboModule(this.ctx);
+ }
+ return null;
+ }
+
+ hasTurboModule(name: string): boolean {
+ return name === 'ReactNativeFs';
+ }
+}
+
+export class FsPackage extends RNPackage {
+ createTurboModulesFactory(ctx: TurboModuleContext): TurboModulesFactory {
+ return new FsTurboModulesFactory(ctx);
+ }
+}
diff --git a/example/harmony/fs/src/main/ets/FsTurboModule.ts b/example/harmony/fs/src/main/ets/FsTurboModule.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d29d7cba51047087e168a8706674a86193dd701f
--- /dev/null
+++ b/example/harmony/fs/src/main/ets/FsTurboModule.ts
@@ -0,0 +1,571 @@
+/*
+ * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
+ * Use of this source code is governed by a MIT license that can be
+ * found in the LICENSE file.
+ */
+
+import { TurboModule, RNOHError, TurboModuleContext } from '@rnoh/react-native-openharmony/ts';
+import { TM } from "./generated/ts"
+import fs, { ListFileOptions, ReadOptions, ReadTextOptions, WriteOptions } from '@ohos.file.fs';
+import hash from '@ohos.file.hash';
+import { BusinessError } from '@ohos.base';
+import resourceManager from '@ohos.resourceManager'
+import util from '@ohos.util';
+import loadRequest from '@ohos.request';
+import buffer from '@ohos.buffer';
+import HashMap from '@ohos.util.HashMap';
+import { Context } from '@ohos.abilityAccessCtrl';
+import common from '@ohos.app.ability.common';
+
+const TAG: string = "[RNOH] Fs"
+
+interface StatResult {
+ ctime: number, // The creation date of the file
+ mtime: number, // The last modified date of the file
+ size: number, // Size in bytes
+ mode: number, // UNIX file mode
+ originalFilepath: string, // ANDROID: In case of content uri this is the pointed file path, otherwise is the same as path
+ type: number // Is the file just a file? Is the file a directory?
+}
+
+type Headers = { [name: string]: string }
+type Fields = { [name: string]: string }
+
+type DownloadFileOptions = {
+ jobId: number
+ fromUrl: string // URL to download file from
+ toFile: string // Local filesystem path to save the file to
+ headers?: Headers // An object of headers to be passed to the server
+ background?: boolean // Continue the download in the background after the app terminates (iOS only)
+ progressInterval?: number
+ progressDivider?: number
+ readTimeout?: number
+ hasBeginCallback?: (res: DownloadBeginCallbackResult) => void
+ hasProgressCallback?: (res: DownloadProgressCallbackResult) => void
+ hasResumableCallback?: () => void // only supported on iOS yet
+ connectionTimeout?: number // only supported on Android yet
+ backgroundTimeout?: number // Maximum time (in milliseconds) to download an entire resource (iOS only, useful for timing out background downloads)
+}
+
+type DownloadBeginCallbackResult = {
+ jobId: number // The download job ID, required if one wishes to cancel the download. See `stopDownload`.
+ statusCode: number // The HTTP status code
+ contentLength: number // The total size in bytes of the download resource
+ headers: Headers // The HTTP response headers from the server
+}
+
+type DownloadProgressCallbackResult = {
+ jobId: number // The download job ID, required if one wishes to cancel the download. See `stopDownload`.
+ contentLength: number // The total size in bytes of the download resource
+ bytesWritten: number // The number of bytes written to the file so far
+}
+
+type DownloadResult = {
+ jobId: number // The download job ID, required if one wishes to cancel the download. See `stopDownload`.
+ statusCode: number // The HTTP status code
+ bytesWritten: number // The number of bytes written to the file
+}
+
+type ReadDirItem = {
+ ctime?: number;
+ mtime?: number;
+ name: string;
+ path: string;
+ size: number;
+ type: number;
+};
+
+enum LOADTASK_STATUS {
+ PROGRESS = 'progress',
+ COMPLETE = 'complete',
+ PAUSE = 'pause',
+ REMOVE = 'remove',
+ FAIL = 'fail'
+}
+
+export class FsTurboModule extends TurboModule implements TM.ReactNativeFs.Spec {
+ private context: Context; // ApplicationContext
+ private resourceManager: resourceManager.ResourceManager;
+ private FOUR_ZERO_NINE_SIX: number = 4096
+ private ZERO: number = 0
+ private ONE: number = 1
+ private ONE_Three_Nine_ZERO_ZERO_ZERO_ONE_FIVE: number = 13900015
+ private UTF8: buffer.BufferEncoding = 'utf8'
+ private MD5: string = 'md5'
+ private SHA1: string = 'sha1'
+ private SHA256: string = 'sha256'
+ private UTF_8: buffer.BufferEncoding = 'utf-8'
+ private BASE64: buffer.BufferEncoding = 'base64'
+ private logger = this.ctx.logger.clone("ReactNativeFsLogger")
+
+
+ constructor(ctx: TurboModuleContext) {
+ super(ctx)
+ this.context = this.ctx.uiAbilityContext;
+ this.resourceManager = this.context.resourceManager;
+ }
+
+ existsAssets(filepath: string): Promise {
+ this.logger.info('existsAssets')
+ return new Promise((resolve, reject) => {
+ try {
+ this.resourceManager.getRawFileList(filepath, (error: BusinessError, value: Array) => {
+ if (error != null) {
+ resolve(false);
+ } else {
+ resolve(true);
+ }
+ });
+ } catch (error) {
+ this.logger.error(`existsAssets error: ${error?.message}`)
+ resolve(false);
+ }
+ });
+ }
+
+ readDir(dirpath: string): Promise