@flun/node-mobile-app 4.1.1 → 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/src/install.js CHANGED
@@ -1,20 +1,25 @@
1
- import path from 'path';
1
+ import chalk from 'chalk';
2
+ import { createHash } from 'crypto';
2
3
  import fs from 'fs';
3
- import { run } from './utils.js';
4
- import { clearVerified, optimizeNodeModules, SELF_HASH } from './optimize-node-modules.js';
5
4
  import { createRequire } from 'module';
6
- import chalk from 'chalk';
5
+ import path from 'path';
7
6
  import { resolveSdkPath } from './config.js';
8
- import { createHash } from 'crypto';
9
- const
7
+ import { clearVerified, optimizeNodeModules, SELF_HASH } from './optimize-node-modules.js';
8
+ import { run } from './utils.js';
9
+
10
+ const ABI_MAP = {
11
+ 'armeabi-v7a': { arch: 'arm', prefix: 'armv7a-linux-androideabi' },
12
+ 'arm64-v8a': { arch: 'arm64', prefix: 'aarch64-linux-android' },
13
+ 'x86_64': { arch: 'x64', prefix: 'x86_64-linux-android' }
14
+ },
10
15
  /**
11
16
  * 内部:从 libnode 的 node_version.h 读取运行时 Node 主版本号
12
17
  * 读不到直接报错退出(说明 libnode 目录不完整,继续执行必失败)
13
18
  */
14
19
  resolveRuntimeMajor = (userProjectDir, targetPlatform) => {
15
20
  const sub = targetPlatform === 'ios' ? 'ios' : 'android',
16
- header = path.join(userProjectDir, 'node_modules', '@flun', 'nodejs-mobile-react-native',
17
- sub, 'libnode', 'include', 'node', 'node_version.h');
21
+ header = path.join(userProjectDir, 'node_modules', '@flun', 'nodejs-mobile-react-native', sub,
22
+ 'libnode', 'include', 'node', 'node_version.h');
18
23
  if (!fs.existsSync(header)) {
19
24
  console.error(chalk.red(' [错误] 未找到 node_version.h: ' + header));
20
25
  console.error(chalk.red(' 说明 libnode 目录不完整,请重新安装依赖;')), process.exit(1);
@@ -37,8 +42,7 @@ const
37
42
  } catch { return false; }
38
43
 
39
44
  const nodeModulesDir = path.join(userProjectDir, 'node_modules');
40
- for (const name of Object.keys(deps))
41
- if (fs.existsSync(path.join(nodeModulesDir, name, 'binding.gyp'))) return true;
45
+ for (const name of Object.keys(deps)) if (fs.existsSync(path.join(nodeModulesDir, name, 'binding.gyp'))) return true;
42
46
  return false;
43
47
  },
44
48
  /**
@@ -49,11 +53,7 @@ const
49
53
  for (const c of candidates) if (fs.existsSync(path.join(c, 'android', 'build.gradle'))) return c;
50
54
  return null;
51
55
  },
52
- ABI_MAP = {
53
- 'armeabi-v7a': { arch: 'arm', prefix: 'armv7a-linux-androideabi' },
54
- 'arm64-v8a': { arch: 'arm64', prefix: 'aarch64-linux-android' },
55
- 'x86_64': { arch: 'x64', prefix: 'x86_64-linux-android' }
56
- },
56
+
57
57
  resolveNdkVersion = buildDir => {
58
58
  const gradle = path.join(buildDir, 'android', 'build.gradle');
59
59
  if (!fs.existsSync(gradle))
@@ -77,10 +77,11 @@ const
77
77
  if (entry.startsWith('@')) {
78
78
  for (const sub of fs.readdirSync(full)) {
79
79
  const pkgDir = path.join(full, sub);
80
- if (fs.statSync(pkgDir).isDirectory()
81
- && fs.existsSync(path.join(pkgDir, 'binding.gyp'))) result.push(entry + '/' + sub);
80
+ if (fs.statSync(pkgDir).isDirectory() && fs.existsSync(path.join(pkgDir, 'binding.gyp')))
81
+ result.push(entry + '/' + sub);
82
82
  }
83
- } else if (fs.existsSync(path.join(full, 'binding.gyp'))) result.push(entry);
83
+ }
84
+ else if (fs.existsSync(path.join(full, 'binding.gyp'))) result.push(entry);
84
85
  }
85
86
  return result;
86
87
  },
@@ -93,16 +94,14 @@ const
93
94
  console.error(chalk.red(' [错误] 未找到 NDK ' + ndkVersion + ': ' + ndkPath)), process.exit(1);
94
95
  const pluginDir = findPluginDir(userProjectDir);
95
96
  if (!pluginDir) console.error(chalk.red(' [错误] 未找到 @flun/nodejs-mobile-react-native 插件')), process.exit(1);
96
- const libnodeDir = path.join(pluginDir, 'android', 'libnode'),
97
- gypPath = createRequire(path.join(pluginDir, 'package.json')).resolve('@flun/nodejs-mobile-gyp/bin/node-gyp.js'),
98
- hostTag = { win32: 'windows-x86_64', darwin: 'darwin-x86_64', linux: 'linux-x86_64' }[process.platform],
99
- hostOs = process.platform === 'darwin' ? 'mac' : 'linux',
100
- toolSuffix = process.platform === 'win32' ? '.cmd' : '',
101
- binSuffix = process.platform === 'win32' ? '.exe' : '';
97
+ const gypPath = createRequire(path.join(pluginDir, 'package.json')).resolve('@flun/nodejs-mobile-gyp/bin/node-gyp.js'),
98
+ libnodeDir = path.join(pluginDir, 'android', 'libnode'), hostOs = process.platform === 'darwin' ? 'mac' : 'linux',
99
+ toolSuffix = process.platform === 'win32' ? '.cmd' : '', binSuffix = process.platform === 'win32' ? '.exe' : '',
100
+ hostTag = { win32: 'windows-x86_64', darwin: 'darwin-x86_64', linux: 'linux-x86_64' }[process.platform];
102
101
  if (!hostTag) console.error(chalk.red(' [错误] 不支持的操作系统: ' + process.platform)), process.exit(1);
103
102
  const toolchain = path.join(ndkPath, 'toolchains', 'llvm', 'prebuilt', hostTag),
104
- assetsDir = path.dirname(targetDir), gradleProjectDir = path.join(buildDir, 'android');
105
- const patchScript = path.join(pluginDir, 'scripts', 'patch-package.js');
103
+ assetsDir = path.dirname(targetDir), gradleProjectDir = path.join(buildDir, 'android'),
104
+ patchScript = path.join(pluginDir, 'scripts', 'patch-package.js');
106
105
  if (fs.existsSync(patchScript)) run(process.execPath, [patchScript, path.join(targetDir, 'node_modules')],
107
106
  { cwd: targetDir, inherit: true });
108
107
  for (const abi of abiFilters) {
@@ -132,11 +131,10 @@ const
132
131
  }
133
132
  catch (e) { console.error(chalk.red(' [错误] 原生模块编译失败 (' + abi + '): ' + e.message)), process.exit(1); }
134
133
  const missing = findNativePackages(targetDir).filter(name => {
135
- const pkgDir = path.join(targetDir, 'node_modules', name),
134
+ const pkgDir = path.join(targetDir, 'node_modules', name), releaseDir = path.join(pkgDir, 'build', 'Release'),
135
+ hasBuild = fs.existsSync(releaseDir) && fs.readdirSync(releaseDir).some(f => f.endsWith('.node')),
136
136
  prebuildDir = path.join(pkgDir, 'prebuilds', 'android-' + map.arch),
137
- hasPrebuild = fs.existsSync(prebuildDir) && fs.readdirSync(prebuildDir).some(f => f.endsWith('.node')),
138
- releaseDir = path.join(pkgDir, 'build', 'Release'),
139
- hasBuild = fs.existsSync(releaseDir) && fs.readdirSync(releaseDir).some(f => f.endsWith('.node'));
137
+ hasPrebuild = fs.existsSync(prebuildDir) && fs.readdirSync(prebuildDir).some(f => f.endsWith('.node'));
140
138
  return !hasPrebuild && !hasBuild;
141
139
  });
142
140
  if (missing.length > 0) {
@@ -177,152 +175,168 @@ const
177
175
  c = c.substring(0, idx) + injected + c.substring(idx);
178
176
  fs.writeFileSync(file, c, 'utf-8');
179
177
  console.log(chalk.green(' ✓ 已应用 iOS WebView SSL 补丁(允许 localhost 自签证书)'));
180
- },
181
- /**
182
- * 装用户项目的生产依赖到 <buildDir>/nodejs-assets/nodejs-project/
183
- * 只装 dependencies,不装 devDependencies
184
- * 依赖 + 环境未变化且上次构建完成时跳过,否则按需重装 / 仅优化
185
- * 快照记录在 buildDir/.deps-snapshot.json(与桌面版一致)
186
- */
187
- installUserDeps = async (targetDir, userProjectDir, config, buildDir, targetPlatform) => {
188
- const assetsDir = path.dirname(targetDir), bnmFile = path.join(assetsDir, 'BUILD_NATIVE_MODULES.txt'),
189
- nodeModulesDir = path.join(targetDir, 'node_modules'), snapshotPath = path.join(buildDir, '.deps-snapshot.json'),
190
- forceRebuild = process.env.NODE_MOBILE_FORCE_REBUILD === '1', hasNative = hasNativeModules(userProjectDir),
191
- optimizeCfg = config.optimize || {}, thisOptimize = optimizeCfg.enabled !== false,
192
- abiFilters = [...(config.android.abiFilters || [])].sort(),
193
- runtimeMajor = resolveRuntimeMajor(userProjectDir, targetPlatform);
194
-
195
- fs.writeFileSync(bnmFile, hasNative ? '1' : '0');
196
- if (hasNative) console.log(chalk.gray(' ℹ️ 检测到原生模块(binding.gyp)'));
178
+ };
197
179
 
198
- const pkg = JSON.parse(fs.readFileSync(path.join(targetDir, 'package.json'), 'utf-8')), deps = pkg.dependencies || {},
199
- sortedDeps = Object.keys(deps).sort().reduce((acc, k) => { acc[k] = deps[k]; return acc; }, {}),
200
- installerHash = createHash('sha256').update(fs.readFileSync(new URL(import.meta.url), 'utf-8'))
201
- .digest('hex').slice(0, 16),
202
- snapshotData = { deps: sortedDeps, type: pkg.type || '', hasNative, forceRebuild };
180
+ /**
181
+ * 装用户项目的生产依赖到 <buildDir>/nodejs-assets/nodejs-project/
182
+ * 只装 dependencies,不装 devDependencies
183
+ * 依赖 + 环境未变化且上次构建完成时跳过,否则按需重装 / 仅优化
184
+ * 快照记录在 buildDir/.deps-snapshot.json(与桌面版一致)
185
+ * >查看定义:@see {@link installUserDeps}
186
+ * @param {string} targetDir nodejs-project 目录
187
+ * @param {string} userProjectDir 用户项目目录
188
+ * @param {object} config 用户配置
189
+ * @param {string} buildDir 构建目录
190
+ * @param {string} targetPlatform 目标平台('android' 或 'ios')
191
+ * @returns {Promise<void>}
192
+ */
193
+ const installUserDeps = async (targetDir, userProjectDir, config, buildDir, targetPlatform) => {
194
+ const assetsDir = path.dirname(targetDir), bnmFile = path.join(assetsDir, 'BUILD_NATIVE_MODULES.txt'),
195
+ nodeModulesDir = path.join(targetDir, 'node_modules'), snapshotPath = path.join(buildDir, '.deps-snapshot.json'),
196
+ forceRebuild = process.env.NODE_MOBILE_FORCE_REBUILD === '1', hasNative = hasNativeModules(userProjectDir),
197
+ abiFilters = [...(config.android.abiFilters || [])].sort(), optimizeCfg = config.optimize || {},
198
+ thisOptimize = optimizeCfg.enabled !== false, runtimeMajor = resolveRuntimeMajor(userProjectDir, targetPlatform);
203
199
 
204
- let shouldInstall = true, shouldOptimize = thisOptimize, reasons = [];
205
- if (fs.existsSync(snapshotPath)) {
206
- const old = JSON.parse(fs.readFileSync(snapshotPath, 'utf-8')), verifiedOk = old.verified === true,
207
- depsSame = JSON.stringify({
208
- deps: old.deps, type: old.type, hasNative: old.hasNative, forceRebuild: old.forceRebuild
209
- }) === JSON.stringify(snapshotData),
210
- envSame = old.platform === targetPlatform && JSON.stringify(old.abiFilters) === JSON.stringify(abiFilters) &&
211
- old.runtimeMajor === runtimeMajor,
212
- lastOptimized = old.optimized !== false, selfHashSame = old.optimizeHash === SELF_HASH,
213
- installerSame = old.installerHash === installerHash, nmExists = fs.existsSync(nodeModulesDir);
214
- if (!depsSame) reasons.push('依赖清单已变化');
215
- if (!verifiedOk) reasons.push('上次构建未完成');
216
- if (!envSame) reasons.push('运行环境已变化');
217
- if (!nmExists) reasons.push('缓存目录缺失');
218
- if (lastOptimized && !thisOptimize) reasons.push('优化已由开启转为关闭');
219
- if (thisOptimize && !selfHashSame && lastOptimized) reasons.push('优化程序已更新');
220
- if (!installerSame) reasons.push('安装器已更新');
221
- if (reasons.length > 0) {
222
- shouldInstall = true, shouldOptimize = thisOptimize;
223
- console.log(chalk.gray(' ℹ️ ' + (thisOptimize ? '重新安装依赖+优化' : '重新安装依赖')
224
- + '(原因: ' + reasons.join(', ') + ')'));
225
- }
226
- else if (thisOptimize && !lastOptimized)
227
- shouldInstall = false, shouldOptimize = true, console.log(chalk.gray(' ℹ️ 依赖与环境未变化,仅需优化'));
228
- else {
229
- shouldInstall = false, shouldOptimize = false, console.log(chalk.gray(' ✓ 依赖与环境未变化,跳过安装与优化'));
230
- await clearVerified(buildDir);
231
- return;
232
- }
233
- } else
234
- console.log(chalk.gray(' ℹ️ ' + (thisOptimize ? '重新安装依赖+优化' : '重新安装依赖') + '(原因: 缓存快照缺失)'));
200
+ fs.writeFileSync(bnmFile, hasNative ? '1' : '0');
201
+ if (hasNative) console.log(chalk.gray(' ℹ️ 检测到原生模块(binding.gyp)'));
235
202
 
236
- if (shouldInstall) {
237
- if (fs.existsSync(nodeModulesDir))
238
- console.log(chalk.cyan(' 清理旧 node_modules...')), fs.rmSync(nodeModulesDir, { recursive: true, force: true });
203
+ const pkg = JSON.parse(fs.readFileSync(path.join(targetDir, 'package.json'), 'utf-8')), deps = pkg.dependencies || {},
204
+ sortedDeps = Object.keys(deps).sort().reduce((acc, k) => { acc[k] = deps[k]; return acc; }, {}),
205
+ installerHash = createHash('sha256').update(fs.readFileSync(new URL(import.meta.url), 'utf-8'))
206
+ .digest('hex').slice(0, 16), snapshotData = { deps: sortedDeps, type: pkg.type || '', hasNative, forceRebuild };
239
207
 
240
- try { fs.rmSync(snapshotPath, { force: true }); } catch { }
241
- const env = { ...process.env };
242
- if (hasNative) env.NODEJS_MOBILE_BUILD_NATIVE_MODULES = '1';
243
- console.log(chalk.cyan(' 安装用户生产依赖(nodejs-assets/nodejs-project)...'));
244
- run('npm', ['install', '--omit=dev', '--no-audit', '--no-fund'], { cwd: targetDir, env, quiet: true });
245
- const snap = {
246
- ...snapshotData, platform: targetPlatform, abiFilters, runtimeMajor,
247
- optimizeHash: SELF_HASH, installerHash, optimized: false, verified: false
248
- };
249
- fs.writeFileSync(snapshotPath, JSON.stringify(snap, null, 2));
250
- console.log(chalk.green(' ✓ 用户依赖安装完成'));
208
+ let shouldInstall = true, shouldOptimize = thisOptimize, reasons = [];
209
+ if (fs.existsSync(snapshotPath)) {
210
+ const old = JSON.parse(fs.readFileSync(snapshotPath, 'utf-8')), verifiedOk = old.verified === true,
211
+ depsSame = JSON.stringify({
212
+ deps: old.deps, type: old.type, hasNative: old.hasNative, forceRebuild: old.forceRebuild
213
+ }) === JSON.stringify(snapshotData),
214
+ envSame = old.platform === targetPlatform && JSON.stringify(old.abiFilters) === JSON.stringify(abiFilters) &&
215
+ old.runtimeMajor === runtimeMajor,
216
+ lastOptimized = old.optimized !== false, selfHashSame = old.optimizeHash === SELF_HASH,
217
+ installerSame = old.installerHash === installerHash, nmExists = fs.existsSync(nodeModulesDir);
218
+ if (!depsSame) reasons.push('依赖清单已变化');
219
+ if (!verifiedOk) reasons.push('上次构建未完成');
220
+ if (!envSame) reasons.push('运行环境已变化');
221
+ if (!nmExists) reasons.push('缓存目录缺失');
222
+ if (lastOptimized && !thisOptimize) reasons.push('优化已由开启转为关闭');
223
+ if (thisOptimize && !selfHashSame && lastOptimized) reasons.push('优化程序已更新');
224
+ if (!installerSame) reasons.push('安装器已更新');
225
+ if (reasons.length > 0) {
226
+ shouldInstall = true, shouldOptimize = thisOptimize;
227
+ console.log(chalk.gray(' ℹ️ ' + (thisOptimize ? '重新安装依赖+优化' : '重新安装依赖')
228
+ + '(原因: ' + reasons.join(', ') + ')'));
229
+ }
230
+ else if (thisOptimize && !lastOptimized)
231
+ shouldInstall = false, shouldOptimize = true, console.log(chalk.gray(' ℹ️ 依赖与环境未变化,仅需优化'));
232
+ else {
233
+ shouldInstall = false, shouldOptimize = false, console.log(chalk.gray(' ✓ 依赖与环境未变化,跳过安装与优化'));
234
+ await clearVerified(buildDir);
235
+ return;
251
236
  }
237
+ }
238
+ else console.log(chalk.gray(' ℹ️ ' + (thisOptimize ? '重新安装依赖+优化' : '重新安装依赖') + '(原因: 缓存快照缺失)'));
252
239
 
253
- if (hasNative && targetPlatform === 'android' && shouldInstall)
254
- compileNativeModules(targetDir, userProjectDir, config, buildDir, abiFilters);
255
- if (shouldOptimize) {
256
- console.log(chalk.cyan(' [信息] 正在执行优化...'));
257
- try {
258
- await optimizeNodeModules(targetDir, {
259
- targetPlatform, abiFilters, runtimeMajor, exclude: optimizeCfg.exclude || [], snapshotDir: buildDir
260
- });
261
- } catch (err) {
262
- console.error(chalk.red(' [错误] node_modules 优化失败:' + err.message));
263
- console.error(chalk.red(' [提示] 可在 mobileAppConfig.js 中设置 optimize: { enabled: false } 关闭优化,'));
264
- console.error(chalk.red(' 或用 optimize: { exclude: ["包名"] } 排除问题包;')), process.exit(1);
265
- }
240
+ if (shouldInstall) {
241
+ if (fs.existsSync(nodeModulesDir))
242
+ console.log(chalk.cyan(' 清理旧 node_modules...')), fs.rmSync(nodeModulesDir, { recursive: true, force: true });
243
+
244
+ try { fs.rmSync(snapshotPath, { force: true }); } catch { }
245
+ const env = { ...process.env };
246
+ if (hasNative) env.NODEJS_MOBILE_BUILD_NATIVE_MODULES = '1';
247
+ console.log(chalk.cyan(' 安装用户生产依赖(nodejs-assets/nodejs-project)...'));
248
+ run('npm', ['install', '--omit=dev', '--no-audit', '--no-fund'], { cwd: targetDir, env, quiet: true });
249
+ const snap = {
250
+ ...snapshotData, platform: targetPlatform, abiFilters, runtimeMajor,
251
+ optimizeHash: SELF_HASH, installerHash, optimized: false, verified: false
252
+ };
253
+ fs.writeFileSync(snapshotPath, JSON.stringify(snap, null, 2));
254
+ console.log(chalk.green(' ✓ 用户依赖安装完成'));
255
+ }
256
+
257
+ if (hasNative && targetPlatform === 'android' && shouldInstall)
258
+ compileNativeModules(targetDir, userProjectDir, config, buildDir, abiFilters);
259
+ if (shouldOptimize) {
260
+ console.log(chalk.cyan(' [信息] 正在执行优化...'));
261
+ try {
262
+ await optimizeNodeModules(targetDir, {
263
+ targetPlatform, abiFilters, runtimeMajor, exclude: optimizeCfg.exclude || [], snapshotDir: buildDir
264
+ });
265
+ } catch (err) {
266
+ console.error(chalk.red(' [错误] node_modules 优化失败:' + err.message));
267
+ console.error(chalk.red(' [提示] 可在 mobileAppConfig.js 中设置 optimize: { enabled: false } 关闭优化,'));
268
+ console.error(chalk.red(' 或用 optimize: { exclude: ["包名"] } 排除问题包;')), process.exit(1);
266
269
  }
267
- },
268
- /**
269
- * 修改 @flun/nodejs-mobile-react-native 插件 build.gradle 的 abiFilters
270
- */
271
- patchNodejsMobilePlugin = (userProjectDir, config) => {
272
- const pluginDir = findPluginDir(userProjectDir);
273
- if (!pluginDir) return console.warn(chalk.yellow(' ⚠️ 未找到 @flun/nodejs-mobile-react-native 插件,跳过 patch'));
270
+ }
271
+ };
274
272
 
275
- const file = path.join(pluginDir, 'android', 'build.gradle'),
276
- abiList = config.android.abiFilters.map((x) => `"${x}"`).join(', ');
273
+ /**
274
+ * 修改 @flun/nodejs-mobile-react-native 插件 build.gradle 的 abiFilters
275
+ * >查看定义:@see {@link patchNodejsMobilePlugin}
276
+ * @param {string} userProjectDir 用户项目目录
277
+ * @param {object} config 用户配置
278
+ * @returns {void}
279
+ */
280
+ const patchNodejsMobilePlugin = (userProjectDir, config) => {
281
+ const pluginDir = findPluginDir(userProjectDir);
282
+ if (!pluginDir) return console.warn(chalk.yellow(' ⚠️ 未找到 @flun/nodejs-mobile-react-native 插件,跳过 patch'));
277
283
 
278
- let c = fs.readFileSync(file, 'utf-8');
279
- const before = c;
280
- c = c.replace(/abiFilters\s*=\s*\[[^\]]*\]/, `abiFilters = [${abiList}]`);
284
+ const file = path.join(pluginDir, 'android', 'build.gradle'),
285
+ abiList = config.android.abiFilters.map((x) => `"${x}"`).join(', ');
281
286
 
282
- if (c !== before) fs.writeFileSync(file, c, 'utf-8'),
283
- console.log(chalk.green(` ✓ 已设置插件 abiFilters = [${abiList}]`));
284
- },
285
- /**
286
- * 让 react-native-webview 对 localhost 自签名证书放行
287
- * Android: patch RNCWebViewClient.java
288
- * iOS: patch RNCWebViewImpl.m
289
- * 仅在 config.webview.allowSelfSignedCert === true 时应用
290
- * 每次 test/build 都检查(npm 重装 webview 后会覆盖补丁)
291
- */
292
- patchWebViewSsl = (userProjectDir, config) => {
293
- if (!config.webview || config.webview.allowSelfSignedCert !== true) return;
287
+ let c = fs.readFileSync(file, 'utf-8');
288
+ const before = c;
289
+ c = c.replace(/abiFilters\s*=\s*\[[^\]]*\]/, `abiFilters = [${abiList}]`);
290
+
291
+ if (c !== before)
292
+ fs.writeFileSync(file, c, 'utf-8'), console.log(chalk.green(` ✓ 已设置插件 abiFilters = [${abiList}]`));
293
+ };
294
+
295
+ /**
296
+ * 让 react-native-webview 对 localhost 自签名证书放行
297
+ * Android: patch RNCWebViewClient.java
298
+ * iOS: patch RNCWebViewImpl.m
299
+ * 仅在 config.webview.allowSelfSignedCert === true 时应用
300
+ * 每次 test/build 都检查(npm 重装 webview 后会覆盖补丁)
301
+ * >查看定义:@see {@link patchWebViewSsl}
302
+ * @param {string} userProjectDir 用户项目目录
303
+ * @param {object} config 用户配置
304
+ * @returns {void}
305
+ */
306
+ const patchWebViewSsl = (userProjectDir, config) => {
307
+ if (!config.webview || config.webview.allowSelfSignedCert !== true) return;
294
308
 
295
- const file = path.join(
296
- userProjectDir, 'node_modules', 'react-native-webview', 'android', 'src', 'main', 'java', 'com',
297
- 'reactnativecommunity', 'webview', 'RNCWebViewClient.java'
298
- );
309
+ const file = path.join(
310
+ userProjectDir, 'node_modules', 'react-native-webview', 'android', 'src', 'main', 'java', 'com',
311
+ 'reactnativecommunity', 'webview', 'RNCWebViewClient.java'
312
+ );
299
313
 
300
- if (fs.existsSync(file)) {
301
- let c = fs.readFileSync(file, 'utf-8');
302
- if (c.includes('LOCALHOST_SSL_BYPASS')) console.log(chalk.green(' ✓ Android WebView SSL 补丁已应用'));
303
- else {
304
- const old = 'handler.cancel();', methodIndex = c.indexOf('onReceivedSslError');
305
- if (methodIndex !== -1) {
306
- const cancelIndex = c.indexOf(old, methodIndex);
307
- if (cancelIndex !== -1) {
308
- const newCode = [
309
- '// LOCALHOST_SSL_BYPASS: 对 localhost/127.0.0.1 放行自签名证书(仅本地测试用)',
310
- ' if (failingUrl != null',
311
- ' && (failingUrl.startsWith("https://127.0.0.1") || ' +
312
- ' failingUrl.startsWith("https://localhost"))) {',
313
- ' handler.proceed();',
314
- ' return;',
315
- ' }',
316
- ' handler.cancel();',
317
- ].join('\n');
318
- c = c.substring(0, cancelIndex) + newCode + c.substring(cancelIndex + old.length);
319
- fs.writeFileSync(file, c, 'utf-8');
320
- console.log(chalk.green(' ✓ 已应用 Android WebView SSL 补丁(允许 localhost 自签证书)'));
321
- }
314
+ if (fs.existsSync(file)) {
315
+ let c = fs.readFileSync(file, 'utf-8');
316
+ if (c.includes('LOCALHOST_SSL_BYPASS')) console.log(chalk.green(' ✓ Android WebView SSL 补丁已应用'));
317
+ else {
318
+ const old = 'handler.cancel();', methodIndex = c.indexOf('onReceivedSslError');
319
+ if (methodIndex !== -1) {
320
+ const cancelIndex = c.indexOf(old, methodIndex);
321
+ if (cancelIndex !== -1) {
322
+ const newCode = [
323
+ '// LOCALHOST_SSL_BYPASS: 对 localhost/127.0.0.1 放行自签名证书(仅本地测试用)',
324
+ ' if (failingUrl != null',
325
+ ' && (failingUrl.startsWith("https://127.0.0.1") || ' +
326
+ ' failingUrl.startsWith("https://localhost"))) {',
327
+ ' handler.proceed();',
328
+ ' return;',
329
+ ' }',
330
+ ' handler.cancel();',
331
+ ].join('\n');
332
+ c = c.substring(0, cancelIndex) + newCode + c.substring(cancelIndex + old.length);
333
+ fs.writeFileSync(file, c, 'utf-8');
334
+ console.log(chalk.green(' ✓ 已应用 Android WebView SSL 补丁(允许 localhost 自签证书)'));
322
335
  }
323
336
  }
324
337
  }
325
- patchWebViewSslIos(userProjectDir); // iOS 分支
326
- };
338
+ }
339
+ patchWebViewSslIos(userProjectDir); // iOS 分支
340
+ };
327
341
 
328
342
  export { installUserDeps, patchNodejsMobilePlugin, patchWebViewSsl };
@@ -1,7 +1,7 @@
1
+ import chalk from 'chalk';
1
2
  import fs from 'fs';
2
- import path from 'path';
3
3
  import { createRequire } from 'module';
4
- import chalk from 'chalk';
4
+ import path from 'path';
5
5
 
6
6
  const require = createRequire(import.meta.url), subpath = (...parts) => './' + path.posix.join(...parts),
7
7
  BUNDLED_DIR = 'bundled', NM_DIR = 'node_modules', PKG_JSON = 'package.json',
@@ -491,8 +491,9 @@ const require = createRequire(import.meta.url), subpath = (...parts) => './' + p
491
491
  const platBad = plat && !myPlat.includes(plat), archBad = plat && arch && !myArch.includes(arch);
492
492
  if (platBad || archBad) {
493
493
  if (process.env.OPTIMIZE_DEBUG) {
494
- const rel = toPosix(path.relative(nmDir, full));
495
- console.log(chalk.gray(` ✓ ${rel} (${platBad ? '平台不符: ' + plat : '架构不符: ' + arch})`));
494
+ const rel = toPosix(path.relative(nmDir, full)),
495
+ why = platBad ? '平台不符: ' + plat : '架构不符: ' + arch;
496
+ console.log(chalk.gray(` ✓ ${rel} (${why})`));
496
497
  }
497
498
  delFile(full);
498
499
  }