移植指南¶
每个移植任务都必须遵循的约定。标准是与 JS golden 文件字节级一致的行为;在改动任何 crate 之前先读本文。
事实来源¶
- JS 源码:公共仓库检出(本地
../impeccable-second,CI 中../impeccable;环境变量IMPECCABLE_PUBLIC_REPO可覆盖)。 - 契约:公共仓库中的
docs/CLI-CONTRACT.md。每个命令的 argv、env、stdout/stderr、退出码、文件、网络。 - Goldens:公共仓库中的
tests/oracle/。使用IMPECCABLE_BIN=<path to our binary>运行node tests/oracle/run.mjs会重放每个案例并做差异对比。 - 函数向量:
tests/oracle/vectors/calls/<module>/<fn>.jsonl,由node tests/oracle/vectors/record-calls.mjs生成(需要 JS 引擎存在)。crates/core/tests/vectors.rs重放它们。
规则¶
- 移植行为本身,包括 bug。对于有意为之的怪异移植,用
// JS-PARITY:注释标明,指出对应的 JS 函数及其行为。移植时绝不更改阈值、消息字符串或顺序。 - 改进需在公共仓库通过
tests/oracle/DELTAS.md,每个案例 id 一行,且需经过评审。在此之前,以 golden 文件为准。 - JS 数字和字符串语义位于
impeccable_core::js(number_to_string、to_fixed、parse_float、parse_int、trim、to_lower_case)。所有输出文本的地方都要使用它们;绝不format!("{}", f64)一个最终会进入 stdout 或代码片段的值。 - 正则:使用
regexcrate。对于 JS 有而它缺少的特性(lookbehind、反向引用、Unicode 上的\b),手写等价实现并附带测试。 - 输出 JSON 中的字段顺序很重要。使用与 JS 字段顺序一致的 serde 结构体,或按顺序构建的
serde_json::Map(preserve_order已开启)。 - 所有产生 stdout 的 crate 都通过
clicrate 的写入器进行输出,以便尾随换行和 stdout/stderr 的划分与契约完全一致。 - 退出码是契约的一部分。
std::process::exit仅出现在cli中。 - 用户输入不得导致 panic。release 构建设置了
panic = "abort";对用户而言,panic 就是崩溃。 - 保持
core不涉及 I/O 和std::process;它会编译为 wasm。
Crate 边界¶
| crate | 负责 | 不得 |
|---|---|---|
| core | 纯规则、颜色、注册表、findings、inline ignores;浏览器规则基于 browser::dom::Dom 探针 trait |
触碰 fs/进程/网络 |
| html | 静态引擎:DOM 模型、cascade、适配器、文本/正则引擎、设计系统、视觉 | 启动浏览器 |
| browser | CDP 客户端、浏览器发现、页面注入 | 自行解析 HTML |
| context | context/doctor/pin/briefs/critique/palette/concept-seed/question/image/embed/signals/csp | |
| hook | hook、hook-before-edit、hooks admin | |
| live | live server 以及所有 live 命令 | 不得要求 Node,文档说明的 Svelte/Vue 例外除外 |
| cli | argv 路由、退出码、stdout 写入器 | 业务逻辑 |
| wasm | wasm-bindgen 对 core 的导出;JsDom(基于 JS imports 的探针);参见 docs/WASM-BUNDLE.md |
每个模块的工作流程¶
- 从头到尾阅读 JS 文件。列出导出项和内部辅助函数。
- 先移植辅助函数,再移植导出项,保持命名(
snake_case)并添加文档注释/// JS: <file>#<name>。 - 运行该模块的向量测试,直到通过。
- 如果该模块服务于某个命令(verb),则构建该命令,针对该命令的前缀运行 oracle,修复直到通过。
- 提交消息中说明已移植的 JS 模块和向量/oracle 结果。以
Prepared with AI assistance (Claude Code).结尾。
Windows¶
JS 运行在 Node 上,Node 在 Windows 上选用 path.win32,其他平台选用 path.posix。impeccable_common::jsp 也是如此:顶层函数(join、resolve、relative、dirname、basename、extname、normalize、is_absolute、SEP)根据 cfg!(windows) 分派;两个实现在 jsp::posix 和 jsp::win32 下,每个都有测试,其预期值来自 node -p "path.win32.X(...)"。规则:
- 只要 JS 使用
path.*,就用jsp::*。仅当 JS 写了path.posix.*时才使用jsp::posix::*(tanstack-adapter 的 dirname、live 的posix_normalize)。 - 当 JS 执行
.split(path.sep).join('/')(显示路径、glob 匹配、其他平台可能读取的 manifest)时,移植版调用jsp::to_posix。当 JS 用path.sep构建前缀时,使用jsp::SEP/SEP_CHAR。当 JS 按字面'/'分割时,保留字面:这在 Windows 上也是一样的行为,包括 bug。 - 在 Node 读取
process.cwd()的地方,resolve/relative接受显式的 cwd。只传绝对路径的调用者可以传入"/";在 Windows 上这是无盘符根目录,正是path.win32.resolve('/')在不提供设备 cwd 时所得到的结果。 - 因操作系统而异的进程管道位于
impeccable_common::proc:kill0/pid_reachable(process.kill(pid, 0);Windows 上用 OpenProcess)、terminate(process.kill(pid))、detach(spawn({ detached: true }):Unix 上setsid,Windows 上DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP,因此不会出现控制台窗口)、hide_window(用于短生命周期辅助程序)、on_interrupt(process.on('SIGINT'|'SIGTERM');Windows 上是控制台控制处理程序)、shell({ shell: true }:/bin/sh -c或cmd.exe /d /s /c)、node_exe(node.exe)、tool_on_path(which/where)。 - Chrome 发现已经包含 Windows 候选列表;Windows 的启动器是
launcher/impeccable.cmd。
在 macOS 或 Linux 上交叉检查:先 rustup target add x86_64-pc-windows-msvc,然后 cargo check --workspace --target x86_64-pc-windows-msvc。ring(位于 ureq 的 rustls 之下)会为目标编译 C 代码,因此需要将 cc 指向带 Windows CRT/SDK 头文件的 clang(cargo install xwin && xwin --accept-license splat --output ~/.xwin),例如:
CC_x86_64_pc_windows_msvc=clang \
CFLAGS_x86_64_pc_windows_msvc="--target=x86_64-pc-windows-msvc -Wno-everything -I$HOME/.xwin/crt/include -I$HOME/.xwin/sdk/include/ucrt -I$HOME/.xwin/sdk/include/um -I$HOME/.xwin/sdk/include/shared" \
AR_x86_64_pc_windows_msvc=<a lib.exe stand-in: llvm-lib, or a script mapping "-out:X objs" to "ar crs X objs"> \
cargo check --workspace --target x86_64-pc-windows-msvc
CI 在 windows-latest 上运行 cargo test --workspace(见 .github/workflows/ci.yml),这是项目唯一真正的 Windows 执行环境。仅凭检查无法发现、只有该任务(或一台 Windows 机器)才能暴露的问题包括:实时服务器的 --background 生成的子进程能否在父控制台退出后继续存活、Ctrl-C 能否触发 on_interrupt、cmd.exe 对 proc::shell 脚本的引号处理,以及 Svelte 桥接层能否找到 node.exe。
公共仓库中的 oracle 黄金文件是在 macOS 上录制的:<WS> / <HOME> 掩码假定路径以 / 分隔,tests/oracle/lib.mjs 对 stdout 进行逐字节比较,并且多个用例准备了仅限 POSIX 的工作区(core.hooksPath=/dev/null、sh 步骤)。若要在 Windows 上运行 oracle,测试工具需要先将 <WS>/<HOME> 掩码路径中的 \ 规范化为 / 再进行比较,需要支持盘符的掩码,并需要对输出中嵌入了 path.sep 的动词(钩子文件列表、实时清单)进行一轮 Windows 录制。这项工作属于公共仓库,不在本范围内。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 im/impeccable