cordis-migrate 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +208 -0
- package/LICENSE +202 -0
- package/README.md +198 -2
- package/package.json +46 -4
- package/src/cli.js +373 -0
- package/src/core.js +608 -0
package/DESIGN.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# DESIGN — cordis-migrate
|
|
2
|
+
|
|
3
|
+
> 非官方社区工具。与 Cordiverse / Cordis / DeepSeek 无隶属关系。Apache-2.0。
|
|
4
|
+
>
|
|
5
|
+
> **核心立场:内置规则是示例,不是官方迁移表;工具只生成候选改动,语义正确性由人负责。**
|
|
6
|
+
|
|
7
|
+
## 1. 目标与非目标
|
|
8
|
+
|
|
9
|
+
### 目标
|
|
10
|
+
|
|
11
|
+
- 对 Cordis 3 → 4 迁移中**重复、文本可判定**的片段,提供可复现的扫描与改写建议。
|
|
12
|
+
- 默认安全:`apply` 默认 dry-run,`--write` 必须显式;`scan` 永不写盘。
|
|
13
|
+
- 纯 ESM + JSDoc + 零第三方依赖,可离线测试(`node --test`)。
|
|
14
|
+
- 核心(匹配/计划/应用)与 IO(遍历/读写/参数)分离,方便嵌入其它工具。
|
|
15
|
+
- 对二进制/非法编码文件绝不写入。
|
|
16
|
+
|
|
17
|
+
### 非目标
|
|
18
|
+
|
|
19
|
+
- **不是**官方迁移工具,不提供权威迁移表,不判断业务语义。
|
|
20
|
+
- **不是** AST codemod:不解析 TS/JS,不处理作用域、别名、类型、跨行结构。
|
|
21
|
+
- 不安装依赖、不改 `package.json`、不跑测试、不提交/推送。
|
|
22
|
+
- 不自动向上游 `cordiverse/cordis` 或任何官方仓库发起改写/PR。
|
|
23
|
+
- 不提供回滚/备份;版本控制是使用者的责任。
|
|
24
|
+
|
|
25
|
+
## 2. 为什么是规则而不是 AST
|
|
26
|
+
|
|
27
|
+
Cordis 4 仍在 RC(`4.0.0-rc.13`),迁移细节随版本变化,且真实仓库有大量 re-export、生成代码和类型技巧。完整 AST 迁移器需要:
|
|
28
|
+
|
|
29
|
+
1. 解析 TS/JS(引入解析器依赖,违背零依赖约束);
|
|
30
|
+
2. 理解类型信息与模块解析(需要编译环境,离线不可靠);
|
|
31
|
+
3. 对每个 API 变化做出语义判断(本工具没有权威依据)。
|
|
32
|
+
|
|
33
|
+
规则引擎用**极小的可审计面**换取确定性:每条规则只是一个行内正则和一个替换串。代价是误报/漏报,因此:
|
|
34
|
+
|
|
35
|
+
- 内置规则标为 `warning`/`error` 并有明确的人工确认提示;
|
|
36
|
+
- `plan` 展示每一处 `before -> after`,先审后写;
|
|
37
|
+
- 不声称“跑完即迁移完成”。
|
|
38
|
+
|
|
39
|
+
## 3. 架构
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
src/core.js 纯函数:RULES / scanSource / plan / applySource / formatPlan
|
|
43
|
+
src/cli.js 参数解析 + 目录遍历 + fs 读写 + 输出
|
|
44
|
+
test/core.test.js 纯核心单测(不碰磁盘)
|
|
45
|
+
test/cli.test.js spawn CLI,用 %TEMP% 下的临时目录验证 dry-run/--write/二进制跳过
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
依赖方向单向:`cli.js -> core.js`,`core.js` 不 import 任何 Node API 或第三方包(`TextDecoder` 是 Node 22+ / 现代浏览器提供的全局对象)。因此 core 可直接在浏览器/其它运行时复用。
|
|
49
|
+
|
|
50
|
+
CLI 只使用内置 `RULES`;库调用者可以传入自定义规则数组,这是有意保留的扩展点。
|
|
51
|
+
|
|
52
|
+
## 4. 数据模型与不变量
|
|
53
|
+
|
|
54
|
+
### Rule
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
{ id, kind, pattern, replacement, description, severity }
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- `pattern`:`RegExp`(推荐带 `g`)或字符串(内部按 `g` 编译),按行匹配。
|
|
61
|
+
- `replacement`:字符串(支持 `$1`/`$&`)或 `(match, ...captures) => string`。
|
|
62
|
+
- `id` 在结果与输出中标识规则;内置规则彼此唯一。
|
|
63
|
+
- 六个字段在 `scanSource`/`plan` 入口统一校验,缺字段立即 `TypeError`,避免“空计划看起来成功”。
|
|
64
|
+
|
|
65
|
+
### Match
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
{ ruleId, line, column, before, after }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- `line`、`column` 均为 **1-based**。
|
|
72
|
+
- `column` 是 UTF-16 code unit 的偏移(JS 字符串索引 + 1),不是 Unicode code point 计数。
|
|
73
|
+
- `before` 是精确匹配文本;`after` 是替换后的文本(在扫描时算好,便于 JSON 展示)。
|
|
74
|
+
- `scanSource` 只返回这五个键,按 `line`、`column`、规则输入顺序稳定排序。
|
|
75
|
+
|
|
76
|
+
### Plan
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
{
|
|
80
|
+
changes: [{ path, matches }],
|
|
81
|
+
skipped: [{ path, reason }],
|
|
82
|
+
summary: { files, changedFiles, matches, skipped }
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- `changes` 只含至少一个匹配的文件;无匹配的干净文件不在任何列表里,只计入 `summary.files`。
|
|
87
|
+
- `skipped` 是“无法按文本处理”的文件:二进制扩展名、NUL、非法 UTF-8、CLI 读取失败等。
|
|
88
|
+
- `plan` 是纯函数,不读盘、不写盘、不改变输入对象。
|
|
89
|
+
- JSON 输出不包含 `RegExp`,避免 `JSON.stringify` 后丢字段。
|
|
90
|
+
|
|
91
|
+
## 5. 匹配算法
|
|
92
|
+
|
|
93
|
+
1. 用 `/\r\n|\n|\r/g` 切分文本,并单独保存每个分隔符;扫描只看行内容,不把 `\r` 带进列号。
|
|
94
|
+
2. 每条规则克隆一个带 `g` 的 `RegExp`,每行开始前重置 `lastIndex`,避免规则状态跨文件泄漏。
|
|
95
|
+
3. `exec` 循环;对零长度匹配手动推进 `lastIndex`,防止死循环。
|
|
96
|
+
4. 零长度匹配不产出 `Match`(无法安全应用)。
|
|
97
|
+
5. 字符串替换的 `after` 用“对 `match[0]` 再执行一次非全局正则替换”得到;因此规则不应依赖匹配之外的上下文(lookbehind 等),内置规则均满足。
|
|
98
|
+
6. 排序:`line` → `column` → 规则数组顺序。稳定排序保证同一行的多个规则结果可复现。
|
|
99
|
+
|
|
100
|
+
**已知误报面**:注释、字符串字面量、正则字面量中的文本同样会被匹配。这是文本引擎的固有限制,不是 bug;需要 AST 才能消除。
|
|
101
|
+
|
|
102
|
+
## 6. 应用算法与幂等
|
|
103
|
+
|
|
104
|
+
对每个有匹配的行:
|
|
105
|
+
|
|
106
|
+
1. 只接受结构合法的 `Match`(整数 `line`/`column`、非空 `before`、字符串 `after`)。
|
|
107
|
+
2. 按 `column` 升序做**最左非重叠选择**:若当前匹配的起点早于上一个已选匹配的终点,跳过。相同列时保持 `scanSource` 的顺序(稳定排序)。
|
|
108
|
+
3. 选中的匹配按 `column` **降序**应用,从行尾向行首替换,避免左侧替换改变右侧列号。
|
|
109
|
+
4. 应用前检查 `line.slice(column - 1, column - 1 + before.length) === before`;不满足则跳过。这使:
|
|
110
|
+
- 陈旧匹配(文件已被改过)不会被误用;
|
|
111
|
+
- 对同一批匹配重复调用 `applySource` 是 no-op(幂等)。
|
|
112
|
+
5. 重新拼接时使用原始行分隔符数组,因此 CRLF/LF/混合行尾、末尾无换行都原样保留。
|
|
113
|
+
|
|
114
|
+
**为什么最左优先**:比“最右优先”更符合“从文件开头顺序改写”的直觉,也便于解释重叠时的取舍。代价是宽泛规则可能遮蔽后面的精确规则,规则作者应避免大范围重叠模式。
|
|
115
|
+
|
|
116
|
+
**幂等边界**:若自定义规则把 `before` 重新引入(例如 A→B、B→A 两条规则同时使用),重复应用仍可能变化。内置规则不构成循环。文档明确说明这一点。
|
|
117
|
+
|
|
118
|
+
## 7. 编码与二进制
|
|
119
|
+
|
|
120
|
+
`decodeTextContent(path, content)`:
|
|
121
|
+
|
|
122
|
+
1. 已知二进制扩展名(`BINARY_EXTENSIONS`)→ `binary-extension`;
|
|
123
|
+
2. 字符串含 U+0000 或字节数组含 `0x00` → `binary-nul`;
|
|
124
|
+
3. 字节数组用 `TextDecoder('utf-8', { fatal: true, ignoreBOM: true })`:
|
|
125
|
+
- 非法 UTF-8 → `invalid-utf8`(不做有损替换后写回);
|
|
126
|
+
- `ignoreBOM: true` 保留 BOM,写回不丢字节;
|
|
127
|
+
4. 非字符串/字节数组/ArrayBuffer → `unsupported-content`。
|
|
128
|
+
|
|
129
|
+
BOM 会作为 U+FEFF 参与第 1 行文本,因而使该行列号 +1;这是保留字节完整性的取舍,已在 README 说明。
|
|
130
|
+
|
|
131
|
+
**为什么不用扩展名白名单**:白名单会漏掉 `.tsx`、`.mts`、`.md`、无扩展名脚本等;用“二进制扩展名 + NUL + 严格 UTF-8”作为排除条件更稳。
|
|
132
|
+
|
|
133
|
+
## 8. CLI 的取舍
|
|
134
|
+
|
|
135
|
+
- **默认 dry-run**:迁移工具的默认行为应是“展示”,不是“修改”。`--write` 必须显式出现。
|
|
136
|
+
- **扫描/写入互斥语义**:`scan --write`、`apply --write --dry-run` 直接报错,而不是选一个“更安全”的。
|
|
137
|
+
- **遍历**:递归、排序、跳过 symlink;忽略 VCS/构建/依赖目录与 lockfile、`*.min.js`、`*.map`,减少噪音和误写风险。
|
|
138
|
+
- **路径展示**:相对目标路径,统一 `/` 分隔符,便于跨平台 JSON 对比;实际写入使用绝对路径。
|
|
139
|
+
- **输出**:文本报告逐条列出 `line:column ruleId before -> after`;`--json` 输出单一 JSON 对象,`--write --json` 追加 `write.{written,unchanged,failures}`。
|
|
140
|
+
- **退出码**:`scan` 有匹配也返回 0(把判定留给调用方/CI 解析 JSON);参数错误、目标不可读、写入失败返回 1。
|
|
141
|
+
- **读写时机**:先把所有候选文件读入内存再 plan;MVP 不做流式/大仓库优化。
|
|
142
|
+
- **不跟随 symlink**:避免扫描越界与写穿到目标目录之外。
|
|
143
|
+
|
|
144
|
+
## 9. 规则为什么是示例
|
|
145
|
+
|
|
146
|
+
5 条内置规则覆盖 import 说明符、`Context` 类型导入、`ctx.on('dispose')`、`ctx.plugin`、配置字段;写法参考了 2026-11-14 查看的已发布类型声明:
|
|
147
|
+
|
|
148
|
+
- `cordis@3.18.1` 的 `cordis/loader` 子路径;
|
|
149
|
+
- `cordis@4.0.0-rc.13` 的 `registry.d.ts` 把 `ctx.plugin` 标为 deprecated、`context.d.ts` 用 `baseUrl`、`fiber.d.ts` 提供 `effect`;
|
|
150
|
+
- 这些只是**表面证据**,不是官方迁移文档,也没有覆盖版本差异和边界语义。
|
|
151
|
+
|
|
152
|
+
因此 README、`RULES_NOTICE`、CLI help、plan 输出都重复同一句话:示例规则,人工确认。新增规则前应:
|
|
153
|
+
|
|
154
|
+
1. 找到目标版本的**一手**变更说明/类型声明;
|
|
155
|
+
2. 写出“误匹配会怎样”的例子;
|
|
156
|
+
3. 决定是否值得自动改写,还是只应作为提示;
|
|
157
|
+
4. 为规则加至少一个正例与一个反例测试;
|
|
158
|
+
5. 不让替换把旧文本重新引入(保持幂等)。
|
|
159
|
+
|
|
160
|
+
## 10. 安全边界:不自动改官方仓
|
|
161
|
+
|
|
162
|
+
本工具刻意**不具备**以下能力:
|
|
163
|
+
|
|
164
|
+
- 不执行 git、不创建分支/commit/PR;
|
|
165
|
+
- 不访问网络、不读取 registry;
|
|
166
|
+
- 不修改 `package.json`/lockfile;
|
|
167
|
+
- 不清理、不删除文件;
|
|
168
|
+
- 不对官方仓库做任何自动提交。
|
|
169
|
+
|
|
170
|
+
即使把 `--write` 指向 Cordis 官方 checkout,工具也只是按本包示例规则改文本;README 明确要求不要这样做。向上游提议变更应遵循该仓库自己的贡献流程,并由人审阅。
|
|
171
|
+
|
|
172
|
+
## 11. 测试策略
|
|
173
|
+
|
|
174
|
+
`node --test packages/cordis-migrate/test/*.test.js`,当前 26 个测试,全部离线:
|
|
175
|
+
|
|
176
|
+
- 规则完整性(≥5 条、字段齐全、kind 覆盖、说明含“示例”);
|
|
177
|
+
- 五类规则的正例、双引号、CRLF、行/列精确值;
|
|
178
|
+
- 已迁移代码无匹配;
|
|
179
|
+
- 捕获组替换、函数替换;
|
|
180
|
+
- `applySource` 应用后重扫为空、重复应用 no-op、陈旧匹配跳过;
|
|
181
|
+
- 同行多匹配、重叠匹配最左优先;
|
|
182
|
+
- CRLF/LF/混合行尾、无末尾换行;
|
|
183
|
+
- `plan` 的 changes/skipped/summary、Buffer 输入、BOM、非法 UTF-8、无效文件输入;
|
|
184
|
+
- `formatPlan` 文本/JSON 可序列化、notice、空计划;
|
|
185
|
+
- CLI:`--help`、无参数、未知命令、scan JSON/文本、单文件、默认 dry-run、显式 `--dry-run`、`--write` 写盘、二次写入幂等、二进制不被改动、`--write --json`、冲突参数、缺失目标、apply --help。
|
|
186
|
+
|
|
187
|
+
CLI 测试用 `mkdtemp` 在系统临时目录创建隔离目录,`t.after` 清理;不触碰用户目录或真实仓库。
|
|
188
|
+
|
|
189
|
+
## 12. 已知限制与可能的下一步
|
|
190
|
+
|
|
191
|
+
- 只做单行文本规则;多行/结构替换需要 AST 或模板级规则。
|
|
192
|
+
- 列号按 UTF-16 code unit;与某些工具(按 code point)不同。
|
|
193
|
+
- 无交互式确认、无 diff 补丁输出(`--json` 可供上层生成)。
|
|
194
|
+
- 无备份/回滚;无 “只改某几个文件” 的 include/exclude 参数。
|
|
195
|
+
- 大仓库全量读内存;未来可改为逐文件扫描/写入。
|
|
196
|
+
- CLI 不支持自定义规则文件;库 API 已支持,未来可加 `--rules <file>`。
|
|
197
|
+
- 未做 Windows 实机 CLI 测试之外的平台矩阵;路径输出已统一 `/`。
|
|
198
|
+
|
|
199
|
+
## 13. 命名核验与发布
|
|
200
|
+
|
|
201
|
+
- `npm view cordis-migrate version` 在 2026-11-14 返回 E404(npm 上未注册);GitHub 仓库名搜索 0 结果。
|
|
202
|
+
- npm 的 `cordis` 包由 Cordiverse 维护;本包不是其官方工具,README 已声明非隶属。
|
|
203
|
+
- 名称可用性会变化,发布前需重新核验;本包 `0.1.0`,未发布。
|
|
204
|
+
- `package.json` 已配置 `bin`、`exports`、`files`、`engines`、`publishConfig.access=public`;发布前应跑 `npm pack --dry-run` 检查包内容。
|
|
205
|
+
|
|
206
|
+
## 14. 许可
|
|
207
|
+
|
|
208
|
+
Apache-2.0。见 `LICENSE`。
|
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,3 +1,199 @@
|
|
|
1
|
-
#
|
|
1
|
+
# cordis-migrate
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **非官方社区工具。** 与 Cordiverse / Cordis 项目、DeepSeek 均无隶属关系。
|
|
4
|
+
> Apache-2.0,`0.1.0`,**未发布**。内置规则是**示例**,不是官方迁移表。
|
|
5
|
+
|
|
6
|
+
Cordis 3 → 4 的**文本级迁移规则引擎**:扫描(scan)、生成计划(plan / dry-run)、显式写入(apply `--write`)。
|
|
7
|
+
|
|
8
|
+
纯 ESM JavaScript + JSDoc,零第三方依赖,Node `^22.19.0 || >=24.9.0`。
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
node packages/cordis-migrate/src/cli.js scan ./src
|
|
12
|
+
node packages/cordis-migrate/src/cli.js apply ./src # 默认 dry-run
|
|
13
|
+
node packages/cordis-migrate/src/cli.js apply ./src --write # 只有显式传入才写盘
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 是什么
|
|
17
|
+
|
|
18
|
+
### ⚠️ 先读这一节
|
|
19
|
+
|
|
20
|
+
1. **规则是示例,不是官方迁移表。** 5 条内置规则只用于演示引擎的匹配、计划、应用与幂等行为。它们参考了 `cordis@3.18.1` / `cordis@4.0.0-rc.13` 已发布类型声明中的若干表面差异(2026-11-14 查看),但 Cordis 4 仍在 RC,API 可能继续变化;规则也没有覆盖别名、re-export、动态构造、生成代码等真实情况。
|
|
21
|
+
2. **每条改动都必须人工确认。** `--write` 不会替你理解语义。例如 `Context` 既可能只作类型,也可能被 `extends`;`baseDir` 与 `baseUrl` 的路径/URL 语义可能不同。
|
|
22
|
+
3. **不要用本工具自动修改官方仓库。** 不向 `cordiverse/cordis` 或任何上游仓库提交自动改写结果;本工具不执行 git、不推送、不联网。
|
|
23
|
+
4. **先备份或用版本控制。** `--write` 直接覆盖文件,没有内置备份/回滚;请先看 dry-run 输出并在干净工作区操作。
|
|
24
|
+
|
|
25
|
+
一个可离线运行的规则引擎,核心只有四个函数:
|
|
26
|
+
|
|
27
|
+
| API | 作用 |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `scanSource(code, rules)` | 扫描一段源码,返回 `{ruleId, line, column, before, after}` |
|
|
30
|
+
| `plan(files, rules)` | 对 `[{path, content}]` 生成 `{changes, skipped, summary}`,不碰磁盘 |
|
|
31
|
+
| `applySource(code, matches)` | 按行应用匹配,返回新代码;重复应用保持幂等 |
|
|
32
|
+
| `formatPlan(plan, {json})` | 把计划渲染为人类可读文本或 JSON |
|
|
33
|
+
|
|
34
|
+
CLI 只负责遍历目录、读取/写入文件、参数解析;匹配逻辑全部在 `src/core.js`,可脱离文件系统测试。
|
|
35
|
+
|
|
36
|
+
## 为什么
|
|
37
|
+
|
|
38
|
+
Cordis 3 → 4 迁移中有一批重复、机械、但**不能盲改**的文本变化:包说明符、仅类型导入、生命周期写法、方法名、配置字段。把它们做成显式规则,可以获得:
|
|
39
|
+
|
|
40
|
+
- 一条命令得到**可审阅的 diff 式清单**,而不是直接改文件;
|
|
41
|
+
- 规则可替换、可扩展(库 API 接受自定义规则);
|
|
42
|
+
- 默认 dry-run,`--write` 必须显式,降低误伤;
|
|
43
|
+
- 二进制/非法编码文件自动跳过,不会写坏资源文件。
|
|
44
|
+
|
|
45
|
+
## 安装
|
|
46
|
+
|
|
47
|
+
尚未发布到 npm。在仓库内直接运行:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
cd packages/cordis-migrate
|
|
51
|
+
npm test
|
|
52
|
+
node src/cli.js --help
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
发布后(未执行)预期为 `npm i -D cordis-migrate`。本包没有运行时依赖。
|
|
56
|
+
|
|
57
|
+
## 用法
|
|
58
|
+
|
|
59
|
+
### CLI
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
cordis-migrate scan <dir|file> [--json]
|
|
63
|
+
cordis-migrate apply <dir|file> [--dry-run | --write] [--json]
|
|
64
|
+
cordis-migrate --help
|
|
65
|
+
cordis-migrate --version
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- `scan`:只报告匹配;永远不写文件;即使发现匹配,退出码仍为 `0`(便于 CI 解析 JSON 后自行判定)。
|
|
69
|
+
- `apply`:默认 `--dry-run`。只有显式 `--write` 才写盘;`--write` 与 `--dry-run` 互斥。
|
|
70
|
+
- `--json`:stdout 只输出一个 JSON 对象,至少包含 `{ command, ok }`,并带 `notice`、`changes`、`skipped`、`summary`;`--write --json` 额外包含 `write.{written,unchanged,failures}`,写入失败时 `ok=false`。
|
|
71
|
+
- 可以直接传单个文件,也可以传目录;目录会递归遍历。
|
|
72
|
+
- `-h` / `--help` 打印用法并 exit `0`;`-V` / `--version` 打印 `cordis-migrate 0.1.0` 并 exit `0`。
|
|
73
|
+
|
|
74
|
+
退出码:
|
|
75
|
+
|
|
76
|
+
| 码 | 含义 |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `0` | 成功(包括 `scan` 发现匹配;匹配不是失败) |
|
|
79
|
+
| `1` | 保留:本工具没有“业务否定”状态 |
|
|
80
|
+
| `2` | 用法 / 输入 / IO 错误:无参数、未知命令、未知选项、目标不可读、写入失败 |
|
|
81
|
+
|
|
82
|
+
示例:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
# 机器可读扫描
|
|
86
|
+
node packages/cordis-migrate/src/cli.js scan ./src --json > plan.json
|
|
87
|
+
|
|
88
|
+
# 人工审阅后再写
|
|
89
|
+
node packages/cordis-migrate/src/cli.js apply ./src
|
|
90
|
+
node packages/cordis-migrate/src/cli.js apply ./src --write
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 库
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
import { RULES, applySource, formatPlan, plan, scanSource } from 'cordis-migrate'
|
|
97
|
+
|
|
98
|
+
const matches = scanSource(code, RULES)
|
|
99
|
+
console.log(matches) // [{ ruleId, line, column, before, after }, ...]
|
|
100
|
+
|
|
101
|
+
const next = applySource(code, matches)
|
|
102
|
+
const result = plan([
|
|
103
|
+
{ path: 'src/index.js', content: code },
|
|
104
|
+
{ path: 'assets/logo.png', content: pngBuffer },
|
|
105
|
+
])
|
|
106
|
+
console.log(formatPlan(result, { json: true }))
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
自定义规则(字段与内置规则相同):
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
const rule = {
|
|
113
|
+
id: 'my-rule',
|
|
114
|
+
kind: 'api',
|
|
115
|
+
pattern: /ctx\.using\(/g,
|
|
116
|
+
replacement: 'ctx.inject(',
|
|
117
|
+
description: '示例:using 已废弃,改用 inject(需自行核验)。',
|
|
118
|
+
severity: 'warning',
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`pattern` 可以是带 `g` 的 `RegExp` 或不带 flags 的字符串(内部按 `g` 编译);`replacement` 支持 JS 的 `$1` / `$&` 语法,或 `(match, ...captures) => string` 函数。规则按**行**匹配,不能跨行。
|
|
123
|
+
|
|
124
|
+
### 内置示例规则(非官方)
|
|
125
|
+
|
|
126
|
+
| id | kind | severity | 示例改动 | 为什么必须人工确认 |
|
|
127
|
+
|---|---|---|---|---|
|
|
128
|
+
| `import-specifier` | import | error | `from 'cordis/loader'` → `from '@cordisjs/loader'` | 目标包是否已安装、版本是否匹配 |
|
|
129
|
+
| `context-type-import` | type-import | warning | `import { Context } from 'cordis'` → `import type { Context } ...` | `Context` 若同时作值使用会破坏运行时代码 |
|
|
130
|
+
| `dispose-listener` | lifecycle | warning | `ctx.on('dispose', fn)` → `ctx.effect(() => fn)` | 只匹配简单标识符回调;闭包/多行/带参回调不匹配 |
|
|
131
|
+
| `ctx-plugin-to-use` | api | warning | `ctx.plugin(` → `ctx.use(` | 链式返回值与重载可能不同 |
|
|
132
|
+
| `config-base-dir` | config | warning | `baseDir` → `baseUrl` | 目录路径与 URL 语义不同;可能命中注释/字符串/无关对象 |
|
|
133
|
+
|
|
134
|
+
规则是**加权文本替换**,不是 AST 重写。`RULES_NOTICE` 常量与 CLI 输出中都会重复这条警告。
|
|
135
|
+
|
|
136
|
+
### 输出形状
|
|
137
|
+
|
|
138
|
+
`scanSource`:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{ "ruleId": "ctx-plugin-to-use", "line": 5, "column": 3, "before": "ctx.plugin(", "after": "ctx.use(" }
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
行、列均为 **1-based**;列按 UTF-16 code unit 计数。`plan`:
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"command": "scan",
|
|
149
|
+
"ok": true,
|
|
150
|
+
"notice": "These rules are illustrative examples, ...",
|
|
151
|
+
"changes": [{ "path": "src/a.js", "matches": [] }],
|
|
152
|
+
"skipped": [{ "path": "assets/logo.png", "reason": "binary-extension" }],
|
|
153
|
+
"summary": { "files": 2, "changedFiles": 1, "matches": 6, "skipped": 1 }
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`skipped.reason` 可能是:`binary-extension`、`binary-nul`、`invalid-utf8`、`unsupported-content`、`invalid-file`(库输入问题)、`unreadable`(CLI 读取失败)。**无匹配的文本文件既不在 `changes` 也不在 `skipped`**,只计入 `summary.files`。
|
|
158
|
+
|
|
159
|
+
### 安全模型
|
|
160
|
+
|
|
161
|
+
- **默认 dry-run**:`apply` 不带 `--write` 时绝不写盘;`scan` 永远只读。
|
|
162
|
+
- **显式写盘**:`--write` 必须由调用者传入;与 `--dry-run` 同时出现直接报错。
|
|
163
|
+
- **不执行**:不运行 git、npm、shell、网络请求,不安装依赖,不改 `package.json`。
|
|
164
|
+
- **不碰二进制**:已知二进制扩展名、含 NUL 字节、非法 UTF-8 的文件都会跳过;不会做有损解码后再写回。
|
|
165
|
+
- **不跟随符号链接**:目录遍历时跳过 symlink,避免越界或环。
|
|
166
|
+
- **默认忽略**:`.git`、`node_modules`、`dist`、`build`、`coverage`、`.cache`、`.next`、`.output`、`.turbo`,以及 lockfile、`*.min.js`、`*.map`。
|
|
167
|
+
- **不原子写**:直接 `writeFile` 覆盖;请用版本控制或先备份。
|
|
168
|
+
|
|
169
|
+
## 限制
|
|
170
|
+
|
|
171
|
+
- 不是解析器/AST:不理解作用域、别名、类型、字符串与注释;会误报/漏报。
|
|
172
|
+
- 只做单行匹配:多行函数体、模板字符串内的跨行内容不会被处理。
|
|
173
|
+
- 重叠匹配:`scanSource` 会全部返回;`applySource` 按**最左非重叠优先**选择,其余跳过。
|
|
174
|
+
- 幂等性依赖“替换后不再匹配同一 `before`”。内置规则满足;自定义规则若把旧文本重新引入,重复应用可能继续变化。
|
|
175
|
+
- 不处理二进制、编码转换、BOM 之外的编码;BOM 会保留,但参与第 1 行的列计数。
|
|
176
|
+
- 不生成备份、不自动提交、不判断“这个改动是否语义正确”。
|
|
177
|
+
- CLI 只使用内置 `RULES`;自定义规则目前只能通过库 API 使用。
|
|
178
|
+
- 大目录会一次性把文件读入内存(MVP 取舍),没有增量/流式扫描。
|
|
179
|
+
|
|
180
|
+
## 命名核验
|
|
181
|
+
|
|
182
|
+
2026-11-14 做了最小核验(**不是**商标或法律检索):
|
|
183
|
+
|
|
184
|
+
```sh
|
|
185
|
+
npm view cordis-migrate version
|
|
186
|
+
# npm error code E404 / Not found → 该名字当前未在 npm 注册
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
- GitHub 仓库名搜索 `cordis-migrate in:name`:0 个结果。
|
|
190
|
+
- npm 上存在官方的 `cordis` 包(`cordis@4.0.0-rc.13` 为当前 latest),其维护者是 Cordiverse;本包**不是**其官方工具,也不代表其立场。
|
|
191
|
+
- 名称可用性会变化;发布前请重新核验,并确认不与既有商标/组织政策冲突。
|
|
192
|
+
|
|
193
|
+
## 非官方声明
|
|
194
|
+
|
|
195
|
+
本项目是社区实验,**不隶属于 Cordiverse / Cordis / DeepSeek**,未获其背书。“Cordis” 及相关名称归其各自权利人所有。内置规则仅为示例,不构成官方迁移指南;按本工具输出修改代码的风险由使用者承担。
|
|
196
|
+
|
|
197
|
+
## 许可
|
|
198
|
+
|
|
199
|
+
Apache-2.0,见 [LICENSE](./LICENSE)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cordis-migrate",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Cordis 3→4 migration rule engine: scan, dry-run, apply. Rules are illustrative examples, not an official migration table.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"bin": {
|
|
8
|
+
"cordis-migrate": "./src/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./src/core.js",
|
|
12
|
+
"./cli": "./src/cli.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"src",
|
|
16
|
+
"README.md",
|
|
17
|
+
"DESIGN.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": "^22.19.0 || >=24.9.0"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "node --test test/*.test.js"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"cordis",
|
|
28
|
+
"cordis-4",
|
|
29
|
+
"migration",
|
|
30
|
+
"codemod",
|
|
31
|
+
"codemod-cli",
|
|
32
|
+
"dry-run",
|
|
33
|
+
"unofficial"
|
|
34
|
+
],
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/PerryLink/dsh-next-suite.git",
|
|
38
|
+
"directory": "packages/cordis-migrate"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/PerryLink/dsh-next-suite/tree/main/packages/cordis-migrate#readme",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/PerryLink/dsh-next-suite/issues"
|
|
43
|
+
},
|
|
44
|
+
"author": "PerryLink",
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
}
|
|
48
|
+
}
|