miaoda-expo-native-capabilities
v0.2.2
Published
Generate and compare Expo Development Build native capability manifests
Readme
miaoda-expo-native-capabilities
用于生成和比较 Expo Development Build 原生能力 manifest 的 Package。
它可以从当前 Expo 项目中提取 Expo Module、React Native 原生模块、平台原生模块名称、运行时配置以及自定义原生模块源码 hash,并判断当前项目所需能力是否是某个 Development Build 已提供能力的子集。
核心关系
比较方向是:
当前项目所需能力(required)⊆ Development Build 提供能力(provided)因此:
provided中多出的模块允许存在。required中缺失的模块、原生模块名称、核心能力会判定为不兼容。- 版本、运行时配置或自定义模块源码 hash 不一致会报告具体冲突。
- 自定义模块缺少源码 hash 时,结果为
unknown,不会被当作兼容。
版本宽容集合
定制 Development Client 四件套(expo-dev-client、expo-dev-launcher、expo-dev-menu、expo-dev-menu-interface)默认在版本宽容集合
VERSION_TOLERANT_PACKAGE_NAMES 中:这些包只比较存在性与注册名称,不比较版本新旧。
理由:四件套是开发工具而非业务能力,版本差异只体现为开发 UI 的新旧(旧客户端由 JS
侧接口探测自动回退旧 UI);真正需要拦截的 SDK 层不兼容由 runtime.expoVersion 等
运行时比对承担。这样服务端更新四件套钉版后,装着旧版本的设备不会被判
version-conflict 弹「建议重新构建开发客户端」打扰用户。
宽容只作用于版本:四件套整个缺失(该构建不是定制 Development Build)或注册名称
缺失仍正常拦截。调用方可通过 compareExpoNativeCapabilities 的第三个参数覆盖集合
(传空集合恢复严格模式):
import {
compareExpoNativeCapabilities,
VERSION_TOLERANT_PACKAGE_NAMES,
} from 'miaoda-expo-native-capabilities';
// 扩展宽容范围
const result = compareExpoNativeCapabilities(required, provided, {
versionTolerantPackages: new Set([...VERSION_TOLERANT_PACKAGE_NAMES, 'my-dev-tool']),
});
// 恢复对所有包的严格版本比较
const strict = compareExpoNativeCapabilities(required, provided, {
versionTolerantPackages: new Set(),
});依赖与分析器
Package 不内置 Expo SDK,也不依赖全局 Expo 安装。能力提取时会从目标项目解析实际使用的:
expo-modules-autolinking@expo/configexporeact-native
目标项目需要能够解析这些 Package。默认情况下,分析器版本跟随目标项目实际参与构建的版本,以保证提取结果和原生构建行为一致。
不同 Expo SDK 的 autolinking 发现规则和 JSON 输出可能发生变化。不能仅凭更新分析器版本就断言它一定兼容旧 SDK;如果要统一使用较新的分析器,应先用真实的 Expo 55、57 等项目 fixture 验证。
Import API
推荐在 Node.js 或服务端代码中直接 Import 使用:
import {
compareExpoNativeCapabilities,
createExpoNativeCapabilitiesAsync,
} from 'miaoda-expo-native-capabilities';
// 生成当前项目需要的原生能力
const required = await createExpoNativeCapabilitiesAsync(projectRoot, {
platform: 'android',
});
// provided 是 Development Build 构建时保存的 manifest
const result = compareExpoNativeCapabilities(required, buildManifest);
if (result.compatible) {
console.log('Development Build 可以继续使用');
} else {
console.log('Development Build 不兼容', result);
}compareExpoNativeCapabilities 接收两份能力数据和一个可选的配置参数:
| 参数 | 含义 |
| --- | --- |
| required | 当前项目运行所需的能力,通常由当前源码实时生成 |
| provided | Development Build 编译时保存的能力,通常从构建元数据读取 |
| options | 可选行为配置;versionTolerantPackages 覆盖版本宽容集合(默认为 VERSION_TOLERANT_PACKAGE_NAMES,见上文「版本宽容集合」) |
返回值 ExpoNativeCompatibilityResult 包含:
compatible:是否兼容。只有没有任何缺失或冲突时才为true。status:compatible、incompatible或unknown。missingModules:缺失的 Package。missingNativeModuleNames:缺失的平台原生模块名称。missingCoreFeatures:缺失的 Expo 核心能力。versionConflicts:Package 版本冲突(版本宽容集合中的包不会出现在这里)。customModuleConflicts:自定义模块来源或源码 hash 冲突。runtimeConflicts:Expo、React Native、JS Engine、New Architecture 或平台冲突。reasons:每条不兼容原因的结构化双语提示,包含稳定的code、messageZh、messageEn,以及可选的packageName。
调用方可以根据用户语言选择 messageZh 或 messageEn,不要根据提示文本判断原因,应使用 code。
生成 manifest
应在编译 Development Build 的同一个源码 checkout 中生成 manifest,并将它和构建元数据一起保存:
expo-native-capabilities generate --root . --platform android --output development-build.android.json
expo-native-capabilities generate --root . --platform ios --output development-build.ios.jsonmanifest 按平台分别生成。Android 和 iOS 的原生能力不能混用,应分别保存和比较。
Package 不会在编译完成后读取 APK 或 IPA 来反向推断能力。必须在原生编译之前生成 manifest,这样自定义模块的源码 hash 才对应实际被编译的源码。
比较两份 manifest
如果已经有两份 manifest,可以直接通过 Import API 比较:
const result = compareExpoNativeCapabilities(requiredManifest, providedManifest);也可以使用 CLI:
expo-native-capabilities check \
--required current-project.android.json \
--provided development-build.android.jsonCLI 会输出 JSON 格式的比较结果,适合 CI、构建脚本或构建元数据服务使用。
自定义原生模块
模块 Package 根目录位于 node_modules 之外时,会被识别为 custom 模块,并计算确定性的 SHA-256 源码 hash。这样可以识别:
- 当前项目新增了原生模块。
- 自定义模块的 Android 或 iOS 原生实现发生变化。
- Development Build 中编译的自定义模块源码与当前源码不一致。
如果自定义模块通过 workspace 或 symlink 引入,Package 会使用 realpath 判断真实位置,避免把本地源码误判成普通依赖。
Dev Server HTTP API
仓库中的 Expo Dev Server 提供以下接口:
读取当前项目能力
GET /innerapi/v1/sourcecode/expo/native-capabilities接口会重新计算当前项目的 Android 和 iOS manifest,返回当前源码的原生能力。
检查 Development Build 是否兼容
POST /innerapi/v1/sourcecode/expo/native-capabilities/check请求体可以直接传入一份 provided manifest,也可以传入:
{
"provided": {
"schemaVersion": 1,
"platform": "android",
"runtime": {
"expoVersion": "55.0.0",
"reactNativeVersion": "0.83.0",
"jsEngine": "hermes",
"newArchitecture": true
},
"modules": [],
"coreFeatures": []
}
}服务端会实时生成当前项目的 required manifest,再和传入的 provided manifest 比较,并在 data.result 中返回兼容关系和所有冲突详情。
当前边界
能力比较依赖规范化后的 manifest schema,不依赖 Expo SDK 的具体版本。能力提取仍然使用目标项目自己的 Expo 配置和 autolinking 版本。
当前方案重点覆盖 autolinking 原生模块、运行时配置、核心能力和自定义模块源码 hash;它不是完整的 @expo/fingerprint,尚未覆盖所有 app-level config plugin、权限声明或任意原生工程文件变化。
