@react-native-oh/react-native-harmony 0.72.13-lazy-2 → 0.72.17
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/Alert/Alert.harmony.js +1 -1
- package/Libraries/Components/AccessibilityInfo/AccessibilityInfo.harmony.js +426 -0
- package/Libraries/Components/ScrollView/ScrollView.harmony.js +6 -5
- package/Libraries/Core/setUpReactDevTools.harmony.js +93 -0
- package/Libraries/Image/AssetSourceResolver.harmony.ts +46 -0
- package/Libraries/NativeComponent/BaseViewConfig.harmony.js +12 -0
- package/Libraries/ReactNative/UIManager.harmony.js +210 -0
- package/Libraries/StyleSheet/NativePlatformColor.ts +8 -0
- package/Libraries/StyleSheet/PlatformColorValueTypes.harmony.ts +14 -0
- package/Libraries/Vibration/Vibration.harmony.js +88 -0
- package/index.js +19 -2
- package/jest.config.js +6 -0
- package/metro.config.js +114 -35
- package/package.json +15 -6
- package/{rnoh-4.0.0.201-14-vmall.har → rnoh-4.0.0.202-02-vmall.har} +0 -0
- package/tsconfig.json +1 -0
- package/types/index.d.ts +6 -6
- package/Libraries/Core/setUpErrorHandling.harmony.js +0 -35
- package/Libraries/NativeModules/specs/NativeDevSettings.harmony.js +0 -10
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and 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
|
+
* @flow
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// RNOH patch: imports
|
|
12
|
+
import type {RootTag} from 'react-native/Libraries/Types/RootTagTypes';
|
|
13
|
+
import type {Spec} from 'react-native/Libraries/ReactNative/NativeUIManager';
|
|
14
|
+
|
|
15
|
+
import {getFabricUIManager} from 'react-native/Libraries/ReactNative/FabricUIManager';
|
|
16
|
+
import nullthrows from 'nullthrows';
|
|
17
|
+
|
|
18
|
+
export interface UIManagerJSInterface extends Spec {
|
|
19
|
+
+getViewManagerConfig: (viewManagerName: string) => Object;
|
|
20
|
+
+hasViewManagerConfig: (viewManagerName: string) => boolean;
|
|
21
|
+
+createView: (
|
|
22
|
+
reactTag: ?number,
|
|
23
|
+
viewName: string,
|
|
24
|
+
rootTag: RootTag,
|
|
25
|
+
props: Object,
|
|
26
|
+
) => void;
|
|
27
|
+
+updateView: (reactTag: number, viewName: string, props: Object) => void;
|
|
28
|
+
+manageChildren: (
|
|
29
|
+
containerTag: ?number,
|
|
30
|
+
moveFromIndices: Array<number>,
|
|
31
|
+
moveToIndices: Array<number>,
|
|
32
|
+
addChildReactTags: Array<number>,
|
|
33
|
+
addAtIndices: Array<number>,
|
|
34
|
+
removeAtIndices: Array<number>,
|
|
35
|
+
) => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isFabricReactTag(reactTag: number): boolean {
|
|
39
|
+
// React reserves even numbers for Fabric.
|
|
40
|
+
return reactTag % 2 === 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// RNOH patch: always export BridgelessUIManager properties
|
|
44
|
+
const BridgelessUIManager: UIManagerJSInterface = require('react-native/Libraries/ReactNative/BridgelessUIManager')
|
|
45
|
+
const PaperUIManager: UIManagerJSInterface =
|
|
46
|
+
global.RN$Bridgeless === true
|
|
47
|
+
? {}
|
|
48
|
+
: require('react-native/Libraries/ReactNative/PaperUIManager');
|
|
49
|
+
|
|
50
|
+
// $FlowFixMe[cannot-spread-interface]
|
|
51
|
+
const UIManager = {
|
|
52
|
+
...BridgelessUIManager,
|
|
53
|
+
...PaperUIManager,
|
|
54
|
+
measure(
|
|
55
|
+
reactTag: number,
|
|
56
|
+
callback: (
|
|
57
|
+
left: number,
|
|
58
|
+
top: number,
|
|
59
|
+
width: number,
|
|
60
|
+
height: number,
|
|
61
|
+
pageX: number,
|
|
62
|
+
pageY: number,
|
|
63
|
+
) => void,
|
|
64
|
+
): void {
|
|
65
|
+
if (isFabricReactTag(reactTag)) {
|
|
66
|
+
const FabricUIManager = nullthrows(getFabricUIManager());
|
|
67
|
+
const shadowNode =
|
|
68
|
+
FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
|
|
69
|
+
if (shadowNode) {
|
|
70
|
+
FabricUIManager.measure(shadowNode, callback);
|
|
71
|
+
} else {
|
|
72
|
+
console.warn(`measure cannot find view with tag #${reactTag}`);
|
|
73
|
+
// $FlowFixMe[incompatible-call]
|
|
74
|
+
callback();
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
// Paper
|
|
78
|
+
UIManagerImpl.measure(reactTag, callback);
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
measureInWindow(
|
|
83
|
+
reactTag: number,
|
|
84
|
+
callback: (
|
|
85
|
+
left: number,
|
|
86
|
+
top: number,
|
|
87
|
+
width: number,
|
|
88
|
+
height: number,
|
|
89
|
+
) => void,
|
|
90
|
+
): void {
|
|
91
|
+
if (isFabricReactTag(reactTag)) {
|
|
92
|
+
const FabricUIManager = nullthrows(getFabricUIManager());
|
|
93
|
+
const shadowNode =
|
|
94
|
+
FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
|
|
95
|
+
if (shadowNode) {
|
|
96
|
+
FabricUIManager.measureInWindow(shadowNode, callback);
|
|
97
|
+
} else {
|
|
98
|
+
console.warn(`measure cannot find view with tag #${reactTag}`);
|
|
99
|
+
// $FlowFixMe[incompatible-call]
|
|
100
|
+
callback();
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
// Paper
|
|
104
|
+
UIManagerImpl.measureInWindow(reactTag, callback);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
measureLayout(
|
|
109
|
+
reactTag: number,
|
|
110
|
+
ancestorReactTag: number,
|
|
111
|
+
errorCallback: (error: Object) => void,
|
|
112
|
+
callback: (
|
|
113
|
+
left: number,
|
|
114
|
+
top: number,
|
|
115
|
+
width: number,
|
|
116
|
+
height: number,
|
|
117
|
+
) => void,
|
|
118
|
+
): void {
|
|
119
|
+
if (isFabricReactTag(reactTag)) {
|
|
120
|
+
const FabricUIManager = nullthrows(getFabricUIManager());
|
|
121
|
+
const shadowNode =
|
|
122
|
+
FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
|
|
123
|
+
const ancestorShadowNode =
|
|
124
|
+
FabricUIManager.findShadowNodeByTag_DEPRECATED(ancestorReactTag);
|
|
125
|
+
|
|
126
|
+
if (!shadowNode || !ancestorShadowNode) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
FabricUIManager.measureLayout(
|
|
131
|
+
shadowNode,
|
|
132
|
+
ancestorShadowNode,
|
|
133
|
+
errorCallback,
|
|
134
|
+
callback,
|
|
135
|
+
);
|
|
136
|
+
} else {
|
|
137
|
+
// Paper
|
|
138
|
+
UIManagerImpl.measureLayout(
|
|
139
|
+
reactTag,
|
|
140
|
+
ancestorReactTag,
|
|
141
|
+
errorCallback,
|
|
142
|
+
callback,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
measureLayoutRelativeToParent(
|
|
148
|
+
reactTag: number,
|
|
149
|
+
errorCallback: (error: Object) => void,
|
|
150
|
+
callback: (
|
|
151
|
+
left: number,
|
|
152
|
+
top: number,
|
|
153
|
+
width: number,
|
|
154
|
+
height: number,
|
|
155
|
+
) => void,
|
|
156
|
+
): void {
|
|
157
|
+
if (isFabricReactTag(reactTag)) {
|
|
158
|
+
console.warn(
|
|
159
|
+
'RCTUIManager.measureLayoutRelativeToParent method is deprecated and it will not be implemented in newer versions of RN (Fabric) - T47686450',
|
|
160
|
+
);
|
|
161
|
+
const FabricUIManager = nullthrows(getFabricUIManager());
|
|
162
|
+
const shadowNode =
|
|
163
|
+
FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
|
|
164
|
+
if (shadowNode) {
|
|
165
|
+
FabricUIManager.measure(
|
|
166
|
+
shadowNode,
|
|
167
|
+
(left, top, width, height, pageX, pageY) => {
|
|
168
|
+
callback(left, top, width, height);
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
// Paper
|
|
174
|
+
UIManagerImpl.measureLayoutRelativeToParent(
|
|
175
|
+
reactTag,
|
|
176
|
+
errorCallback,
|
|
177
|
+
callback,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
dispatchViewManagerCommand(
|
|
183
|
+
reactTag: number,
|
|
184
|
+
commandName: number | string,
|
|
185
|
+
commandArgs: any[],
|
|
186
|
+
) {
|
|
187
|
+
if (isFabricReactTag(reactTag)) {
|
|
188
|
+
const FabricUIManager = nullthrows(getFabricUIManager());
|
|
189
|
+
const shadowNode =
|
|
190
|
+
FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
|
|
191
|
+
if (shadowNode) {
|
|
192
|
+
// Transform the accidental CommandID into a CommandName which is the stringified number.
|
|
193
|
+
// The interop layer knows how to convert this number into the right method name.
|
|
194
|
+
// Stringify a string is a no-op, so it's safe.
|
|
195
|
+
commandName = `${commandName}`;
|
|
196
|
+
FabricUIManager.dispatchCommand(shadowNode, commandName, commandArgs);
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
UIManagerImpl.dispatchViewManagerCommand(
|
|
200
|
+
reactTag,
|
|
201
|
+
// We have some legacy components that are actually already using strings. ¯\_(ツ)_/¯
|
|
202
|
+
// $FlowFixMe[incompatible-call]
|
|
203
|
+
commandName,
|
|
204
|
+
commandArgs,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
module.exports = UIManager;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NativePlatformColor } from "./NativePlatformColor";
|
|
2
|
+
|
|
3
|
+
export const PlatformColor = (...colors: string[]) => {
|
|
4
|
+
const color = NativePlatformColor.getSystemColor(colors);
|
|
5
|
+
return color;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const normalizeColorObject = (color: string) => {
|
|
9
|
+
return color;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const processColorObject = (color: string) => {
|
|
13
|
+
return color;
|
|
14
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// This implementation is based on iOS logic from file - react-native/Libraries/Vibration/Vibration.js
|
|
2
|
+
import NativeVibration from 'react-native/Libraries/Vibration/NativeVibration'; // RNOH: patch
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Vibration API
|
|
6
|
+
*
|
|
7
|
+
* See https://reactnative.dev/docs/vibration
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
let _vibrating: boolean = false;
|
|
11
|
+
let _id: number = 0; // _id is necessary to prevent race condition.
|
|
12
|
+
const _default_vibration_length = 400;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
function vibrateByPattern(pattern: Array<number>, repeat: boolean = false) {
|
|
16
|
+
if (_vibrating) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
_vibrating = true;
|
|
20
|
+
if (pattern.length === 0) {
|
|
21
|
+
_vibrating = false;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
vibrateScheduler(++_id, pattern, repeat, 0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function vibrateScheduler(
|
|
28
|
+
id: number,
|
|
29
|
+
pattern: Array<number>,
|
|
30
|
+
repeat: boolean,
|
|
31
|
+
nextIndex: number,
|
|
32
|
+
shouldVibrate: boolean = false, // first value in pattern is delay
|
|
33
|
+
) {
|
|
34
|
+
if (!_vibrating || id !== _id) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (shouldVibrate && nextIndex < pattern.length) {
|
|
38
|
+
NativeVibration.vibrate(pattern[nextIndex]);
|
|
39
|
+
}
|
|
40
|
+
if (nextIndex >= pattern.length) {
|
|
41
|
+
if (repeat) {
|
|
42
|
+
// $FlowFixMe[reassign-const]
|
|
43
|
+
nextIndex = 0;
|
|
44
|
+
shouldVibrate = true;
|
|
45
|
+
} else {
|
|
46
|
+
_vibrating = false;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
setTimeout(
|
|
51
|
+
() => vibrateScheduler(id, pattern, repeat, nextIndex + 1, !shouldVibrate),
|
|
52
|
+
pattern[nextIndex],
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const Vibration = {
|
|
57
|
+
/**
|
|
58
|
+
* Trigger a vibration with specified `pattern`.
|
|
59
|
+
*
|
|
60
|
+
* See https://reactnative.dev/docs/vibration#vibrate
|
|
61
|
+
*/
|
|
62
|
+
vibrate: function (
|
|
63
|
+
pattern: number | Array<number> = _default_vibration_length,
|
|
64
|
+
repeat: boolean = false,
|
|
65
|
+
) {
|
|
66
|
+
if (_vibrating) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (typeof pattern === 'number') {
|
|
70
|
+
NativeVibration.vibrate(pattern);
|
|
71
|
+
} else if (Array.isArray(pattern)) {
|
|
72
|
+
vibrateByPattern(pattern, repeat);
|
|
73
|
+
} else {
|
|
74
|
+
throw new Error('Vibration pattern should be a number or array');
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
/**
|
|
78
|
+
* Stop vibration
|
|
79
|
+
*
|
|
80
|
+
* See https://reactnative.dev/docs/vibration#cancel
|
|
81
|
+
*/
|
|
82
|
+
cancel: function () {
|
|
83
|
+
_vibrating = false;
|
|
84
|
+
NativeVibration.cancel();
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
module.exports = Vibration;
|
package/index.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
module.exports = {
|
|
2
|
+
get AccessibilityInfo() {
|
|
3
|
+
return require('./Libraries/Components/AccessibilityInfo/AccessibilityInfo')
|
|
4
|
+
.default;
|
|
5
|
+
},
|
|
2
6
|
get ActivityIndicator() {
|
|
3
7
|
return require('react-native/Libraries/Components/ActivityIndicator/ActivityIndicator')
|
|
4
8
|
.default;
|
|
@@ -24,6 +28,9 @@ module.exports = {
|
|
|
24
28
|
get Button() {
|
|
25
29
|
return require('./Libraries/Components/Button/Button');
|
|
26
30
|
},
|
|
31
|
+
get DevSettings() {
|
|
32
|
+
return require('react-native/Libraries/Utilities/DevSettings');
|
|
33
|
+
},
|
|
27
34
|
get Dimensions() {
|
|
28
35
|
return require('react-native/Libraries/Utilities/Dimensions').default;
|
|
29
36
|
},
|
|
@@ -82,6 +89,10 @@ module.exports = {
|
|
|
82
89
|
get Platform() {
|
|
83
90
|
return require('./Libraries/Utilities/Platform');
|
|
84
91
|
},
|
|
92
|
+
get PlatformColor() {
|
|
93
|
+
return require('./Libraries/StyleSheet/PlatformColorValueTypes')
|
|
94
|
+
.PlatformColor;
|
|
95
|
+
},
|
|
85
96
|
get Pressable() {
|
|
86
97
|
return require('react-native/Libraries/Components/Pressable/Pressable')
|
|
87
98
|
.default;
|
|
@@ -99,6 +110,9 @@ module.exports = {
|
|
|
99
110
|
get SafeAreaView() {
|
|
100
111
|
return require('./Libraries/Components/SafeAreaView/SafeAreaView').default;
|
|
101
112
|
},
|
|
113
|
+
get Share() {
|
|
114
|
+
return require('react-native/Libraries/Share/Share');
|
|
115
|
+
},
|
|
102
116
|
get ScrollView() {
|
|
103
117
|
return require('./Libraries/Components/ScrollView/ScrollView');
|
|
104
118
|
},
|
|
@@ -117,6 +131,9 @@ module.exports = {
|
|
|
117
131
|
get TextInput() {
|
|
118
132
|
return require('./Libraries/Components/TextInput/TextInput.harmony');
|
|
119
133
|
},
|
|
134
|
+
get ToastAndroid() {
|
|
135
|
+
return require('react-native/Libraries/Components/ToastAndroid/ToastAndroid.android');
|
|
136
|
+
},
|
|
120
137
|
get Touchable() {
|
|
121
138
|
return require('react-native/Libraries/Components/Touchable/Touchable');
|
|
122
139
|
},
|
|
@@ -136,7 +153,7 @@ module.exports = {
|
|
|
136
153
|
return require('react-native/Libraries/TurboModule/TurboModuleRegistry');
|
|
137
154
|
},
|
|
138
155
|
get UIManager() {
|
|
139
|
-
return require('
|
|
156
|
+
return require('./Libraries/ReactNative/UIManager');
|
|
140
157
|
},
|
|
141
158
|
get useAnimatedValue() {
|
|
142
159
|
return require('react-native/Libraries/Animated/useAnimatedValue').default;
|
|
@@ -164,7 +181,7 @@ module.exports = {
|
|
|
164
181
|
return require('react-native/Libraries/Lists/SectionList').default;
|
|
165
182
|
},
|
|
166
183
|
get Vibration() {
|
|
167
|
-
return require('
|
|
184
|
+
return require('./Libraries/Vibration/Vibration');
|
|
168
185
|
},
|
|
169
186
|
get VirtualizedList() {
|
|
170
187
|
return require('react-native/Libraries/Lists/VirtualizedList');
|
package/jest.config.js
ADDED
package/metro.config.js
CHANGED
|
@@ -38,8 +38,8 @@ function createHarmonyMetroConfig(options) {
|
|
|
38
38
|
if (shouldPrintInfoAboutRNRedirection) {
|
|
39
39
|
info(
|
|
40
40
|
`Redirected imports from ${colors.bold(
|
|
41
|
-
colors.gray('react-native')
|
|
42
|
-
)} to ${colors.bold(reactNativeHarmonyName)}
|
|
41
|
+
colors.gray('react-native')
|
|
42
|
+
)} to ${colors.bold(reactNativeHarmonyName)}`
|
|
43
43
|
);
|
|
44
44
|
shouldPrintInfoAboutRNRedirection = false;
|
|
45
45
|
}
|
|
@@ -53,20 +53,56 @@ function createHarmonyMetroConfig(options) {
|
|
|
53
53
|
if (moduleName.startsWith('.')) {
|
|
54
54
|
const moduleAbsPath = pathUtils.resolve(
|
|
55
55
|
pathUtils.dirname(ctx.originModulePath),
|
|
56
|
-
moduleName
|
|
56
|
+
moduleName
|
|
57
57
|
);
|
|
58
58
|
const [_, modulePathRelativeToReactNative] = moduleAbsPath.split(
|
|
59
|
-
`${pathUtils.sep}node_modules${pathUtils.sep}react-native${pathUtils.sep}
|
|
59
|
+
`${pathUtils.sep}node_modules${pathUtils.sep}react-native${pathUtils.sep}`
|
|
60
60
|
);
|
|
61
61
|
try {
|
|
62
62
|
return ctx.resolveRequest(
|
|
63
63
|
ctx,
|
|
64
64
|
`${reactNativeHarmonyName}${pathUtils.sep}${modulePathRelativeToReactNative}`,
|
|
65
|
-
'harmony'
|
|
65
|
+
'harmony'
|
|
66
66
|
);
|
|
67
67
|
} catch (err) {}
|
|
68
68
|
}
|
|
69
69
|
return ctx.resolveRequest(ctx, moduleName, 'ios');
|
|
70
|
+
} else if(isHarmonyPackageInternalImport(ctx.originModulePath, moduleName)) {
|
|
71
|
+
/**
|
|
72
|
+
* Replace internal imports in `react-native-foo` with equivalent files from `react-native-harmony-foo`
|
|
73
|
+
* if a package has internal import redirection enabled in its package.json configuration e.g.
|
|
74
|
+
*
|
|
75
|
+
* react-native-harmony-foo/package.json:
|
|
76
|
+
* "harmony": {
|
|
77
|
+
* "alias": "react-native-foo"
|
|
78
|
+
* "redirectInternalImports": true,
|
|
79
|
+
* }
|
|
80
|
+
*/
|
|
81
|
+
const alias = getPackageNameFromOriginModulePath(ctx.originModulePath);
|
|
82
|
+
if (alias) {
|
|
83
|
+
const harmonyPackage = getHarmonyPackageByAliasMap(".");
|
|
84
|
+
const harmonyPackageName = harmonyPackage[alias]?.name;
|
|
85
|
+
const redirectInternalImports = harmonyPackage[alias]?.redirectInternalImports;
|
|
86
|
+
if (harmonyPackageName && !isRequestFromHarmonyPackage(ctx.originModulePath, harmonyPackageName) && redirectInternalImports) {
|
|
87
|
+
const moduleAbsPath = pathUtils.resolve(
|
|
88
|
+
pathUtils.dirname(ctx.originModulePath),
|
|
89
|
+
moduleName,
|
|
90
|
+
);
|
|
91
|
+
const [_, modulePathRelativeToOriginalPackage] = moduleAbsPath.split(
|
|
92
|
+
`${pathUtils.sep}node_modules${pathUtils.sep}${alias}${pathUtils.sep}`,
|
|
93
|
+
);
|
|
94
|
+
const backslashes = new RegExp('\\\\', 'g');
|
|
95
|
+
const pathToHarmonyModule = `${harmonyPackageName}/${modulePathRelativeToOriginalPackage.replace(backslashes, "/")}`;
|
|
96
|
+
try {
|
|
97
|
+
return ctx.resolveRequest(
|
|
98
|
+
ctx,
|
|
99
|
+
pathToHarmonyModule,
|
|
100
|
+
'harmony',
|
|
101
|
+
);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
70
106
|
} else {
|
|
71
107
|
/**
|
|
72
108
|
* Replace `react-native-foo` with `react-native-harmony-foo` if a package has harmony directory and proper package.json configuration e.g.
|
|
@@ -76,21 +112,21 @@ function createHarmonyMetroConfig(options) {
|
|
|
76
112
|
* "alias": "react-native-foo"
|
|
77
113
|
* }
|
|
78
114
|
*/
|
|
79
|
-
const
|
|
115
|
+
const harmonyPackageByAlias = getHarmonyPackageByAliasMap(".");
|
|
80
116
|
const alias = getPackageName(moduleName);
|
|
81
117
|
if (alias) {
|
|
82
|
-
const harmonyPackageName =
|
|
118
|
+
const harmonyPackageName = harmonyPackageByAlias[alias]?.name;
|
|
83
119
|
if (
|
|
84
120
|
harmonyPackageName &&
|
|
85
121
|
!isRequestFromHarmonyPackage(
|
|
86
122
|
ctx.originModulePath,
|
|
87
|
-
harmonyPackageName
|
|
123
|
+
harmonyPackageName
|
|
88
124
|
)
|
|
89
125
|
) {
|
|
90
126
|
return ctx.resolveRequest(
|
|
91
127
|
ctx,
|
|
92
128
|
moduleName.replace(alias, harmonyPackageName),
|
|
93
|
-
platform
|
|
129
|
+
platform
|
|
94
130
|
);
|
|
95
131
|
}
|
|
96
132
|
}
|
|
@@ -128,13 +164,56 @@ function getPackageName(moduleName) {
|
|
|
128
164
|
}
|
|
129
165
|
}
|
|
130
166
|
|
|
167
|
+
/**
|
|
168
|
+
* @param originModulePath {string}
|
|
169
|
+
* @returns {string}
|
|
170
|
+
*/
|
|
171
|
+
function getPackageNameFromOriginModulePath(originModulePath) {
|
|
172
|
+
const nodeModulesPosition = originModulePath.search("node_modules");
|
|
173
|
+
const pathRelativeToNodeModules = originModulePath.substring(nodeModulesPosition);
|
|
174
|
+
const pathSegments = pathRelativeToNodeModules.split(pathUtils.sep);
|
|
175
|
+
const module = pathSegments[1];
|
|
176
|
+
if (module.startsWith('@')) {
|
|
177
|
+
return `${pathSegments[1]}/${pathSegments[2]}`;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
return pathSegments[1];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @param originModulePath {string}
|
|
186
|
+
* @param moduleName {string}
|
|
187
|
+
* @returns {boolean}
|
|
188
|
+
*/
|
|
189
|
+
function isHarmonyPackageInternalImport(originModulePath, moduleName) {
|
|
190
|
+
if (moduleName.startsWith(".")) {
|
|
191
|
+
const alias = getPackageNameFromOriginModulePath(originModulePath);
|
|
192
|
+
const slashes = new RegExp('/', 'g');
|
|
193
|
+
if (alias && originModulePath.includes(`${pathUtils.sep}node_modules${pathUtils.sep}${alias.replace(slashes, pathUtils.sep)}${pathUtils.sep}`)) {
|
|
194
|
+
const harmonyPackage = getHarmonyPackageByAliasMap(".");
|
|
195
|
+
const harmonyPackageName = harmonyPackage[alias]?.name;
|
|
196
|
+
if (
|
|
197
|
+
harmonyPackageName &&
|
|
198
|
+
!isRequestFromHarmonyPackage(
|
|
199
|
+
originModulePath,
|
|
200
|
+
harmonyPackageName,
|
|
201
|
+
)
|
|
202
|
+
) {
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
|
|
131
210
|
/**
|
|
132
211
|
* @param originModulePath {string}
|
|
133
212
|
* @returns {boolean}
|
|
134
213
|
*/
|
|
135
214
|
function isInternalReactNativeRelativeImport(originModulePath) {
|
|
136
215
|
return originModulePath.includes(
|
|
137
|
-
`${pathUtils.sep}node_modules${pathUtils.sep}react-native${pathUtils.sep}
|
|
216
|
+
`${pathUtils.sep}node_modules${pathUtils.sep}react-native${pathUtils.sep}`
|
|
138
217
|
);
|
|
139
218
|
}
|
|
140
219
|
|
|
@@ -147,27 +226,27 @@ function isRequestFromHarmonyPackage(originModulePath, harmonyPackageName) {
|
|
|
147
226
|
const slashes = new RegExp('/', 'g');
|
|
148
227
|
const packagePath = harmonyPackageName.replace(slashes, pathUtils.sep);
|
|
149
228
|
return originModulePath.includes(
|
|
150
|
-
`${pathUtils.sep}node_modules${pathUtils.sep}${packagePath}${pathUtils.sep}
|
|
229
|
+
`${pathUtils.sep}node_modules${pathUtils.sep}${packagePath}${pathUtils.sep}`
|
|
151
230
|
);
|
|
152
231
|
}
|
|
153
232
|
|
|
154
233
|
/**
|
|
155
|
-
* @type {Record<string, string> | undefined}
|
|
234
|
+
* @type {Record<string, {name: string, redirectInternalImports: boolean}> | undefined}
|
|
156
235
|
*/
|
|
157
|
-
let
|
|
236
|
+
let cachedHarmonyPackageByAliasMap = undefined;
|
|
158
237
|
|
|
159
238
|
/**
|
|
160
239
|
* @param projectRootPath {string}
|
|
161
240
|
*/
|
|
162
|
-
function
|
|
241
|
+
function getHarmonyPackageByAliasMap(projectRootPath) {
|
|
163
242
|
/**
|
|
164
|
-
* @type {Record<string, string>}
|
|
243
|
+
* @type {Record<string, {name: string, redirectInternalImports: boolean}>}
|
|
165
244
|
*/
|
|
166
245
|
const initialAcc = {};
|
|
167
|
-
if (
|
|
168
|
-
return
|
|
246
|
+
if (cachedHarmonyPackageByAliasMap) {
|
|
247
|
+
return cachedHarmonyPackageByAliasMap;
|
|
169
248
|
}
|
|
170
|
-
|
|
249
|
+
cachedHarmonyPackageByAliasMap = findHarmonyNodeModulePaths(
|
|
171
250
|
findHarmonyNodeModuleSearchPaths(projectRootPath),
|
|
172
251
|
).reduce((acc, harmonyNodeModulePath) => {
|
|
173
252
|
const harmonyNodeModulePathSegments = harmonyNodeModulePath.split(
|
|
@@ -185,13 +264,17 @@ function getHarmonyPackageNameByAlias(projectRootPath) {
|
|
|
185
264
|
const packageJSONPath = `${harmonyNodeModulePath}${pathUtils.sep}package.json`;
|
|
186
265
|
const packageJSON = readHarmonyModulePackageJSON(packageJSONPath);
|
|
187
266
|
const alias = packageJSON.harmony?.alias;
|
|
267
|
+
const redirectInternalImports = packageJSON?.harmony?.redirectInternalImports ?? false;
|
|
188
268
|
if (alias) {
|
|
189
|
-
acc[alias] =
|
|
269
|
+
acc[alias] ={
|
|
270
|
+
name: harmonyNodeModuleName,
|
|
271
|
+
redirectInternalImports: redirectInternalImports
|
|
272
|
+
}
|
|
190
273
|
}
|
|
191
274
|
return acc;
|
|
192
275
|
}, initialAcc);
|
|
193
276
|
const harmonyPackagesCount = Object.keys(
|
|
194
|
-
|
|
277
|
+
cachedHarmonyPackageByAliasMap,
|
|
195
278
|
).length;
|
|
196
279
|
if (harmonyPackagesCount > 0) {
|
|
197
280
|
const prettyHarmonyPackagesCount = colors.bold(
|
|
@@ -203,8 +286,8 @@ function getHarmonyPackageNameByAlias(projectRootPath) {
|
|
|
203
286
|
`Redirected imports to ${prettyHarmonyPackagesCount} harmony-specific third-party package(s):`,
|
|
204
287
|
);
|
|
205
288
|
if (harmonyPackagesCount > 0) {
|
|
206
|
-
Object.entries(
|
|
207
|
-
([original, alias]) => {
|
|
289
|
+
Object.entries(cachedHarmonyPackageByAliasMap).forEach(
|
|
290
|
+
([original, {name: alias}]) => {
|
|
208
291
|
info(
|
|
209
292
|
`• ${colors.bold(colors.gray(original))} → ${colors.bold(alias)}`,
|
|
210
293
|
);
|
|
@@ -215,8 +298,7 @@ function getHarmonyPackageNameByAlias(projectRootPath) {
|
|
|
215
298
|
info('No harmony-specific third-party packages have been detected');
|
|
216
299
|
}
|
|
217
300
|
console.log('');
|
|
218
|
-
|
|
219
|
-
return cachedHarmonyPackageAliasByName;
|
|
301
|
+
return cachedHarmonyPackageByAliasMap;
|
|
220
302
|
}
|
|
221
303
|
|
|
222
304
|
/**
|
|
@@ -227,8 +309,8 @@ function findHarmonyNodeModuleSearchPaths(projectRootPath) {
|
|
|
227
309
|
const nodeModulesPath = `${projectRootPath}${pathUtils.sep}node_modules`;
|
|
228
310
|
const searchPaths = fs
|
|
229
311
|
.readdirSync(nodeModulesPath)
|
|
230
|
-
.filter(dirName => dirName.startsWith('@'))
|
|
231
|
-
.map(dirName => `${nodeModulesPath}${pathUtils.sep}${dirName}`);
|
|
312
|
+
.filter((dirName) => dirName.startsWith('@'))
|
|
313
|
+
.map((dirName) => `${nodeModulesPath}${pathUtils.sep}${dirName}`);
|
|
232
314
|
searchPaths.push(nodeModulesPath);
|
|
233
315
|
return searchPaths;
|
|
234
316
|
}
|
|
@@ -239,11 +321,11 @@ function findHarmonyNodeModuleSearchPaths(projectRootPath) {
|
|
|
239
321
|
*/
|
|
240
322
|
function findHarmonyNodeModulePaths(searchPaths) {
|
|
241
323
|
return searchPaths
|
|
242
|
-
.map(searchPath => {
|
|
324
|
+
.map((searchPath) => {
|
|
243
325
|
return fs
|
|
244
326
|
.readdirSync(searchPath)
|
|
245
|
-
.map(dirName => `${searchPath}${pathUtils.sep}${dirName}`)
|
|
246
|
-
.filter(
|
|
327
|
+
.map((dirName) => `${searchPath}${pathUtils.sep}${dirName}`)
|
|
328
|
+
.filter(hasPackageJSON);
|
|
247
329
|
})
|
|
248
330
|
.flat();
|
|
249
331
|
}
|
|
@@ -252,18 +334,15 @@ function findHarmonyNodeModulePaths(searchPaths) {
|
|
|
252
334
|
* @param nodeModulePath {string}
|
|
253
335
|
* @returns {boolean}
|
|
254
336
|
*/
|
|
255
|
-
function
|
|
337
|
+
function hasPackageJSON(nodeModulePath) {
|
|
256
338
|
if (!fs.lstatSync(nodeModulePath).isDirectory()) return false;
|
|
257
339
|
const nodeModuleContentNames = fs.readdirSync(nodeModulePath);
|
|
258
|
-
return (
|
|
259
|
-
nodeModuleContentNames.includes('harmony') ||
|
|
260
|
-
nodeModuleContentNames.includes('harmony.tar.gz')
|
|
261
|
-
);
|
|
340
|
+
return nodeModuleContentNames.includes('package.json');
|
|
262
341
|
}
|
|
263
342
|
|
|
264
343
|
/**
|
|
265
344
|
* @param packageJSONPath {string}
|
|
266
|
-
* @returns {{name: string, harmony?: {alias?: string}}}
|
|
345
|
+
* @returns {{name: string, harmony?: {alias?: string, redirectInternalImports?: boolean}}}
|
|
267
346
|
*/
|
|
268
347
|
function readHarmonyModulePackageJSON(packageJSONPath) {
|
|
269
348
|
return JSON.parse(fs.readFileSync(packageJSONPath).toString());
|