feat(common): Metro 插件自动发现 .xuqmconfig 配置文件
新增 withXuqmConfig(metroConfig) Metro 插件: - 扫描 src/assets/config/ 下的 *.xuqmconfig 文件 - 通过 resolveRequest 拦截 @xuqm/autoinit-config 模块 - 宿主只需放入下载的 .xuqmconfig 文件,无需重命名 对齐 Android SDK 的 ConfigFileReader 逻辑。 Co-Authored-By: Claude <noreply@anthropic.com>
这个提交包含在:
父节点
c7f1f7cb72
当前提交
4f5739da1f
96
packages/common/metro/index.js
普通文件
96
packages/common/metro/index.js
普通文件
@ -0,0 +1,96 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Metro 插件:自动发现 .xuqmconfig 配置文件。
|
||||
*
|
||||
* 宿主只需将平台下载的 .xuqmconfig 文件放入 src/assets/config/,
|
||||
* SDK 自动读取并完成初始化,无需重命名或创建 .ts 包装文件。
|
||||
*
|
||||
* 对齐 Android SDK 的 ConfigFileReader 逻辑:
|
||||
* 优先 config.xuqmconfig,否则取第一个 *.xuqmconfig。
|
||||
*
|
||||
* @param {import('@react-native/metro-config').MetroConfig} metroConfig
|
||||
* @returns {import('@react-native/metro-config').MetroConfig}
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
|
||||
const VIRTUAL_MODULE_ID = '@xuqm/autoinit-config';
|
||||
const CONFIG_DIR_CANDIDATES = [
|
||||
'src/assets/config',
|
||||
'src/assets/xuqm',
|
||||
'assets/config',
|
||||
'assets/xuqm',
|
||||
];
|
||||
|
||||
function findConfigFile(projectRoot) {
|
||||
for (const dir of CONFIG_DIR_CANDIDATES) {
|
||||
const absDir = path.resolve(projectRoot, dir);
|
||||
if (!fs.existsSync(absDir)) continue;
|
||||
|
||||
const files = fs.readdirSync(absDir);
|
||||
// 优先 config.xuqmconfig 或 config.xuqm
|
||||
const preferred = files.find(
|
||||
(f) => f === 'config.xuqmconfig' || f === 'config.xuqm',
|
||||
);
|
||||
if (preferred) {
|
||||
return path.join(absDir, preferred);
|
||||
}
|
||||
// 否则取第一个 .xuqmconfig
|
||||
const fallback = files.find((f) => f.endsWith('.xuqmconfig'));
|
||||
if (fallback) {
|
||||
return path.join(absDir, fallback);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function withXuqmConfig(metroConfig) {
|
||||
const projectRoot = metroConfig.projectRoot ?? process.cwd();
|
||||
const configFile = findConfigFile(projectRoot);
|
||||
|
||||
if (!configFile) {
|
||||
// 未找到配置文件,返回原配置(autoInit 会静默跳过)
|
||||
return metroConfig;
|
||||
}
|
||||
|
||||
// 读取加密配置内容
|
||||
const encryptedContent = fs.readFileSync(configFile, 'utf-8').trim();
|
||||
|
||||
// 生成临时 TS 文件作为虚拟模块
|
||||
const tmpDir = path.join(os.tmpdir(), 'xuqm-sdk-config');
|
||||
fs.mkdirSync(tmpDir, { recursive: true });
|
||||
const virtualFile = path.join(tmpDir, 'autoinit-config.ts');
|
||||
fs.writeFileSync(
|
||||
virtualFile,
|
||||
`// Auto-generated by @xuqm/rn-common/metro — do not edit\nexport const ENCRYPTED_CONFIG = ${JSON.stringify(encryptedContent)};\n`,
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
// 拦截模块解析,将 @xuqm/autoinit-config 指向虚拟文件
|
||||
const existingResolveRequest = metroConfig.resolver?.resolveRequest;
|
||||
|
||||
return {
|
||||
...metroConfig,
|
||||
resolver: {
|
||||
...metroConfig.resolver,
|
||||
resolveRequest: (context, moduleName, platform) => {
|
||||
if (moduleName === VIRTUAL_MODULE_ID) {
|
||||
return {
|
||||
type: 'sourceFile',
|
||||
filePath: virtualFile,
|
||||
};
|
||||
}
|
||||
|
||||
if (existingResolveRequest) {
|
||||
return existingResolveRequest(context, moduleName, platform);
|
||||
}
|
||||
|
||||
return context.resolveRequest(context, moduleName, platform);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { withXuqmConfig };
|
||||
@ -6,8 +6,10 @@
|
||||
"main": "src/index.ts",
|
||||
"react-native": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"metro": "metro/index.js",
|
||||
"files": [
|
||||
"src"
|
||||
"src",
|
||||
"metro"
|
||||
],
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
|
||||
@ -3,13 +3,16 @@
|
||||
*
|
||||
* 对齐 Android SDK 的 ContentProvider 模式。
|
||||
*
|
||||
* React Native 没有 ContentProvider,所以用 Metro moduleNameMapper 桥接:
|
||||
* 1. 宿主把加密配置放到 src/assets/xuqm/config.xuqmconfig.ts
|
||||
* 2. 宿主 babel.config.js 添加 alias: '@xuqm/autoinit-config' → 该文件
|
||||
* 推荐方式(withXuqmConfig Metro 插件):
|
||||
* 1. 宿主将 .xuqmconfig 文件放入 src/assets/config/
|
||||
* 2. metro.config.js 使用 withXuqmConfig(metroConfig) 包装
|
||||
* 3. 本模块 tryRequire('@xuqm/autoinit-config') 自动读取并初始化
|
||||
* 4. 宿主代码零初始化调用
|
||||
*
|
||||
* 如果 alias 未配置(require 失败),静默跳过,不崩溃。
|
||||
* 兼容方式(手动 alias):
|
||||
* 1. 宿主把加密配置放到 src/assets/xuqm/config.xuqmconfig.ts
|
||||
* 2. 宿主 babel/metro config 添加 alias: '@xuqm/autoinit-config' → 该文件
|
||||
*
|
||||
* 如果配置未找到(require 失败),静默跳过,不崩溃。
|
||||
*/
|
||||
|
||||
import {isInitialized} from './config'
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户