import { spawnSync } from 'node:child_process'; import { dirname, resolve } from 'node:path'; import process from 'node:process'; import { fileURLToPath } from 'node:url'; import { selectRustfmtWorkspaces } from './lint-staged-rustfmt-workspaces.mjs'; // lint-staged 会把命中的暂存文件路径追加到命令末尾,而 `cargo fmt` 只按 workspace 粒度格式化、 // 不接受文件参数(也做不到「只格式化某个文件」),所以这里用暂存路径筛出**需要检查的 // workspace**,再对它们跑 `--check`。只查不改:pre-commit 不应该自动改写别人正在改的 Rust 文件。 const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); const { workspaces, excluded, unmanaged, usedStagedList } = selectRustfmtWorkspaces({ stagedFiles: process.argv.slice(2), repoRoot, }); // 静默通过是这里最贵的失败模式:glob 命中了文件、但没有任何 workspace 检查到它。 if (excluded.length > 0) { process.stderr.write( `Rust 格式检查跳过(有意排除的 workspace):${excluded.join(', ')}\n` + `该 workspace 当前整体未过 \`cargo fmt --check\`,详见 scripts/lint-staged-rustfmt-workspaces.mjs。\n`, ); } if (unmanaged.length > 0) { process.stderr.write( `Rust 格式检查未覆盖以下暂存文件(不在任何已登记 workspace 内):${unmanaged.join(', ')}\n` + `如果是新 workspace,请登记到 scripts/lint-staged-rustfmt-workspaces.mjs。\n`, ); } // 一条都没筛出来又不曾拿到暂存列表,说明调用方式变了(例如 lint-staged 不再追加参数): // 宁可回退到全量检查,也不要假装检查过了。 if (!usedStagedList) { process.stderr.write( 'Rust 格式检查未收到 lint-staged 的暂存文件列表,回退为全量检查所有 workspace。\n', ); } for (const { manifestPath } of workspaces) { const result = spawnSync( 'cargo', ['fmt', '--all', '--manifest-path', manifestPath, '--', '--check'], { stdio: 'inherit', shell: process.platform === 'win32' }, ); if (result.error) { process.stderr.write( `Rust 格式检查无法执行:${manifestPath}: ${result.error.message}\n`, ); process.exit(1); } if (result.status !== 0) { process.stderr.write( `Rust 格式检查未通过:${manifestPath}\n` + `本地复现:npm run check:rustfmt\n` + `自动修复:cargo fmt --all --manifest-path ${manifestPath}\n`, ); process.exit(result.status ?? 1); } }