XuqmGroup-RNSDK/packages/update/metro/index.js
2026-07-18 08:59:40 +08:00

100 行
3.0 KiB
JavaScript

const fs = require('node:fs')
const path = require('node:path')
const { withXuqmConfig } = require('@xuqm/rn-common/metro')
const MODULE_RANGE_SIZE = 10_000_000
function readCache(file) {
if (!fs.existsSync(file)) return {}
try {
return JSON.parse(fs.readFileSync(file, 'utf8'))
} catch {
return {}
}
}
function writeCache(file, value) {
fs.mkdirSync(path.dirname(file), { recursive: true })
fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`)
}
function hasModule(cache, modulePath) {
return Object.prototype.hasOwnProperty.call(cache, modulePath)
}
function isRuntimePrelude(modulePath) {
return modulePath.includes('__prelude__') || modulePath.includes('polyfills')
}
function createModuleSerializer(environment = process.env) {
const moduleId = environment.XUQM_MODULE_ID
const moduleType = environment.XUQM_MODULE_TYPE
if (!moduleId || !moduleType) return null
const cacheFile = path.resolve(
environment.XUQM_MODULE_CACHE_FILE ?? '.xuqm-cache/common-module-ids.json',
)
if (environment.XUQM_RESET_MODULE_CACHE === 'true') writeCache(cacheFile, {})
const sharedCache = readCache(cacheFile)
const writesSharedCache = moduleType === 'startup' || moduleType === 'common'
const moduleIndex = Number.parseInt(environment.XUQM_MODULE_INDEX ?? '0', 10)
let nextId = writesSharedCache
? Object.values(sharedCache).reduce((maximum, value) => Math.max(maximum, Number(value)), -1) +
1
: (Number.isFinite(moduleIndex) && moduleIndex >= 0 ? moduleIndex + 1 : 1) * MODULE_RANGE_SIZE
const localIds = new Map()
return {
createModuleIdFactory() {
return modulePath => {
if (hasModule(sharedCache, modulePath)) return sharedCache[modulePath]
const existing = localIds.get(modulePath)
if (existing !== undefined) return existing
const id = nextId++
localIds.set(modulePath, id)
if (writesSharedCache) {
sharedCache[modulePath] = id
writeCache(cacheFile, sharedCache)
}
return id
}
},
processModuleFilter(module) {
if (moduleType === 'startup') return true
if (isRuntimePrelude(module.path)) return false
return !hasModule(sharedCache, module.path)
},
}
}
/**
* 同一份 Metro 配置同时服务日常开发和插件构建。CLI 构建插件时注入模块上下文;
* 普通 `pnpm android/start` 没有这些变量,因此保持标准 React Native 行为。
*/
function withXuqmModuleConfig(metroConfig) {
const configured = withXuqmConfig(metroConfig)
const moduleSerializer = createModuleSerializer()
if (!moduleSerializer) return configured
const configuredFilter = configured.serializer?.processModuleFilter
return {
...configured,
serializer: {
...configured.serializer,
...moduleSerializer,
processModuleFilter(module) {
return (
(configuredFilter ? configuredFilter(module) : true) &&
moduleSerializer.processModuleFilter(module)
)
},
},
}
}
module.exports = {
createModuleSerializer,
withXuqmModuleConfig,
}