From d51d3c934d6f0d5a8d00216d2c0b54c702cc07c9 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Thu, 25 Jun 2026 18:45:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(common/metro):=20.xuqmconfig=20=E5=8D=95?= =?UTF-8?q?=E7=82=B9=E6=94=BE=E7=BD=AE=E2=80=94=E2=80=94=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=88=B0=E5=8E=9F=E7=94=9F=20Android/iOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 宿主只需在 RN 侧放置一份 .xuqmconfig;构建/运行时 withXuqmConfig 自动比对并 复制到 android assets/config(供 XuqmMergedProvider 自动初始化)及 iOS 资源目录, 缺失或内容不一致才写入(幂等)。免去 RN/Android/iOS 三处分别放置。 Co-Authored-By: Claude --- packages/common/metro/index.js | 55 ++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/packages/common/metro/index.js b/packages/common/metro/index.js index 43509de..e4647bf 100644 --- a/packages/common/metro/index.js +++ b/packages/common/metro/index.js @@ -58,6 +58,58 @@ function applyBugCollect(metroConfig) { } } +/** 写入目标文件(仅在缺失或内容不一致时),返回是否发生写入。 */ +function syncFile(content, destPath, label) { + try { + if (fs.existsSync(destPath) && fs.readFileSync(destPath, 'utf-8').trim() === content) { + return false; + } + fs.mkdirSync(path.dirname(destPath), { recursive: true }); + fs.writeFileSync(destPath, content, 'utf-8'); + console.log(`[XuqmConfig] 已同步 .xuqmconfig → ${label}`); + return true; + } catch (e) { + console.warn(`[XuqmConfig] 同步 .xuqmconfig 到 ${label} 失败:${e.message}`); + return false; + } +} + +/** + * 单点放置:宿主只在 RN 侧放置一份 .xuqmconfig,构建/运行时自动同步到 + * 原生位置(Android assets/config,供 XuqmMergedProvider 自动初始化;iOS 资源目录)。 + * 缺失或内容不一致才复制,幂等。 + */ +function syncNativeConfig(projectRoot, content) { + const fileName = 'config.xuqmconfig'; + + // Android:原生 ContentProvider 扫描 assets/config 下的 .xuqmconfig + if (fs.existsSync(path.resolve(projectRoot, 'android'))) { + syncFile( + content, + path.resolve(projectRoot, 'android/app/src/main/assets/config', fileName), + 'android assets/config', + ); + } + + // iOS:复制到 app 资源目录(需在 Xcode「Copy Bundle Resources」中引用一次) + const iosDir = path.resolve(projectRoot, 'ios'); + if (fs.existsSync(iosDir)) { + try { + const appDir = fs + .readdirSync(iosDir, { withFileTypes: true }) + .filter( + (d) => d.isDirectory() && fs.existsSync(path.join(iosDir, d.name, 'Info.plist')), + ) + .map((d) => d.name)[0]; + if (appDir) { + syncFile(content, path.join(iosDir, appDir, 'config', fileName), `ios ${appDir}/config`); + } + } catch { + // 忽略 iOS 同步异常,不阻断打包 + } + } +} + function withXuqmConfig(metroConfig) { const projectRoot = metroConfig.projectRoot ?? process.cwd(); const configFile = findConfigFile(projectRoot); @@ -70,6 +122,9 @@ function withXuqmConfig(metroConfig) { // 读取加密配置内容 const encryptedContent = fs.readFileSync(configFile, 'utf-8').trim(); + // 单点放置:自动同步到原生 Android/iOS(缺失或不一致才复制) + syncNativeConfig(projectRoot, encryptedContent); + // 生成临时 TS 文件作为虚拟模块 const tmpDir = path.join(os.tmpdir(), 'xuqm-sdk-config'); fs.mkdirSync(tmpDir, { recursive: true });