跳转至

移植指南

每个移植任务都必须遵循的约定。标准是与 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 重放它们。

规则

  1. 移植行为本身,包括 bug。对于有意为之的怪异移植,用 // JS-PARITY: 注释标明,指出对应的 JS 函数及其行为。移植时绝不更改阈值、消息字符串或顺序。
  2. 改进需在公共仓库通过 tests/oracle/DELTAS.md,每个案例 id 一行,且需经过评审。在此之前,以 golden 文件为准。
  3. JS 数字和字符串语义位于 impeccable_core::js(number_to_string、to_fixed、parse_float、parse_int、trim、to_lower_case)。所有输出文本的地方都要使用它们;绝不 format!("{}", f64) 一个最终会进入 stdout 或代码片段的值。
  4. 正则:使用 regex crate。对于 JS 有而它缺少的特性(lookbehind、反向引用、Unicode 上的 \b),手写等价实现并附带测试。
  5. 输出 JSON 中的字段顺序很重要。使用与 JS 字段顺序一致的 serde 结构体,或按顺序构建的 serde_json::Map(preserve_order 已开启)。
  6. 所有产生 stdout 的 crate 都通过 cli crate 的写入器进行输出,以便尾随换行和 stdout/stderr 的划分与契约完全一致。
  7. 退出码是契约的一部分。std::process::exit 仅出现在 cli 中。
  8. 用户输入不得导致 panic。release 构建设置了 panic = "abort";对用户而言,panic 就是崩溃。
  9. 保持 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

每个模块的工作流程

  1. 从头到尾阅读 JS 文件。列出导出项和内部辅助函数。
  2. 先移植辅助函数,再移植导出项,保持命名(snake_case)并添加文档注释 /// JS: <file>#<name>。
  3. 运行该模块的向量测试,直到通过。
  4. 如果该模块服务于某个命令(verb),则构建该命令,针对该命令的前缀运行 oracle,修复直到通过。
  5. 提交消息中说明已移植的 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(...)"。规则:

  1. 只要 JS 使用 path.*,就用 jsp::*。仅当 JS 写了 path.posix.* 时才使用 jsp::posix::*(tanstack-adapter 的 dirname、live 的 posix_normalize)。
  2. 当 JS 执行 .split(path.sep).join('/')(显示路径、glob 匹配、其他平台可能读取的 manifest)时,移植版调用 jsp::to_posix。当 JS 用 path.sep 构建前缀时,使用 jsp::SEP / SEP_CHAR。当 JS 按字面 '/' 分割时,保留字面:这在 Windows 上也是一样的行为,包括 bug。
  3. 在 Node 读取 process.cwd() 的地方,resolve / relative 接受显式的 cwd。只传绝对路径的调用者可以传入 "/";在 Windows 上这是无盘符根目录,正是 path.win32.resolve('/') 在不提供设备 cwd 时所得到的结果。
  4. 因操作系统而异的进程管道位于 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)。
  5. 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