XuqmGroup-RNSDK/packages/update/metro/index.js

104 行
3.4 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)
// processModuleFilter 会在 createModuleIdFactory 分配编号之后执行。必须冻结进入
// 当前 bundle 之前已经存在的共享模块;否则 common 新分配的模块刚写入缓存,随即
// 又会被当作 startup 已提供模块过滤,生成只含 require(entryId) 的无效空包。
const inheritedSharedModules = new Set(Object.keys(sharedCache))
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 !inheritedSharedModules.has(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,
}