发现于 #3943 的实现(PR 见下),不在该 PR 处理 —— 那一单的面是 sideEffects 一致性门。这一条是另一个门的判定精度问题。
机制
scripts/__tests__/scripts-type-check.test.ts 的最后一条断言(does not need a workspace build, so it can stay in the cheap half of the job)这样判定「scripts 程序是否 import 了 workspace 包」:
constworkspaceImports=parsedProject().fileNames.flatMap((f)=>[...fs.readFileSync(f,'utf8').matchAll(/from\s+'(@object-ui\/[^']+)'/g)]).map((m)=>m[1]);
读的是文件文本,不是 TS AST。于是任何位置只要出现 from + 空白 + 引号包住的 @object-ui/* 字面量就算命中 —— 包括注释、JSDoc @example、以及讲解用的代码片段。\s 匹配换行,所以跨行的散文也能凑出这个形状。
实测
在 scripts/__tests__/check-package-self-import.test.ts 里写下一行注释(内容是在解释「注释里的包名不是 import 边」这件事本身):
// rewrite of this gate breaks on a documented `import … from '@object-ui/core'`
该断言即变红:
- []
+ [ "@object-ui/core" ]
❯ scripts/__tests__/scripts-type-check.test.ts:213:7
为什么值得记一笔:失败信息把人指向不存在的东西
红的本身是「安全方向」(宁可误报不可漏报),真正的成本在诊断词:
tsconfig.scripts.json’s program now imports workspace packages, so it needs their built
declaration files. Move the ci.yml step below the build, or drop the import.
而现场根本没有 import,没有 import 可 drop,也没有任何 .d.ts 需求。读到这条的人会去找一条不存在的 import,或者更糟 —— 真的去动 ci.yml 的步骤顺序,把一个廉价步骤挪到 build 之后,而它的前提其实从未被破坏。误报本身廉价,误报 + 错误的修复指引才是这条的内容。
同一个仓里已经有过这个教训的反面:examples/console-starter/vite.config.ts:50 专门写了一段注释说明它故意不在注释里拼出带引号的 specifier,因为「那个诱饵已经骗过一次扫描」。
可能的处置(供分诊,未实施)
按代价从低到高:
- 只改诊断词 —— 承认判据是文本匹配,让信息同时给出两个方向:「这是真 import → 移动 ci.yml 步骤 / 删掉它;这是注释或示例 → 改写措辞,别在
scripts/ 里拼出带引号的 specifier」。最小、不改判据,消除误导。 - 剥掉注释再匹配 —— 与
vitest-config-alias-targets-3944.test.ts 的 withoutCommentLines() 同形状(该文件已因同类原因这么做)。能挡掉整行注释,挡不掉行尾注释与跨行散文。 - 改用 AST ——
parsedProject() 已经产出 fileNames,拿 ts.createSourceFile 遍历 ImportDeclaration/ExportDeclaration 的 moduleSpecifier 即精确判定,顺带覆盖 import type 与 export … from。判据从「像不像」变成「是不是」。
倾向 3(判据与被判定的事实对齐),但 1 单独就能消掉误导,且可独立落地。
退出条件
- 上述任一处置落地(诊断词至少要不再指向不存在的 import);或
- 判据改为 AST 后本条自动关闭。
参考位置:scripts/__tests__/scripts-type-check.test.ts:205-214、scripts/__tests__/vitest-config-alias-targets-3944.test.ts(withoutCommentLines() 先例)、examples/console-starter/vite.config.ts:50(同类诱饵的现场注释)。
发现于 #3943 的实现(PR 见下),不在该 PR 处理 —— 那一单的面是
sideEffects一致性门。这一条是另一个门的判定精度问题。机制
scripts/__tests__/scripts-type-check.test.ts的最后一条断言(does not need a workspace build, so it can stay in the cheap half of the job)这样判定「scripts 程序是否 import 了 workspace 包」:读的是文件文本,不是 TS AST。于是任何位置只要出现
from+ 空白 + 引号包住的@object-ui/*字面量就算命中 —— 包括注释、JSDoc@example、以及讲解用的代码片段。\s匹配换行,所以跨行的散文也能凑出这个形状。实测
在
scripts/__tests__/check-package-self-import.test.ts里写下一行注释(内容是在解释「注释里的包名不是 import 边」这件事本身):该断言即变红:
为什么值得记一笔:失败信息把人指向不存在的东西
红的本身是「安全方向」(宁可误报不可漏报),真正的成本在诊断词:
而现场根本没有 import,没有 import 可 drop,也没有任何
.d.ts需求。读到这条的人会去找一条不存在的 import,或者更糟 —— 真的去动ci.yml的步骤顺序,把一个廉价步骤挪到 build 之后,而它的前提其实从未被破坏。误报本身廉价,误报 + 错误的修复指引才是这条的内容。同一个仓里已经有过这个教训的反面:
examples/console-starter/vite.config.ts:50专门写了一段注释说明它故意不在注释里拼出带引号的 specifier,因为「那个诱饵已经骗过一次扫描」。可能的处置(供分诊,未实施)
按代价从低到高:
scripts/里拼出带引号的 specifier」。最小、不改判据,消除误导。vitest-config-alias-targets-3944.test.ts的withoutCommentLines()同形状(该文件已因同类原因这么做)。能挡掉整行注释,挡不掉行尾注释与跨行散文。parsedProject()已经产出fileNames,拿ts.createSourceFile遍历ImportDeclaration/ExportDeclaration的moduleSpecifier即精确判定,顺带覆盖import type与export … from。判据从「像不像」变成「是不是」。倾向 3(判据与被判定的事实对齐),但 1 单独就能消掉误导,且可独立落地。
退出条件
参考位置:
scripts/__tests__/scripts-type-check.test.ts:205-214、scripts/__tests__/vitest-config-alias-targets-3944.test.ts(withoutCommentLines()先例)、examples/console-starter/vite.config.ts:50(同类诱饵的现场注释)。