2222 * `engine.guard.test.ts` 同款用法);② **文本级扫描,不做 AST** —— 它只能证「字面量在不在」,
2323 * 不证实现是否真的遵守(评审 I-4 的那条限度);③ 未按 `*_PX` 命名的位移常量(如 `CARD_LIFT = 12`)
2424 * 本判据抓不到,只由评审兜 —— **诚实登记,不声称封死**。
25+ * ④ 🔴 **语法洞的剩余边界(R29.2 收窄后仍未关掉的面 · R36.1①)**:读口是**文本正则、不是 TS 解析器**
26+ * (见 ②)。收窄后它认得「`= 字面量 ;`」这一族(可带类型标注、可套 `Number()`、可套括号、可跨行、
27+ * 负号按 `Math.abs` 取值);**仍看不见**的形态逐字如下:**运算表达式**(`8 + 4`)· **三元**
28+ * (`x ? 12 : 4`)· **模板串**(`` `12` ``)· **`as const`**(`12 as const`)· **十六进制**(`0xc`)·
29+ * **右值来自跨文件导入或计算**(`= IMPORTED_PX` / `= BASE * 2`)。另有三条**结构性限度**:
30+ * 类型标注里含 `=`(如函数类型 `() => void`)会让读口错位 · 缺分号(ASI)的声明会把右值捕到**下一条**
31+ * 声明的 `;` · 右值里含 `;`(字符串 / 模板串内)会被截断。⇒ 这些面**由评审兜**,本判据不声称覆盖。
32+ * ⑤ **正面口径(不许读成全覆盖)**:本判据只保证「**它看得见的形态必须 ≤ 8px**」——**不是**「所有 TS
33+ * 形态都被覆盖」。⇒ **有意不做 fail-closed**:非字面量 `_PX` 的**合法**来源包括跨文件导入与计算 ⇒
34+ * 判红会把合法写法当犯规 = **假阳性**;本仓口径 **判据错杀比漏判更坏**(它会挡住正当改动)——
35+ * R36.1④ 逐字裁定「**不取 fail-closed**」。剩余边界以 ④ 为准,逐字登记,不假装全覆盖。
2536 */
2637import { readdirSync , readFileSync , statSync } from "node:fs" ;
2738import { dirname , join } from "node:path" ;
@@ -75,8 +86,10 @@ function shiftViolations(text: string, file: string): string[] {
7586 . map ( ( s ) => `${ file } : ${ s . call } = ${ s . value } px > ${ SHIFT_MAX_PX } px` ) ;
7687}
7788
78- /** `export const <name> = <number>` 形态的数值常量(R11.4 的「位移常量」面) */
79- const NUMERIC_CONST = / e x p o r t c o n s t ( [ A - Z a - z _ $ ] [ \w $ ] * ) \s * = \s * ( - ? \d + (?: \. \d + ) ? ) \s * ; / g;
89+ /** 读口(R29.2 收窄):`export const <名>[: <标注>] = <右值>;` —— 右值**整段**交给 `LITERAL_RHS` 判形态 */
90+ const NUMERIC_CONST = / e x p o r t c o n s t ( [ A - Z a - z _ $ ] [ \w $ ] * ) \s * (?: : [ ^ = ; ] + ) ? = \s * ( [ \s \S ] * ?) ; / g;
91+ /** 字面量右值:裸数字 / `Number(...)` 包装 / 任意层括号(跨行已折叠空白);**不匹配 ⇒ 跳过、不判红**(文件头 ④⑤) */
92+ const LITERAL_RHS = / ^ \( * \s * (?: N u m b e r \s * \( \s * ) ? ( - ? \d + (?: \. \d + ) ? ) (?: \s * \) ) ? \s * \) * $ / ;
8093const PX_NAME = / _ P X $ / ;
8194
8295interface NumericConst {
@@ -88,7 +101,9 @@ function numericConstants(text: string, onlyPxNames: boolean): NumericConst[] {
88101 const out : NumericConst [ ] = [ ] ;
89102 for ( const m of stripComments ( text ) . matchAll ( NUMERIC_CONST ) ) {
90103 if ( onlyPxNames && ! PX_NAME . test ( m [ 1 ] ) ) continue ;
91- out . push ( { name : m [ 1 ] , value : Number ( m [ 2 ] ) } ) ;
104+ const lit = LITERAL_RHS . exec ( m [ 2 ] . replace ( / \s + / g, " " ) . trim ( ) ) ;
105+ if ( lit === null ) continue ; // 非字面量右值:本读口看不见 —— 剩余边界见文件头 ④(有意不 fail-closed,见 ⑤)
106+ out . push ( { name : m [ 1 ] , value : Math . abs ( Number ( lit [ 1 ] ) ) } ) ;
92107 }
93108 return out ;
94109}
0 commit comments