@flun/node-mobile-app 4.1.2 → 4.2.0
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/Android/346/236/204/345/273/272/346/214/207/345/215/227.md +1 -1
- package/CHANGELOG.md +15 -22
- package/README.md +67 -13
- package/bin/cli.js +3 -3
- package/mobileAppConfig.js +17 -4
- package/package.json +1 -1
- package/scripts/copy-files.js +3 -2
- package/src/commands/build.js +105 -99
- package/src/commands/clean.js +12 -6
- package/src/commands/test.js +141 -133
- package/src/config.js +88 -52
- package/src/copy.js +302 -215
- package/src/fingerprint.js +96 -77
- package/src/generate.js +33 -21
- package/src/install.js +176 -162
- package/src/optimize-node-modules.js +5 -4
- package/src/patcher.js +438 -358
- package/src/platform-ios.js +122 -106
- package/src/utils.js +351 -252
- package/template/.prettierrc.js +8 -2
- package/template/App.tsx +98 -87
- package/template/babel.config.js +8 -2
- package/template/eslint.config.js +8 -2
- package/template/metro.config.js +9 -3
- package//350/207/252/345/256/232/344/271/211libnode/346/214/207/345/215/227.md +2 -2
package/template/App.tsx
CHANGED
|
@@ -1,105 +1,116 @@
|
|
|
1
|
+
import * as NodeMobile from '@flun/nodejs-mobile-react-native';
|
|
1
2
|
import { useEffect, useState } from 'react';
|
|
2
|
-
import {
|
|
3
|
+
import { ActivityIndicator, StyleSheet, Text, useColorScheme, View } from 'react-native';
|
|
3
4
|
import { WebView } from 'react-native-webview';
|
|
4
|
-
import * as NodeMobile from '@flun/nodejs-mobile-react-native';
|
|
5
5
|
import runtime from './mobileApp.runtime';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
/**
|
|
8
|
+
* 背景色解析,支持三种配置:
|
|
9
|
+
* - '#ffffff' / 'white' 等字符串:固定色
|
|
10
|
+
* - 'system':跟随系统(内部用默认浅/深色)
|
|
11
|
+
* - { light: '#fff', dark: '#000' }:按系统主题切换
|
|
12
|
+
* @param {string|object} bg 背景色配置
|
|
13
|
+
* @param {string|null|undefined} theme 当前系统主题
|
|
14
|
+
* @returns {string} 解析后的颜色值
|
|
15
|
+
*/
|
|
11
16
|
const resolveBgColor = (bg: string | { light: string; dark: string }, theme: string | null | undefined): string => {
|
|
12
17
|
if (typeof bg === 'object' && bg !== null)
|
|
13
18
|
return (theme === 'dark' ? bg.dark : bg.light) || bg.light || '#ffffff';
|
|
14
19
|
if (bg === 'system') return theme === 'dark' ? '#121212' : '#ffffff';
|
|
15
20
|
return bg || '#ffffff';
|
|
16
|
-
}
|
|
17
|
-
App = () => {
|
|
18
|
-
const systemTheme = useColorScheme(),
|
|
19
|
-
[url, setUrl] = useState<string | null>(null), [failedReason, setFailedReason] = useState<string | null>(null),
|
|
20
|
-
[showDetail, setShowDetail] = useState(false),
|
|
21
|
-
styles = StyleSheet.create({
|
|
22
|
-
fill: { flex: 1 },
|
|
23
|
-
loading: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
|
24
|
-
errorContainer: {
|
|
25
|
-
flex: 1,
|
|
26
|
-
justifyContent: 'center',
|
|
27
|
-
alignItems: 'center',
|
|
28
|
-
padding: 24,
|
|
29
|
-
},
|
|
30
|
-
errorTitle: { fontSize: 20, fontWeight: '600', marginBottom: 16, color: '#c00' },
|
|
31
|
-
errorHint: { fontSize: 14, textAlign: 'center', color: '#888' },
|
|
32
|
-
detailToggle: { fontSize: 13, marginTop: 24, color: '#06c' },
|
|
33
|
-
detailText: {
|
|
34
|
-
fontSize: 12,
|
|
35
|
-
marginTop: 12,
|
|
36
|
-
paddingHorizontal: 16,
|
|
37
|
-
color: '#666',
|
|
38
|
-
fontFamily: 'monospace',
|
|
39
|
-
textAlign: 'center',
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
NodeMobile.start('main.js');
|
|
21
|
+
};
|
|
44
22
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
23
|
+
/**
|
|
24
|
+
* 应用根组件:启动 Node 服务,按消息切换 loading / 错误页 / WebView
|
|
25
|
+
* >查看定义:@see {@link App}
|
|
26
|
+
* @returns {JSX.Element} 当前状态对应的视图
|
|
27
|
+
*/
|
|
28
|
+
const App = () => {
|
|
29
|
+
const systemTheme = useColorScheme(),
|
|
30
|
+
[url, setUrl] = useState<string | null>(null), [failedReason, setFailedReason] = useState<string | null>(null),
|
|
31
|
+
[showDetail, setShowDetail] = useState(false),
|
|
32
|
+
styles = StyleSheet.create({
|
|
33
|
+
fill: { flex: 1 },
|
|
34
|
+
loading: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
|
35
|
+
errorContainer: {
|
|
36
|
+
flex: 1,
|
|
37
|
+
justifyContent: 'center',
|
|
38
|
+
alignItems: 'center',
|
|
39
|
+
padding: 24,
|
|
40
|
+
},
|
|
41
|
+
errorTitle: { fontSize: 20, fontWeight: '600', marginBottom: 16, color: '#c00' },
|
|
42
|
+
errorHint: { fontSize: 14, textAlign: 'center', color: '#888' },
|
|
43
|
+
detailToggle: { fontSize: 13, marginTop: 24, color: '#06c' },
|
|
44
|
+
detailText: {
|
|
45
|
+
fontSize: 12,
|
|
46
|
+
marginTop: 12,
|
|
47
|
+
paddingHorizontal: 16,
|
|
48
|
+
color: '#666',
|
|
49
|
+
fontFamily: 'monospace',
|
|
50
|
+
textAlign: 'center',
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
NodeMobile.start('main.js');
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
const listener = (msg: unknown) => {
|
|
57
|
+
if (typeof msg !== 'string') return;
|
|
58
|
+
if (msg.startsWith('Node started on ')) {
|
|
59
|
+
const target = msg.slice('Node started on '.length).trim();
|
|
60
|
+
setFailedReason(null);
|
|
61
|
+
setUrl(target);
|
|
62
|
+
} else if (msg.startsWith('Node failed: ')) {
|
|
63
|
+
const reason = msg.slice('Node failed: '.length).trim();
|
|
64
|
+
setFailedReason(reason);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
60
67
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
<View style={styles.errorContainer}>
|
|
65
|
-
<Text style={styles.errorTitle}>应用启动失败</Text>
|
|
66
|
-
<Text style={styles.errorHint}>请稍后重试,或联系应用发布者</Text>
|
|
67
|
-
<Text
|
|
68
|
-
style={styles.detailToggle}
|
|
69
|
-
onPress={() => setShowDetail((v) => !v)}
|
|
70
|
-
>
|
|
71
|
-
{showDetail ? '▼ 收起详细信息' : '▶ 查看详细信息'}
|
|
72
|
-
</Text>
|
|
73
|
-
{showDetail && (
|
|
74
|
-
<Text style={styles.detailText}>{failedReason}</Text>
|
|
75
|
-
)}
|
|
76
|
-
</View>
|
|
77
|
-
);
|
|
78
|
-
}
|
|
68
|
+
NodeMobile.channel.addListener('message', listener);
|
|
69
|
+
return () => NodeMobile.channel.removeListener('message', listener);
|
|
70
|
+
}, []);
|
|
79
71
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
72
|
+
// 失败:显示错误页(用户看提示,开发者点开看详情)
|
|
73
|
+
if (failedReason) {
|
|
74
|
+
return (
|
|
75
|
+
<View style={styles.errorContainer}>
|
|
76
|
+
<Text style={styles.errorTitle}>应用启动失败</Text>
|
|
77
|
+
<Text style={styles.errorHint}>请稍后重试,或联系应用发布者</Text>
|
|
78
|
+
<Text
|
|
79
|
+
style={styles.detailToggle}
|
|
80
|
+
onPress={() => setShowDetail((v) => !v)}
|
|
81
|
+
>
|
|
82
|
+
{showDetail ? '▼ 收起详细信息' : '▶ 查看详细信息'}
|
|
83
|
+
</Text>
|
|
84
|
+
{showDetail && (
|
|
85
|
+
<Text style={styles.detailText}>{failedReason}</Text>
|
|
86
|
+
)}
|
|
87
|
+
</View>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
90
|
|
|
91
|
+
// 尚未就绪:loading
|
|
92
|
+
if (!url) {
|
|
93
|
+
if (!runtime.showLoading) return <View style={styles.fill} />;
|
|
91
94
|
return (
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
javaScriptEnabled={runtime.webview.enableJavaScript}
|
|
97
|
-
domStorageEnabled={runtime.webview.enableDomStorage}
|
|
98
|
-
allowFileAccess={runtime.webview.allowFileAccess}
|
|
99
|
-
allowUniversalAccessFromFileURLs={true}
|
|
100
|
-
mixedContentMode={runtime.webview.mixedContentMode}
|
|
101
|
-
/>
|
|
95
|
+
<View style={styles.loading}>
|
|
96
|
+
<ActivityIndicator size="large" />
|
|
97
|
+
<Text>{runtime.loadingText}</Text>
|
|
98
|
+
</View>
|
|
102
99
|
);
|
|
103
|
-
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<WebView
|
|
104
|
+
source={{ uri: url }}
|
|
105
|
+
style={[styles.fill, { backgroundColor: resolveBgColor(runtime.webview.backgroundColor, systemTheme) }]}
|
|
106
|
+
originWhitelist={['*']}
|
|
107
|
+
javaScriptEnabled={runtime.webview.enableJavaScript}
|
|
108
|
+
domStorageEnabled={runtime.webview.enableDomStorage}
|
|
109
|
+
allowFileAccess={runtime.webview.allowFileAccess}
|
|
110
|
+
allowUniversalAccessFromFileURLs={true}
|
|
111
|
+
mixedContentMode={runtime.webview.mixedContentMode}
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
104
115
|
|
|
105
116
|
export { App };
|
package/template/babel.config.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import reactNativeConfig from '@react-native/eslint-config/flat';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* ESLint flat config,基于 React Native 官方配置
|
|
5
|
+
* >查看定义:@see {@link eslintConfig}
|
|
6
|
+
*/
|
|
7
|
+
const eslintConfig = [
|
|
4
8
|
...reactNativeConfig,
|
|
5
9
|
// 你的自定义配置写在这里
|
|
6
|
-
];
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export default eslintConfig;
|
package/template/metro.config.js
CHANGED
|
@@ -3,8 +3,8 @@ import exclusionListModule from 'metro-config/private/defaults/exclusionList';
|
|
|
3
3
|
import { dirname, resolve } from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
|
|
6
|
-
const exclusionList = exclusionListModule.default,
|
|
7
|
-
|
|
6
|
+
const exclusionList = exclusionListModule.default, __filename = fileURLToPath(import.meta.url),
|
|
7
|
+
__dirname = dirname(__filename), workspaceRoot = resolve(__dirname, '..'),
|
|
8
8
|
config = {
|
|
9
9
|
watchFolders: [__dirname, resolve(workspaceRoot, 'node_modules')],
|
|
10
10
|
resolver: {
|
|
@@ -13,4 +13,10 @@ const exclusionList = exclusionListModule.default,
|
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Metro 打包配置,合并默认配置与 workspace 解析路径
|
|
18
|
+
* >查看定义:@see {@link metroConfig}
|
|
19
|
+
*/
|
|
20
|
+
const metroConfig = mergeConfig(getDefaultConfig(__dirname), config);
|
|
21
|
+
|
|
22
|
+
export default metroConfig;
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
- GitHub
|
|
41
41
|
- 源码仓库:[OpenFlun/nodejs-mobile](https://github.com/OpenFlun/nodejs-mobile)
|
|
42
42
|
- Release 页面:[Releases](https://github.com/OpenFlun/nodejs-mobile/releases)
|
|
43
|
-
- Gitee(
|
|
43
|
+
- Gitee(中国)
|
|
44
44
|
- 源码仓库:[OpenFlun/nodejs-mobile](https://gitee.com/OpenFlun/nodejs-mobile)
|
|
45
45
|
- Release 页面:[Releases](https://gitee.com/OpenFlun/nodejs-mobile/releases)
|
|
46
46
|
|
|
@@ -221,7 +221,7 @@ Start-Sleep -Seconds 5
|
|
|
221
221
|
### 3.1 准备源码与 NDK
|
|
222
222
|
|
|
223
223
|
```bash
|
|
224
|
-
# 克隆源码(
|
|
224
|
+
# 克隆源码(中国推荐 Gitee)
|
|
225
225
|
git clone https://gitee.com/OpenFlun/nodejs-mobile.git
|
|
226
226
|
cd nodejs-mobile
|
|
227
227
|
|