@react-native-oh/react-native-harmony 0.61.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Libraries/ART/ReactNativeART.js +43 -0
- package/Libraries/Animated/Animated.js +154 -0
- package/Libraries/Animated/delegates/createAnimatedComponentDelegate.js +20 -0
- package/Libraries/Animated/src/Easing.js +20 -0
- package/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js +15 -0
- package/Libraries/Components/AppleTV/TVEventHandler.js +13 -0
- package/Libraries/Components/CheckBox/CheckBox.tsx +182 -0
- package/Libraries/Components/DatePickerAndroid/DatePickerAndroid.tsx +32 -0
- package/Libraries/Components/DatePickerIOS/DatePickerIOS.tsx +285 -0
- package/Libraries/Components/MaskedView/MaskedView.tsx +32 -0
- package/Libraries/Components/Picker/Picker.tsx +303 -0
- package/Libraries/Components/PickerIOS/PickerIOS.tsx +175 -0
- package/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.tsx +156 -0
- package/Libraries/Components/ProgressViewIOS/ProgressViewIOS.tsx +65 -0
- package/Libraries/Components/PushNotificationIOS/PushNotificationIOS.tsx +609 -0
- package/Libraries/Components/RefreshControl/RefreshControl.tsx +54 -0
- package/Libraries/Components/ScrollView/ScrollView.js +34 -0
- package/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.tsx +79 -0
- package/Libraries/Components/Slider/Slider.tsx +204 -0
- package/Libraries/Components/TextInput/TextInput.js +24 -0
- package/Libraries/Components/TimePickerAndroid/TimePickerAndroid.tsx +20 -0
- package/Libraries/Components/Touchable/TouchableOpacity.js +90 -0
- package/Libraries/Components/View/ViewPropTypes.js +16 -0
- package/Libraries/Components/View/delegate/ViewDelegate.js +49 -0
- package/Libraries/Core/ReactNativeVersion.js +21 -0
- package/Libraries/Core/polyfillPromise.js +18 -0
- package/Libraries/Core/setUpPlatform.js +13 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedColorPropType.js +16 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedEdgeInsetsPropType.js +16 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedImagePropType.js +15 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedImageSourcePropType.js +16 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedImageStylePropTypes.js +15 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedLayoutPropTypes.js +15 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedPointPropType.js +16 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedShadowPropTypesIOS.js +15 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedStyleSheetPropType.js +16 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedTVViewPropTypes.js +15 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedTextInputPropTypes.js +15 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedTextPropTypes.js +16 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedTextStylePropTypes.js +15 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedTransformPropTypes.js +15 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedViewAccessibility.js +15 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedViewPropTypes.js +16 -0
- package/Libraries/DeprecatedPropTypes/DeprecatedViewStylePropTypes.js +15 -0
- package/Libraries/DeprecatedPropTypes/deprecatedCreateStrictShapeTypeChecker.js +20 -0
- package/Libraries/EventEmitter/NativeEventEmitter.js +50 -0
- package/Libraries/Image/Image.tsx +88 -0
- package/Libraries/Promise.js +50 -0
- package/Libraries/Storage/AsyncStorage.ts +216 -0
- package/Libraries/Storage/helpers.ts +36 -0
- package/Libraries/Storage/hooks.ts +19 -0
- package/Libraries/Storage/index.ts +5 -0
- package/Libraries/Storage/types.ts +43 -0
- package/Libraries/StyleSheet/EdgeInsetsPropType.js +15 -0
- package/Libraries/StyleSheet/PointPropType.js +15 -0
- package/Libraries/Utilities/Platform.harmony.ts +68 -0
- package/Libraries/Utilities/setAndForwardRef.js +71 -0
- package/README.dev.md +55 -0
- package/README.md +146 -0
- package/compat/ensurePropTypes.js +45 -0
- package/compat/getNoopPropType.js +42 -0
- package/compat/restoreRemoveListener.js +117 -0
- package/index.js +348 -0
- package/package.json +45 -0
- package/patched-virtualized-list/VirtualizedList.js +87 -0
- package/polyfills/dateSlash.js +89 -0
- package/react_native_harmony.har +0 -0
- package/src/private/specs/AsyncStorage.ts +37 -0
- package/src/private/specs/NativeDatePickerAndroid.ts +27 -0
- package/src/private/specs/NativeTimePickerAndroid.ts +28 -0
- package/src/private/specs/PushNotificationIOS.ts +167 -0
- package/src/private/specs/components/CheckBoxNativeComponent.ts +35 -0
- package/src/private/specs/components/DatePickerNativeComponent.ts +40 -0
- package/src/private/specs/components/MaskedViewNativeComponent.ts +17 -0
- package/src/private/specs/components/PickerIOSNativeComponent.ts +53 -0
- package/src/private/specs/components/PickerNativeComponent.ts +67 -0
- package/src/private/specs/components/ProgressBarNativeComponent.ts +24 -0
- package/src/private/specs/components/ProgressViewNativeComponent.ts +24 -0
- package/src/private/specs/components/ProgressWheelNativeComponent.ts +18 -0
- package/src/private/specs/components/RNSliderNativeComponent.ts +44 -0
- package/src/private/specs/components/SegmentedControlNativeComponent.ts +38 -0
- package/src/private/specs/components/TimePickerNativeComponent.ts +40 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Huawei Technologies Co., Ltd.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Deep-import alias for RN 0.61 compatibility
|
|
11
|
+
* Returns empty object as a no-op replacement for legacy PropTypes maps
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
module.exports = {};
|
|
15
|
+
module.exports.default = module.exports;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Huawei Technologies Co., Ltd.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Deep-import alias for RN 0.61 compatibility
|
|
11
|
+
* Returns empty object as a no-op replacement for legacy PropTypes maps
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
module.exports = {};
|
|
15
|
+
module.exports.default = module.exports;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2024 Huawei Technologies Co., Ltd.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { NativePlatformConstantsHarmony } from '@react-native-oh/react-native-harmony/Libraries/Utilities/NativePlatformConstantsHarmony.harmony';
|
|
9
|
+
import type {
|
|
10
|
+
PlatformHarmonyStatic,
|
|
11
|
+
PlatformHarmonyConstants,
|
|
12
|
+
PlatformOSType,
|
|
13
|
+
} from '@react-native-oh/react-native-harmony/Libraries/Utilities/Platform';
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
_constants: undefined,
|
|
17
|
+
OS: 'harmony',
|
|
18
|
+
|
|
19
|
+
get constants() {
|
|
20
|
+
if (this._constants == null) {
|
|
21
|
+
const customOverrides = {
|
|
22
|
+
reactNativeVersion: {"major": 0, "minor": 61, "patch": 5}
|
|
23
|
+
};
|
|
24
|
+
this._constants = new Proxy(NativePlatformConstantsHarmony.getConstants(), {
|
|
25
|
+
get(target, property) {
|
|
26
|
+
if (property in customOverrides) {
|
|
27
|
+
return customOverrides[property];
|
|
28
|
+
}
|
|
29
|
+
return target[property];
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return this._constants!;
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
get Version() {
|
|
37
|
+
return this.constants.osFullName;
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
get isPad() {
|
|
41
|
+
return this.constants.deviceType === 'tablet';
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
get isTV() {
|
|
45
|
+
return this.constants.deviceType === 'tv';
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
get isTesting() {
|
|
49
|
+
if (__DEV__) {
|
|
50
|
+
return this.constants.isTesting;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
select<T>(
|
|
56
|
+
spec:
|
|
57
|
+
| ({ [platform in PlatformOSType]?: T } & { default: T })
|
|
58
|
+
| { [platform in PlatformOSType]: T }
|
|
59
|
+
) {
|
|
60
|
+
return 'harmony' in spec
|
|
61
|
+
? spec.harmony
|
|
62
|
+
: 'native' in spec
|
|
63
|
+
? spec.native
|
|
64
|
+
: spec.default;
|
|
65
|
+
},
|
|
66
|
+
} satisfies PlatformHarmonyStatic & {
|
|
67
|
+
_constants: undefined | PlatformHarmonyConstants;
|
|
68
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @format
|
|
8
|
+
* @flow
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
import type {ElementRef, Ref} from 'react';
|
|
14
|
+
|
|
15
|
+
type Args = $ReadOnly<{|
|
|
16
|
+
getForwardedRef: () => ?Ref<any>,
|
|
17
|
+
setLocalRef: (ref: ElementRef<any>) => mixed,
|
|
18
|
+
|}>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* This is a helper function for when a component needs to be able to forward a ref
|
|
22
|
+
* to a child component, but still needs to have access to that component as part of
|
|
23
|
+
* its implementation.
|
|
24
|
+
*
|
|
25
|
+
* Its main use case is in wrappers for native components.
|
|
26
|
+
*
|
|
27
|
+
* Usage:
|
|
28
|
+
*
|
|
29
|
+
* class MyView extends React.Component {
|
|
30
|
+
* _nativeRef = null;
|
|
31
|
+
*
|
|
32
|
+
* _setNativeRef = setAndForwardRef({
|
|
33
|
+
* getForwardedRef: () => this.props.forwardedRef,
|
|
34
|
+
* setLocalRef: ref => {
|
|
35
|
+
* this._nativeRef = ref;
|
|
36
|
+
* },
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* render() {
|
|
40
|
+
* return <View ref={this._setNativeRef} />;
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
*
|
|
44
|
+
* const MyViewWithRef = React.forwardRef((props, ref) => (
|
|
45
|
+
* <MyView {...props} forwardedRef={ref} />
|
|
46
|
+
* ));
|
|
47
|
+
*
|
|
48
|
+
* module.exports = MyViewWithRef;
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
function setAndForwardRef({
|
|
52
|
+
getForwardedRef,
|
|
53
|
+
setLocalRef,
|
|
54
|
+
}: Args): (ref: ElementRef<any>) => void {
|
|
55
|
+
return function forwardRef(ref: ElementRef<any>) {
|
|
56
|
+
const forwardedRef = getForwardedRef();
|
|
57
|
+
|
|
58
|
+
setLocalRef(ref);
|
|
59
|
+
|
|
60
|
+
// Forward to user ref prop (if one has been specified)
|
|
61
|
+
if (typeof forwardedRef === 'function') {
|
|
62
|
+
// Handle function-based refs. String-based refs are handled as functions.
|
|
63
|
+
forwardedRef(ref);
|
|
64
|
+
} else if (typeof forwardedRef === 'object' && forwardedRef != null) {
|
|
65
|
+
// Handle createRef-based refs
|
|
66
|
+
forwardedRef.current = ref;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = setAndForwardRef;
|
package/README.dev.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
This document is a concise guide for developers contributing to @react-native-oh/react-native-harmony-61-interop.
|
|
2
|
+
|
|
3
|
+
### Create a component wrapper
|
|
4
|
+
1) Mirror the path of the target component from `react-native-harmony`:
|
|
5
|
+
```
|
|
6
|
+
packages/react-native-harmony-61-interop/Libraries/Components/View/View.js
|
|
7
|
+
```
|
|
8
|
+
2) Implement the wrapper and forward refs/props to the underlying `react-native-harmony` component:
|
|
9
|
+
```js
|
|
10
|
+
/**
|
|
11
|
+
* [INTEROP-WRAPPER]
|
|
12
|
+
* Wraps Harmony View to adapt RN 0.61 semantics.
|
|
13
|
+
*/
|
|
14
|
+
import React from 'react';
|
|
15
|
+
import RNOHView from '@react-native-oh/react-native-harmony/Libraries/Components/View/View';
|
|
16
|
+
|
|
17
|
+
const View = React.forwardRef(function ViewWrapper({ newProp, style, ...props }, ref) {
|
|
18
|
+
const patchedStyle = newProp === 'active'
|
|
19
|
+
? [{ borderColor: 'red', borderWidth: 2, backgroundColor: 'gray' }, style]
|
|
20
|
+
: style;
|
|
21
|
+
return <RNOHView ref={ref} {...props} style={patchedStyle} />;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export default View;
|
|
25
|
+
```
|
|
26
|
+
3. Update `packages/react-native-harmony-61-interop/index.js` and provide a path to the wrapper component:
|
|
27
|
+
```js
|
|
28
|
+
get View() {
|
|
29
|
+
return require('./Libraries/Components/View/View').default;
|
|
30
|
+
},
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Patch an internal JS module
|
|
34
|
+
1) Mirror the original relative path:
|
|
35
|
+
```
|
|
36
|
+
packages/react-native-harmony-61-interop/Libraries/Image/ImageSourceUtils.js
|
|
37
|
+
```
|
|
38
|
+
2) Import from `react-native-harmony` and re-export with your modifications:
|
|
39
|
+
```js
|
|
40
|
+
/**
|
|
41
|
+
* [INTEROP-PATCH]
|
|
42
|
+
* Patches `getImageSourcesFromImageProps` for RN 0.61 compatibility/logging.
|
|
43
|
+
*/
|
|
44
|
+
const RNOHImageSourceUtils = require('@react-native-oh/react-native-harmony/Libraries/Image/ImageSourceUtils');
|
|
45
|
+
|
|
46
|
+
export function getImageSourcesFromImageProps(imageProps) {
|
|
47
|
+
console.log('[INTEROP-PATCH] getImageSourcesFromImageProps');
|
|
48
|
+
return RNOHImageSourceUtils.getImageSourcesFromImageProps(imageProps);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Notes:
|
|
53
|
+
- Do not import from this interop package inside interop files; import only from `@react-native-oh/react-native-harmony/...` to avoid cycles.
|
|
54
|
+
- Mirror the file layout of `@react-native-oh/react-native-harmony` under this package.
|
|
55
|
+
- Wrappers import from `@react-native-oh/react-native-harmony` and re-export a compatible API for RN 0.61'.
|
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# @react-native-oh/react-native-harmony-61-interop
|
|
2
|
+
|
|
3
|
+
This package is designed for React Native application developers maintaining a project that still uses React Native 0.61 or a similar version. @react-native-oh/react-native-harmony-61-interop restores the React Native 0.61 APIs on top of react-native-harmony (0.77).
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
This guide assumes the reader has prepared a React Native OpenHarmony (RNOH) development environment and has run their first RNOH app.
|
|
8
|
+
|
|
9
|
+
The reader is also expected to identify external libraries used by their RN 61 project that support RNOH 0.77 and provide a compatible API. A list of libraries compatible with RNOH is available here: [RNOH libraries overview](https://gitcode.com/OpenHarmony-RN/usage-docs).
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Project Structure
|
|
13
|
+
|
|
14
|
+
You need to create a new NPM project for the RNOH project, because the peer dependencies of RN@77 conflict with those of RN@0.61. This can be achieved in two ways.
|
|
15
|
+
|
|
16
|
+
### RNOH project next to the existing RN 61 project
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
.
|
|
20
|
+
├── legacy-rn61-awesome-project/
|
|
21
|
+
│ ├── android
|
|
22
|
+
│ ├── ios
|
|
23
|
+
│ ├── node_modules
|
|
24
|
+
│ ├── src
|
|
25
|
+
│ ├── index.js
|
|
26
|
+
│ └── package.json
|
|
27
|
+
└── RNOHAwesomeProject/
|
|
28
|
+
├── harmony
|
|
29
|
+
├── node_modules
|
|
30
|
+
├── index.js
|
|
31
|
+
└── package.json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### RNOH project inside the RN 61 project
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
.
|
|
38
|
+
└── legacy-rn61-awesome-project/
|
|
39
|
+
├── android
|
|
40
|
+
├── ios
|
|
41
|
+
├── node_modules
|
|
42
|
+
├── RNOHAwesomeProject/
|
|
43
|
+
│ ├── harmony
|
|
44
|
+
│ ├── node_modules
|
|
45
|
+
│ ├── index.js
|
|
46
|
+
│ └── package.json
|
|
47
|
+
├── src
|
|
48
|
+
├── index.js
|
|
49
|
+
└── package.json
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Getting Started
|
|
53
|
+
Assumption: The RNOH project will be installed next to an existing RN 61 project.
|
|
54
|
+
|
|
55
|
+
1. Open terminal and navigate to a directory where RNOHAwesomeProject should be created
|
|
56
|
+
1. Initialize the RNOH project by running the following snippet (see the RNOH documentation for how to set up your environment)
|
|
57
|
+
```
|
|
58
|
+
npx --yes @react-native-community/cli@latest init RNOHAwesomeProject --version 0.77.1 --skip-install &&
|
|
59
|
+
cd ./RNOHAwesomeProject &&
|
|
60
|
+
npm i @react-native-oh/react-native-harmony@0.77 @react-native-oh/react-native-harmony-cli@0.77 -D &&
|
|
61
|
+
npx react-native init-harmony --bundle-name com.rnoh.awesome_project
|
|
62
|
+
```
|
|
63
|
+
1. Inside "RNOHAwesomeProject" perform the following steps:
|
|
64
|
+
1. (Optional) Remove Android- and iOS-specific files from "RNOHAwesomeProject"
|
|
65
|
+
|
|
66
|
+
1. Install `@react-native-oh/react-native-harmony-61-interop`: <!-- Merge this step with step 2 before release -->
|
|
67
|
+
```
|
|
68
|
+
npm i @react-native-oh/react-native-harmony-61-interop@0.77
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
1. Modify `index.js` to use the code from the `legacy-rn61-awesome-project` project:
|
|
72
|
+
```js
|
|
73
|
+
import '../legacy-rn61-awesome-project/index' // <= UPDATE ME
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
1. Replace `metro.config.js` with the content below (update the directory name for your project) to use assets from `legacy-rn61-awesome-project` and `node_modules` from the RNOH project:
|
|
77
|
+
```js
|
|
78
|
+
/**
|
|
79
|
+
* Copyright (c) 2025 Huawei Technologies Co., Ltd.
|
|
80
|
+
*
|
|
81
|
+
* This source code is licensed under the MIT license found in the
|
|
82
|
+
* LICENSE file in the root directory of this source tree.
|
|
83
|
+
*/
|
|
84
|
+
const { mergeConfig, getDefaultConfig } = require('@react-native/metro-config');
|
|
85
|
+
const {
|
|
86
|
+
createHarmonyMetroConfig,
|
|
87
|
+
} = require('@react-native-oh/react-native-harmony/metro.config');
|
|
88
|
+
const pathUtils = require('node:path');
|
|
89
|
+
|
|
90
|
+
const rnoh61ProjectRoot = __dirname;
|
|
91
|
+
|
|
92
|
+
module.exports = mergeConfig(
|
|
93
|
+
getDefaultConfig(__dirname),
|
|
94
|
+
createHarmonyMetroConfig({
|
|
95
|
+
reactNativeHarmonyPackageName: '@react-native-oh/react-native-harmony',
|
|
96
|
+
}),
|
|
97
|
+
{
|
|
98
|
+
transformer: {
|
|
99
|
+
getTransformOptions: async () => ({
|
|
100
|
+
transform: {
|
|
101
|
+
experimentalImportSupport: false,
|
|
102
|
+
inlineRequires: true,
|
|
103
|
+
},
|
|
104
|
+
}),
|
|
105
|
+
},
|
|
106
|
+
watchFolders: [
|
|
107
|
+
/**
|
|
108
|
+
* RNOH_APP: By default, Metro doesn't see files above the project root.
|
|
109
|
+
* If you integrate the RNOH 61 Interop package, you may need to add this folder to your configuration.
|
|
110
|
+
*/
|
|
111
|
+
pathUtils.resolve(__dirname, '..', 'legacy-rn61-awesome-project'), // <= UPDATE ME
|
|
112
|
+
],
|
|
113
|
+
server: {
|
|
114
|
+
rewriteRequestUrl: (url) => {
|
|
115
|
+
/**
|
|
116
|
+
* RNOH_APP: This is needed to make images loaded from the Metro server work.
|
|
117
|
+
* Metro provides assets by listening on requests with the following format: `assets/<path-relative-to-project-root>`
|
|
118
|
+
* However, when assets are outside the project root, "assets/.." cancels out. Changing `projectRoot` in the Metro config also fixes this problem
|
|
119
|
+
* but introduces a different transpilation problem.
|
|
120
|
+
*/
|
|
121
|
+
let newUrl = url.replace(
|
|
122
|
+
'legacy-rn61-awesome-project', // <= UPDATE ME
|
|
123
|
+
'assets/../legacy-rn61-awesome-project' // <= UPDATE ME
|
|
124
|
+
);
|
|
125
|
+
return newUrl;
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
resolver: {
|
|
129
|
+
nodeModulesPaths: [
|
|
130
|
+
/**
|
|
131
|
+
* RNOH_APP: This configuration tells Metro where RNOH 0.77 dependencies can be found.
|
|
132
|
+
*/
|
|
133
|
+
pathUtils.resolve(rnoh61ProjectRoot, 'node_modules'),
|
|
134
|
+
],
|
|
135
|
+
/**
|
|
136
|
+
* RNOH_APP: The `node_modules` from the legacy project must not be used. Without this configuration,
|
|
137
|
+
* Metro will use two different React instances, likely resulting in an "Invalid hook call" error.
|
|
138
|
+
*/
|
|
139
|
+
blockList: [/legacy-rn61-awesome-project\/node_modules/], // <= UPDATE ME
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
```
|
|
144
|
+
1. Install and link external libraries used in the RN 61 project that support RNOH and provide a compatible API.
|
|
145
|
+
1. Run the OpenHarmony project.
|
|
146
|
+
1. Sync the project twice (During the first sync, autolinking modifies oh-package.json. During the second sync, new dependencies are installed).
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Huawei Technologies Co., Ltd.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Interop-61: minimal shim restoring RN 0.61 .propTypes
|
|
11
|
+
*
|
|
12
|
+
* Context:
|
|
13
|
+
* - RN 0.61 exposed `.propTypes` on components (e.g. Text.propTypes.style)
|
|
14
|
+
* - RN 0.72+ removed `.propTypes` so accessing them can return undefined
|
|
15
|
+
* or crash on Harmony 0.77+
|
|
16
|
+
*
|
|
17
|
+
* Strategy:
|
|
18
|
+
* - Attach stable no‑op .propTypes to components on demand, preserving referential equality
|
|
19
|
+
* for shared keys (e.g. Text.propTypes.style === ViewPropTypes.style) so legacy checks pass
|
|
20
|
+
* - Keep the shim idempotent (multiple calls to ensurePropTypes do not overwrite)
|
|
21
|
+
* - Don't re-implement original runtime validation, the goal is to only satisfy production builds
|
|
22
|
+
* and ensure no crashes when accessing .propTypes
|
|
23
|
+
* - Attach only 'style' propTypes for now, if other fields are needed, we can add them later
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const getNoopPropType = require('./getNoopPropType');
|
|
27
|
+
|
|
28
|
+
function ensurePropTypes(target) {
|
|
29
|
+
if (!target) {
|
|
30
|
+
return target;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const base = target.propTypes || {};
|
|
34
|
+
if (!Object.prototype.hasOwnProperty.call(base, 'style')) {
|
|
35
|
+
const next = {...base, style: getNoopPropType('style')};
|
|
36
|
+
try {
|
|
37
|
+
target.propTypes = next;
|
|
38
|
+
} catch {
|
|
39
|
+
// Ignore if propTypes is read-only / not assignable
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return target;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
exports.ensurePropTypes = ensurePropTypes;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Huawei Technologies Co., Ltd.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Interop-61: helper for creating no-op PropType functions
|
|
11
|
+
*
|
|
12
|
+
* Returns a singleton no-op PropType function with `.isRequired`,
|
|
13
|
+
* mimicking the shape of legacy PropTypes.
|
|
14
|
+
*
|
|
15
|
+
* Strategy:
|
|
16
|
+
* - Don't re-implement original dev-time PropTypes validation, the goal is to only satisfy
|
|
17
|
+
* production builds and ensure no crashes when importing legacy PropTypes.
|
|
18
|
+
* - Each distinct `name` gets its own unique instance, so that
|
|
19
|
+
* `ColorPropType !== EdgeInsetsPropType` (same behavior as RN 0.61).
|
|
20
|
+
* - Within the same `name`, all imports (top-level getter and deep-import) share
|
|
21
|
+
* the exact same reference. This preserves referential equality checks such as:
|
|
22
|
+
* require('react-native').ColorPropType
|
|
23
|
+
* === require('react-native/Libraries/DeprecatedPropTypes/DeprecatedColorPropType')
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
function createNoopPropType() {
|
|
27
|
+
const fn = () => null;
|
|
28
|
+
fn.isRequired = fn;
|
|
29
|
+
return fn;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const noopPropTypeByName = new Map();
|
|
33
|
+
|
|
34
|
+
function getNoopPropType(name) {
|
|
35
|
+
if (!noopPropTypeByName.has(name)) {
|
|
36
|
+
noopPropTypeByName.set(name, createNoopPropType());
|
|
37
|
+
}
|
|
38
|
+
return noopPropTypeByName.get(name);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = getNoopPropType;
|
|
42
|
+
module.exports.default = module.exports;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Huawei Technologies Co., Ltd.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Polyfill helper that restores legacy listener-removal APIs that were removed
|
|
10
|
+
* in React Native >= 0.65. Older React Native 0.61 code expects methods like
|
|
11
|
+
* `Module.removeEventListener(event, handler)` or
|
|
12
|
+
* `Module.removeListener(event, handler)`.
|
|
13
|
+
*
|
|
14
|
+
* In modern React Native versions listeners should be removed by calling `remove()`
|
|
15
|
+
* on the subscription returned from `addEventListener` / `addListener`.
|
|
16
|
+
* This helper bridges the gap by:
|
|
17
|
+
* 1. Monkey-patching the original `add*` method so that it remembers the
|
|
18
|
+
* subscription object returned for every `(eventType, listener)` pair.
|
|
19
|
+
* 2. Defining the legacy `remove*` method that looks up that subscription and
|
|
20
|
+
* calls `remove()` on it.
|
|
21
|
+
*
|
|
22
|
+
* The patch is idempotent – calling it multiple times for the same module is
|
|
23
|
+
* safe.
|
|
24
|
+
*
|
|
25
|
+
* @param {object} target The module object (e.g. AccessibilityInfo)
|
|
26
|
+
* @param {string} addName Existing method name for adding listeners
|
|
27
|
+
* ("addEventListener" | "addListener")
|
|
28
|
+
* @param {string} removeName Legacy method name to recreate
|
|
29
|
+
*/
|
|
30
|
+
function restoreRemoveListener(target, addName, removeName) {
|
|
31
|
+
if (!target || typeof target[addName] !== 'function') {
|
|
32
|
+
console.warn(`[restoreRemoveListener] Cannot patch. '${addName}' is missing on target`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// If legacy method already exists – do nothing.
|
|
37
|
+
if (typeof target[removeName] === 'function') {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Map<eventType, Map<listener, Set<subscription>>> */
|
|
42
|
+
const subsByListenerByEventType = new Map();
|
|
43
|
+
|
|
44
|
+
// Patch add* so that we can later find the subscription.
|
|
45
|
+
const originalAdd = target[addName];
|
|
46
|
+
target[addName] = function patchedAdd(eventType, listener, ...rest) {
|
|
47
|
+
const subscription = originalAdd.call(this, eventType, listener, ...rest);
|
|
48
|
+
let subsByListener = subsByListenerByEventType.get(eventType);
|
|
49
|
+
if (!subsByListener) {
|
|
50
|
+
subsByListener = new Map();
|
|
51
|
+
subsByListenerByEventType.set(eventType, subsByListener);
|
|
52
|
+
}
|
|
53
|
+
let listenerSubs = subsByListener.get(listener);
|
|
54
|
+
if (!listenerSubs) {
|
|
55
|
+
listenerSubs = new Set();
|
|
56
|
+
subsByListener.set(listener, listenerSubs);
|
|
57
|
+
}
|
|
58
|
+
listenerSubs.add(subscription);
|
|
59
|
+
// Ensure cleanup also if consumer calls subscription.remove() directly.
|
|
60
|
+
if (subscription && typeof subscription.remove === 'function') {
|
|
61
|
+
const originalRemove = subscription.remove.bind(subscription);
|
|
62
|
+
subscription.remove = function patchedSubscriptionRemove(...args) {
|
|
63
|
+
try {
|
|
64
|
+
return originalRemove(...args);
|
|
65
|
+
} finally {
|
|
66
|
+
const subsByListener = subsByListenerByEventType.get(eventType);
|
|
67
|
+
if (subsByListener) {
|
|
68
|
+
const listenerSubs = subsByListener.get(listener);
|
|
69
|
+
if (listenerSubs) {
|
|
70
|
+
listenerSubs.delete(subscription);
|
|
71
|
+
if (listenerSubs.size === 0) {
|
|
72
|
+
subsByListener.delete(listener);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (subsByListener.size === 0) {
|
|
76
|
+
subsByListenerByEventType.delete(eventType);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return subscription;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Define legacy remove* API.
|
|
86
|
+
target[removeName] = function legacyRemove(eventType, listener) {
|
|
87
|
+
const subsByListener = subsByListenerByEventType.get(eventType);
|
|
88
|
+
const listenerSubs = subsByListener ? subsByListener.get(listener) : undefined;
|
|
89
|
+
if (listenerSubs && listenerSubs.size > 0) {
|
|
90
|
+
// Remove all subscriptions for this (eventType, listener)
|
|
91
|
+
for (const sub of Array.from(listenerSubs)) {
|
|
92
|
+
if (sub && typeof sub.remove === 'function') {
|
|
93
|
+
sub.remove();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
subsByListener.delete(listener);
|
|
97
|
+
if (subsByListener.size === 0) {
|
|
98
|
+
subsByListenerByEventType.delete(eventType);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Optional cleanup for removeAllListeners to avoid stale entries in our map
|
|
104
|
+
const hasRemoveAll = typeof target.removeAllListeners === 'function';
|
|
105
|
+
if (hasRemoveAll && !target.__rnohPatchedRemoveAll) {
|
|
106
|
+
const originalRemoveAll = target.removeAllListeners.bind(target);
|
|
107
|
+
target.removeAllListeners = function patchedRemoveAllListeners(eventType) {
|
|
108
|
+
const result = originalRemoveAll(eventType);
|
|
109
|
+
subsByListenerByEventType.delete(eventType);
|
|
110
|
+
return result;
|
|
111
|
+
};
|
|
112
|
+
// Mark as patched to keep idempotency
|
|
113
|
+
Object.defineProperty(target, '__rnohPatchedRemoveAll', {value: true});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = restoreRemoveListener;
|