Files
InfoRSI/.dependency-cruiser.cjs
T
iomgaa d2bfb971ab feat: 配置 config 模块(任务书 04)
- src/config:research.json 的字段定义(TypeBox,全部必填、不认识的字段报错,每个字段带中文说明和类别)、一次列出全部错误的校验、步骤清单纯函数。
- 流程写成完整流程 / 朴素循环两选一,第零轮、消融写成开 / 关两选一,冲突组合写不出来,不另写检查。
- 规则名取自 rules 登记表,config 改为依赖 rules(结构.md 第二部分、.dependency-cruiser.cjs)。
- ledger 顺手两处(结构.md 第四部分):消融结算按轮次唯一、去掉消融编号;私人笔记去掉版本号;表结构版本号升到 2。
- 结构.md 第八部分补 config 对外内容、更新 ledger 一节;WORK_LOG 更新。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 08:08:18 -04:00

130 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 模块之间的依赖规则,来源是 docs/结构.md 第二部分(模块表)和第六部分(检查规则)。
// 模块按 src/<模块名>/ 认;新建模块目录后自动受这些规则检查。规则只管 src 下的模块,tests/ 不受"只有某模块能用某外部包"的限制。
// 模块表"依赖"一列:每个模块除了自己和 vocab 以外,还能引用哪些模块。plugin 装配时可用各模块。
const ALLOWED = {
plugin: [
"flow", "export", "config", "materials", "submissions",
"ledger", "agents", "runner", "tasks", "workspace", "jev", "rules",
],
flow: [
"ledger", "rules", "materials", "submissions", "agents",
"runner", "tasks", "workspace", "config", "jev",
],
export: ["ledger", "rules", "materials"],
config: ["rules"],
materials: ["ledger", "rules"],
submissions: ["jev"],
ledger: [],
agents: [],
runner: [],
tasks: [],
workspace: [],
jev: [],
rules: [],
vocab: [],
};
const MODULES = Object.keys(ALLOWED);
const anyOf = (names) => `(${names.join("|")})`;
// 外部的东西各由哪些名字认出来。核心模块带不带 node: 前缀都认;
// npm 包装好后解析成 node_modules/ 下的路径,没装时是包名本身,两种都认。
const npm = (names) => `(^|node_modules/)${anyOf(names)}(/|$)`;
const SUBPROCESS = [
"^(node:)?(child_process|cluster)$",
npm(["cross-spawn", "execa"]),
];
const SQLITE = ["^node:sqlite$", npm(["better-sqlite3", "sqlite3"])];
const PI = npm(["@earendil-works/[^/]+"]);
const directionRules = MODULES.map((name) => ({
name: `direction-${name}`,
comment: `${name} 只能引用自己、vocab 和模块表里列出的模块:${ALLOWED[name].join("、") || "无"}`,
severity: "error",
from: { path: `^src/${name}/` },
to: { path: "^src/", pathNot: `^src/${anyOf([name, "vocab", ...ALLOWED[name]])}/` },
}));
module.exports = {
forbidden: [
{
name: "no-circular",
comment: "不许循环引用",
severity: "error",
from: {},
to: { circular: true },
},
{
name: "unknown-module",
comment: `src 下只有模块表里的模块目录:${MODULES.join("、")}`,
severity: "error",
from: { path: "^src/", pathNot: `^src/${anyOf(MODULES)}/` },
to: {},
},
...directionRules,
{
name: "through-index",
comment: "别的模块只能通过目录下的 index.ts 引用它",
severity: "error",
from: { path: "^src/([^/]+)/" },
to: { path: "^src/[^/]+/", pathNot: ["^src/$1/", "^src/[^/]+/index\\.ts$"] },
},
{
name: "outside-src",
comment: "src 下的文件只许引用 src/、node_modules/ 与 Node 内置模块,不能借 src 外的文件绕开规则",
severity: "error",
from: { path: "^src/" },
to: { pathNot: ["^src/", "^node_modules/"], dependencyTypesNot: ["core"] },
},
{
name: "pure-allowlist",
comment:
"rules 和 vocab 不碰文件、数据库、网络、子进程和 Pi:除了 src 下允许的模块," +
"只许引用 typebox 和 node:crypto",
severity: "error",
from: { path: "^src/(rules|vocab)/" },
to: { pathNot: ["^src/", "^node_modules/typebox/", "^(node:)?crypto$"] },
},
{
name: "sqlite-only-ledger",
comment: "只有 ledger 能用 SQLite",
severity: "error",
from: { path: "^src/", pathNot: "^src/ledger/" },
to: { path: SQLITE },
},
{
name: "pi-only-agents-plugin",
comment: "只有 agents 和 plugin 能用 Pi",
severity: "error",
from: { path: "^src/", pathNot: "^src/(agents|plugin)/" },
to: { path: PI },
},
{
name: "subprocess-only-runner-workspace",
comment: "只有 runner 和 workspace 能起子进程",
severity: "error",
from: { path: "^src/", pathNot: "^src/(runner|workspace)/" },
to: { path: SUBPROCESS },
},
{
name: "tasks-not-runner",
comment: "tasks 不许引用 runner:runner 只管怎么跑,tasks 只管怎么判,由 flow 串起来",
severity: "error",
from: { path: "^src/tasks/" },
to: { path: "^src/runner/" },
},
],
options: {
doNotFollow: { path: "node_modules" },
tsPreCompilationDeps: true,
tsConfig: { fileName: "tsconfig.json" },
// 按 Node 的 ESM 规则读 npm 包的 exports(dependency-cruiser 默认不读),
// 否则 typebox 这类只在 exports 里给入口的包解析不到。
enhancedResolveOptions: {
extensions: [".ts", ".js"],
exportsFields: ["exports"],
conditionNames: ["import", "node", "default"],
},
},
};