expo-migrate-module

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Migrate an Expo Module

迁移Expo模块

Migrate the Swift side of an existing Expo module without changing its observable JS API. Treat the current JS/TypeScript surface and tests as the compatibility contract. Leave Kotlin on the 1.0 DSL unless the user explicitly expands the task.
在不改变其可观察JS API的前提下,迁移现有Expo模块的Swift端。将当前JS/TypeScript接口和测试视为兼容性契约。除非用户明确扩展任务,否则保留Kotlin端使用1.0 DSL。

Prerequisite

前提条件

The Expo Modules API 2.0 macros require
expo
57.0.7
or newer. Before editing, check the target's installed version (
expo
in
package.json
/lockfile, or
npm ls expo
). If it is older, stop and tell the user to upgrade first; do not attempt the migration against an unsupported version. This is a floor, not a guarantee: the exact macro and core surface still varies within
57.x
, so step 2 must still verify the checked-out source.
Expo Modules API 2.0宏要求
expo
版本为
57.0.7
或更高。在编辑之前,请检查目标项目的已安装版本(查看
package.json
/锁定文件中的
expo
,或运行
npm ls expo
)。如果版本低于该要求,请停止操作并告知用户先进行升级;不要尝试在不支持的版本上进行迁移。这是最低要求而非绝对保证:
57.x
版本内的宏和核心接口仍可能存在差异,因此步骤2仍需验证已检出的源代码。

References

参考资料

  • Read
    references/migration-map.md
    before changing source. It contains the 1.0-to-2.0 mappings, semantic traps, and mixed-mode rules.
  • Read
    references/example.md
    for a full before/after walkthrough of one module through mixed mode to a complete migration. Consult it when you need to see how the per-member rules compose.
  • Read
    references/compatibility.md
    when the checked-out
    expo-modules-core
    version or branch is not known to support every requested macro. It explains how to verify the actual compile-time and runtime surface instead of guessing from an SDK number.
  • 在修改源代码前,请阅读
    references/migration-map.md
    。其中包含1.0到2.0的映射关系、语义陷阱和混合模式规则。
  • 阅读
    references/example.md
    ,了解一个模块从混合模式到完全迁移的完整前后对比流程。当需要了解各成员规则如何组合时,请参考该文档。
  • 当检出的
    expo-modules-core
    版本或分支不支持所有请求的宏时,请阅读
    references/compatibility.md
    。它解释了如何验证实际的编译时和运行时接口,而非仅通过SDK版本猜测。

Workflow

工作流程

1. Establish the contract

1. 确立契约

Inspect repository instructions and the worktree before editing. Locate the Swift module classes, records, shared objects, native views, JS/TS bindings, tests, example app, podspec, and installed or checked-out
expo-modules-core
.
Inventory every exported item before rewriting it:
  • module and shared-object JS names
  • function names, arity, labels, defaults, nullability, sync/async behavior, errors, and queue semantics
  • property names, mutability, and constant caching behavior
  • event wire names and payload shapes
  • record field names, defaults, requiredness, and nullability
  • shared-object constructors and instance/static placement
  • lifecycle hooks and views
Use the TypeScript declarations and JS call sites to resolve ambiguity. Do not silently "improve" requiredness, rename an event, or change sync behavior during a syntax migration.
在编辑前检查仓库说明和工作目录。定位Swift模块类、记录、共享对象、原生视图、JS/TS绑定、测试、示例应用、podspec以及已安装或检出的
expo-modules-core
在重写前盘点所有导出项:
  • 模块和共享对象的JS名称
  • 函数名称、参数数量、标签、默认值、可空性、同步/异步行为、错误处理和队列语义
  • 属性名称、可变性和常量缓存行为
  • 事件名称和负载结构
  • 记录字段名称、默认值、必填性和可空性
  • 共享对象构造函数以及实例/静态方法的位置
  • 生命周期钩子和视图
使用TypeScript声明和JS调用站点解决歧义。在语法迁移过程中,请勿擅自“改进”必填性、重命名事件或更改同步行为。

2. Verify the available 2.0 surface

2. 验证可用的2.0接口

Inspect the macro declarations and matching core hooks in the dependency actually used by the target. Do not assume that all items in the 2.0 design are present because one macro compiles.
Classify each 1.0 item as:
  • Migrate: both its macro and required core runtime support exist.
  • Keep in DSL: mixed mode preserves it safely, or 2.0 lacks an equivalent.
  • Blocked: migration would alter the JS contract or requires unavailable runtime support.
Prefer an incremental mixed-mode result over speculative generated code. Keep
definition()
for any remaining DSL elements; delete it only when it is empty and the resolved module name is preserved by
@ExpoModule
.
检查目标项目实际使用的依赖中的宏声明和匹配的核心钩子。不要因为某个宏可以编译就假设2.0设计中的所有功能都已存在。
将每个1.0项分类为:
  • 迁移: 其宏和所需的核心运行时支持均已存在。
  • 保留在DSL中: 混合模式可安全保留该功能,或2.0缺乏等效功能。
  • 受阻: 迁移会改变JS契约,或需要不可用的运行时支持。
优先选择增量混合模式结果,而非推测性生成的代码。对任何剩余的DSL元素保留
definition()
;仅当
definition()
为空且
@ExpoModule
保留了解析后的模块名称时,才删除它。

3. Apply the migration

3. 执行迁移

Migrate one semantic group at a time: module naming, functions, properties/constants, events, shared objects, then records. Keep the diff narrow.
Follow these invariants:
  • Preserve every existing JS-visible name explicitly when Swift naming rules or macro defaults differ.
  • Keep original optional/default behavior. An optional 1.0 record field must not become required merely because 2.0 can express required fields.
  • Do not migrate same-JS-name overloads unless the checked-out macro groups and dispatches them.
  • Do not migrate queue-pinned DSL functions as-is; restructure onto Swift Concurrency or dispatch to the original queue via a continuation, per the async-function rules in
    references/migration-map.md
    .
  • Do not migrate views, unions, synchronous events, or shared-object static functions without verified support.
  • Do not change Kotlin, JS wrappers, or public
    .d.ts
    files unless the user requested an API change.
After each group, search for old DSL entries and call sites that should have moved. Avoid broad formatting or unrelated cleanup.
一次迁移一个语义组:模块命名、函数、属性/常量、事件、共享对象,然后是记录。保持差异范围尽可能小。
遵循以下不变规则:
  • 当Swift命名规则或宏默认值不同时,显式保留所有现有JS可见名称。
  • 保留原始的可选/默认行为。1.0中的可选记录字段不得仅因为2.0可以表达必填字段就变为必填。
  • 除非检出的宏支持分组和调度同名JS重载,否则不要迁移这些重载。
  • 不要按原样迁移固定队列的DSL函数;根据
    references/migration-map.md
    中的异步函数规则,重构为Swift并发模型,或通过延续派发到原始队列。
  • 在未验证支持的情况下,不要迁移视图、联合类型、同步事件或共享对象静态函数。
  • 除非用户请求API更改,否则不要修改Kotlin、JS包装器或公共
    .d.ts
    文件。
每个组迁移完成后,搜索应已迁移的旧DSL条目和调用站点。避免进行大范围格式调整或无关的清理工作。

When a 2.0 equivalent is missing or a group fails

当2.0等效功能缺失或某组迁移失败时

When step 2 classified an item as Blocked, or a migrated group fails to build or breaks the contract, do not force it. Stop on that group and:
  1. Ask the user how to proceed for that item, with two options:
    • Co-exist: keep the item in the 1.0
      definition()
      DSL alongside the migrated
      @ExpoModule
      (mixed mode) and continue with the other groups.
    • Revert: back out the group's edits, leaving it untouched on 1.0, and move on.
    Default to co-existence when mixed mode is verified safe, since it preserves the most progress. Revert when the half-applied change left the module in a non-building state and cannot be salvaged incrementally.
  2. Open a tracking issue on
    expo/expo
    noting the functionality that 2.0 does not yet cover, so the gap is recorded rather than silently worked around. Use
    gh issue create --repo expo/expo
    and confirm with the user before posting (per repo conventions, do not post outward-facing comments without approval). Include:
    • the 1.0 member and its JS contract
    • the specific macro or core hook that is missing (cite the evidence gap from
      references/compatibility.md
      )
    • the
      expo-modules-core
      version/branch checked out
    Reference the issue in the handoff so the remaining DSL entry is traceable to a known limitation.
Keep going with the groups that do migrate cleanly; one blocked member does not block the rest.
当步骤2将某一项分类为受阻,或迁移后的组无法构建或破坏契约时,请勿强行迁移。在该组停止操作并:
  1. 询问用户如何处理该项,提供两个选项:
    • 共存: 将该项保留在1.0
      definition()
      DSL中,与已迁移的
      @ExpoModule
      共存(混合模式),然后继续处理其他组。
    • 回滚: 撤销该组的编辑,使其保持1.0状态不变,然后继续处理其他组。
    当混合模式被验证为安全时,默认选择共存,因为这样可以保留最多的进度。当部分应用的更改导致模块无法构建且无法逐步修复时,选择回滚。
  2. expo/expo
    上创建跟踪议题
    ,记录2.0尚未覆盖的功能,以便记录该缺口而非默默绕过。使用
    gh issue create --repo expo/expo
    命令,并在发布前征得用户确认(根据仓库约定,未经批准请勿发布对外评论)。议题中应包含:
    • 1.0成员及其JS契约
    • 缺失的特定宏或核心钩子(引用
      references/compatibility.md
      中的证据缺口)
    • 检出的
      expo-modules-core
      版本/分支
    在交接时引用该议题,以便剩余的DSL条目可追溯到已知限制。
继续处理可以顺利迁移的组;一个受阻的成员不会阻止其他成员的迁移。

4. Verify behavior

4. 验证行为

Run the narrowest available checks first, then the real integration surface:
  1. Build or type-check the Apple module against the target
    expo-modules-core
    .
  2. Run native unit tests and JS/TS tests.
  3. Build and launch the example app when the repository provides one.
  4. Compare the final exported surface with the inventory from step 1.
  5. Search for stale
    Name
    , migrated
    Function
    /
    Property
    /
    Constant
    /
    Events
    entries, old
    sendEvent
    calls,
    @Field
    , and duplicate registrations.
Expansion tests alone are insufficient: generated macro code can look correct while failing against mismatched core symbols. If dependencies changed or macro plugin flags are missing, reinstall JS dependencies as appropriate, run the repository's CocoaPods installation workflow, and restart Xcode before diagnosing plugin communication failures.
首先运行最精简的检查,然后验证实际集成接口:
  1. 针对目标
    expo-modules-core
    构建或类型检查Apple模块。
  2. 运行原生单元测试和JS/TS测试。
  3. 如果仓库提供了示例应用,构建并启动该应用。
  4. 将最终导出的接口与步骤1中的盘点结果进行对比。
  5. 搜索过时的
    Name
    、已迁移的
    Function
    /
    Property
    /
    Constant
    /
    Events
    条目、旧的
    sendEvent
    调用、
    @Field
    以及重复注册。
仅扩展测试是不够的:生成的宏代码可能看起来正确,但会因核心符号不匹配而失败。如果依赖项发生变化或缺少宏插件标志,请酌情重新安装JS依赖项,运行仓库的CocoaPods安装工作流,并在诊断插件通信失败前重启Xcode。

Handoff

交接

Report:
  • which members moved to 2.0
  • which members intentionally remain in the 1.0 DSL and why
  • any compatibility-sensitive choices, especially event names, record requiredness, constants, and queues
  • the commands run and any verification not completed
报告以下内容:
  • 哪些成员已迁移至2.0
  • 哪些成员有意保留在1.0 DSL中及其原因
  • 任何兼容性相关的选择,尤其是事件名称、记录必填性、常量和队列
  • 运行的命令以及未完成的验证工作