MAW / ESM 迁移图解← 回到迁移工作台
ONE EDGE · BEFORE → AFTER

把一根「查号」线,换成一根「名片」线

例子卡说的那件事,就是下面这两条线的差别。

NOW · 隐式 editor-utils.js:33 门面 resolve('utils-lrc') 只有 9 个字符的名字 · 运行时才知道是谁 window.MAWE 名字 → 师傅 的电话簿 查到了 → 当场执行一次 shared/utils/lrc.js 工厂:交出 parseLrcSegments AFTER · 显式 ESM 入口 新建 · 目前还不存在 import … from "../shared/utils/lrc.js" 文件路径写在源码里 · 构建期就能核对 · 电话簿不再经手 shared/utils/lrc.js 同一个工厂,一个字节不改

上面那条线经过 3 个中间人,下面那条只经过 0 个。工厂本身(lrc.js)两边都不动。

本页只解释,不改产品代码。LRC 这根边还没真的迁移;行号取自当前工作树。

01三个名词 = 三个实物

"依赖袋"不是术语,是我随手起的名字。它对应下面三样东西,都能点进文件。

FACTORY · 还没上工的师傅 lrc.js createUtils Module() 调用它 才产出 {parseLrc…} 此刻全仓库没有任何代码调用它 REGISTRY · 电话簿 'utils-lrc' 'utils-srt' 'utils-data' …共 63 条 register 存名字 resolve 查名字 查到就执行一次 不缓存 · 不是单例 · 查两次得两份 "依赖袋" · 一个普通对象 const helpers = { … } 被 Object.assign 塞了 34 次 最后整个挂到 window 上 editor-utils.js:6 · 没有别的神秘结构

02这根边今天是这么走的

从"有人写了个解析 LRC 的函数"到"导入按钮能用",经过四站。

站 1 · 源码 站 2 · 构建(Node) 站 3 · 装配(浏览器) 站 4 · 发布与消费 web/shared/utils/lrc.js export function createUtilsModule() return { parseLrcSegments } 文件里没有 MAWE、没有 window web/editor-modules.json:130 {"name":"utils-lrc","file":"…/lrc.js"} scripts/build-editor.mjs:144 · 167 代写 import + MAWE.register editor-utils.js:33 Object.assign( helpers, MAWE.resolve( 'utils-lrc', helpers)) 一行干三件事:查号 · 执行 · 塞袋 editor-utils.js:349 window.AsrEditorUtils = { …列 key } 消费点 multi-import:238 这条链上,站 1 到站 3 之间没有任何一处写着文件名 lrc.js 唯一的连接件 = 字符串 'utils-lrc' 名字写错 → 电话簿安静地返回 undefined → Object.assign 什么都没塞 → 页面照常加载 → 用户点导入 .lrc 才炸 同一根边,改成显式之后 ESM 入口(新建) 文件路径写在源码里 构建期按路径 · 不经手电话簿 editor-utils.js:33 原位 换成 import 的结果,位置不动 window 全局桥 几百处旧消费者不动 import { createUtilsModule as createLrcModule } from "../shared/utils/lrc.js"; 这一行同时替掉站 2 的代写 import、站 2 的 register、站 3 的查号 —— 三件事变成一行文字

03亲手把电话簿按坏

规则照抄 editor-runtime.js:20-35。改名字,点查号。

04工厂收到的是"那一刻"的 helpers

门面的 34 次查号顺序如下。排在切线左边的才塞进了袋子,右边的看不见。点时刻切换切线。

这一刻已经在袋里 · 22

▼第 33 行utils-lrc
在这里进袋

还进不去 · 11

例外:utils-word-timing(第 30 行)查回来存进 wordTimingHelpers,从没进袋子,只在第 349 行展开到最终对象。后面的工厂拿不到它。

05那 4 行代码,一行一行对回现状

左边新代码,右边它顶掉的现状。三行等价,一行不等价。

新代码(目标形状)
顶掉的现状(真实代码)
等价换形import { createUtilsModule as createLrcModule }
  from "../shared/utils/lrc.js";
必须起别名:清单里 63 个模块有 40+ 个都叫 createUtilsModule,同作用域直接撞名。
→
build-editor.mjs:144,167 代写的
import + MAWE.register("utils-lrc", …)
查号从"运行时按字符串"变成"构建期按路径"
等价const lrcHelpers = createLrcModule(editorHelpers);lrc 的工厂现在不收参数,传袋子只为统一形状,行为不变。
→
MAWE.resolve('utils-lrc', helpers)
/* 写在 :33 里 */
同一个袋子、同一个时刻,只是不再经手电话簿
完全等价Object.assign(editorHelpers, lrcHelpers);现状这行本来就长这样。
→
Object.assign(helpers, …)
/* editor-utils.js:33 */
塞 parseLrcSegments 进袋子
⚠ 不等价window.AsrEditorUtils = Object.freeze(editorHelpers);现状是手工列 key 的清单,不是冻结袋子。顺手改=同一步里扩大公开面。见坑 1。
→
window.AsrEditorUtils = {
  ...wordTimingHelpers,
  PROJECT_SCHEMA, parseLrcSegments, …
};/* :349 列 key */
列 key 就是为了挡住"袋里有但不对外"的东西

06本步切的是左边这根,不是右边那两根

editor-utils.js 门面 · 装配工 lrc.js 工厂 本步只动这一根边 ★ 装配边 'utils-lrc' → import editor-multi-import.js:238 UI 消费点 editor-loading-progress.js:60 UI 消费点 AsrEditorUtils 全局桥 消费边 虚线那两根属于后续批次:UI 改成不用全局桥,要等门面稳定以后单独排。

07归属只有三条路,前两条现在是死的

lrc.js 归谁装配? editor-modules.json A · 留着清单条目,又手写 import 两条 register 撞在一起 editor-runtime.js:27 already registered B · 直接把清单条目删掉 被当 classic 源码内联 export 进了 script 作用域 → 语法炸 C · 声明 kind:"init",由入口装配 builder 支持:清单位置调用一次 factory() 但 63 个模块一个都没用过 → 需先实现并验证 ✗ 页面起不来 装配阶段直接抛错 同一个名字登记了两次 ✗ 构建失败 :137-141 还要求清单必须 覆盖所有工厂源文件 ✓ 目标路径 入口手工装配 · 不再 register 代价:先给 builder 补这条表达

08其余四个坑(每个一张小图)

坑 1冻结整个袋子 ≠ 现状那份清单

helpers 袋子 33 个模块的全部导出几十个 key :349 对外只列这些 PROJECT_SCHEMA… 袋子 ⊅ 清单

换成 freeze(helpers) 会顺手扩大公开面,而且只是浅冻结。

所以:这一行保持现状,"发布面"另立一步单独验证。

坑 2「查回来 → 塞袋 → 冻结」这套模板有个例外

utils-word-timing wordTimingHelpers另一个变量 :349 展开 :30 没有 Object.assign,后面的工厂拿不到它

套模板没注意这个例外,迁完会出现"某个函数突然没了"。

坑 3"ESM 入口"目前不存在

boot/editor.js加载守卫,零 import 拼出来的 editor-entry(虚拟) 需新建不是改一行

卡片里那个写 import 的入口文件要新建;现在 import 全是 builder 代写。

坑 4行序 = 依赖图本身

:16 utils-ass-style :17 utils-text-processing 换序=换依赖 源码里有条注释钉死了这个顺序

"在原来的初始化点创建同一份 helper"——位置比写法更要紧。

坑 5注册表不缓存:查到一次就执行一次

resolve ×2 {parseLrcSegments} {parseLrcSegments} 两份实例不是单例,别拿它当缓存

第 03 节演示框按两次查号就能看到计数在涨。

09真要动手时该碰的文件

文件现在本步
web/shared/utils/lrc.js无参工厂不动
web/editor-modules.json:130声明 utils-lrc按第 07 节选归属
scripts/build-editor.mjs:144,167,169代写 import+register;清单外按 classic 内联需补"由入口手工装配"的表达
web/shared/editor-utils.js:33resolve('utils-lrc', helpers)换成 import 结果,位置不动
web/shared/editor-utils.js:349显式列 key不动(坑 1)
web/editor/boot/*零 import新建 ESM 入口
…multi-import.js:238 · …loading-progress.js:60走全局桥不动(第 06 节虚线)
pnpm run check:editor # 产物新鲜度 node scripts\build-editor.mjs --write # 改装配逻辑后重建 node --test tests\test_editor_script_syntax.mjs tests\test_editor_script_order.mjs python scripts\run_check.py --timeout 180 -- uv run --no-sync python scripts\run_tests.py
配套解释 工作台 例子卡「迁移一步 · 只显式连接 LRC」。卡文案由 build-plan.mjs:92 生成,改文案改那里,不要手改 dist/plan-data.js。