@jayyuen66/dsh-plugin-shared 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,855 +1 @@
1
- import { createRequire } from "node:module";
2
- import { defineConfig } from "oxlint";
3
- //#region config/oxlint.base.ts
4
- /** `no-unsafe-call` / `-assignment` / `-return` / `-argument` 四条的依据与 member-access 同一条。 */
5
- const NOTE_UNSAFE_SAME_AS_MEMBER_ACCESS = "同 no-unsafe-member-access";
6
- /**
7
- * 实测口径:临时把基线里每条 `off`(rules 与 override 层都算)翻成 `error` 再跑 15 个成员、
8
- * 与基线取差数 `::error` 行,按路径分成 prod / test / scripts 三桶。`--deny=<rule>` 盖不住
9
- * `off`,最早一轮用它在 13 个包目录上量出的数因此全偏小(`vitest/no-hooks` 用 `-D` 量出来是
10
- * 0,实际是 181),整批作废重测。
11
- *
12
- * 键支持插件级通配(`vitest/*`),作用于该插件下没有更具体键的全部规则。
13
- */
14
- const OFF_JUSTIFICATIONS = {
15
- "vitest/*": {
16
- kind: "文体作用域",
17
- measured: 7,
18
- note: "用例结构类判据在非用例文件上无从计算"
19
- },
20
- "vitest/no-hooks": {
21
- kind: "上游不推荐",
22
- measured: 181,
23
- note: "禁用 hook 本身;本仓按官方 vitest 用法大量使用 before*/after*"
24
- },
25
- "vitest/no-conditional-in-test": {
26
- kind: "文体作用域",
27
- measured: 523,
28
- note: "平台/宿主版本分支是刻意的(macOS 与 Linux 的家目录形状、0.1.7 与 0.1.6 的 API 面)"
29
- },
30
- "vitest/max-expects": {
31
- kind: "上游不推荐",
32
- measured: 163,
33
- note: "给单条用例的断言条数设上限属口味,不指向缺陷"
34
- },
35
- "vitest/prefer-expect-assertions": {
36
- kind: "上游不推荐",
37
- measured: 2902,
38
- note: "要求每个 async 用例手写 expect.assertions(n),是与断言强度无关的计数仪式"
39
- },
40
- "vitest/require-test-timeout": {
41
- kind: "上游不推荐",
42
- measured: 2892,
43
- note: "要求每条用例显式 timeout;本仓用泵式假时钟,超时由 13 份 vitest 配置统一给"
44
- },
45
- "vitest/prefer-importing-vitest-globals": {
46
- kind: "实测否证",
47
- measured: 0,
48
- note: "autofix 方向是删掉显式 import 改回裸全局;13 份 vitest 配置都没开 globals:true,应用后当场 3605 处 TS2593、测试全废"
49
- },
50
- "vitest/no-importing-vitest-globals": {
51
- kind: "实测否证",
52
- measured: 133,
53
- note: "与上一条同族、方向相反,两条同时开就是把两种写法都判死"
54
- },
55
- "vitest/prefer-import-in-mock": {
56
- kind: "实测否证",
57
- measured: 6,
58
- note: "动态形态按完整模块面校验工厂返回值,而 @types/node 的 os、@types/react 都是 `export =` 形状、类型面上没有 default;实测三处 TS2769+TS2740/2741,react 那处无论怎么 spread 都补不出 default"
59
- },
60
- "vitest/prefer-lowercase-title": {
61
- kind: "上游不推荐",
62
- measured: 171,
63
- note: "只在用例文件里生效:它带 autofix,实测一轮 oxlint --fix 静默小写化了 260 条标题(含 nO_VERIFY 这种坏名)。包级 titlePrefixes 按 owner 2026-09-28 的口径只留真正的技术名(GET/CSRF/组件名…):规格 ticket 号已从标题里删掉,中文开头的标题天然满足判据(无大小写)⇒ 免检面能收多小收多小。实测 oxlint 前缀一命中就把整条标题免检,所以陈旧条目由 scripts/assert-config-baseline.mjs 的 LINT_TITLE_PREFIX_UNUSED 点名,不靠人工巡检"
64
- },
65
- "vitest/prefer-called-times": {
66
- kind: "对侧互斥",
67
- measured: 0,
68
- note: "探针实测(同文件四种写法各跑一次):toHaveBeenCalledTimes(1) 被 prefer-called-once 判死、toHaveBeenCalledOnce() 被本条判死、expect(spy.mock.calls).toHaveLength(1) 被 prefer-to-have-been-called-times 判死,只有 n>=2 能同时满足三条 ⇒ 三条同开等于「单次调用」这条断言无法书写(实测逼得 plugin-hot-reload 改用 assert.equal(spy.mock.calls.length, 1) 绕行)。留 prefer-called-once 与 prefer-to-have-been-called-times 两条方向一致的组合,关本条"
69
- },
70
- "vitest/prefer-describe-function-title": {
71
- kind: "对侧互斥",
72
- measured: 48,
73
- note: "要求 `describe(fn)`,而 valid-title 要求字符串字面量(oxlint 1.85 的 valid-title 是 DummyRule、无任何选项);上游 recommended 收的是 valid-title,取它,26 处 `describe(fn)` 按代码改成同名字符串"
74
- },
75
- "vitest/prefer-to-be-truthy": {
76
- kind: "实测否证",
77
- measured: 117,
78
- note: "把 toStrictEqual(true) 放宽成 toTruthy(),接受任意真值是削弱断言"
79
- },
80
- "vitest/prefer-to-be-falsy": {
81
- kind: "实测否证",
82
- measured: 89,
83
- note: "同族,放宽成 toFalsy() 同样是削弱断言"
84
- },
85
- "import/no-default-export": {
86
- kind: "契约冲突",
87
- measured: 38,
88
- note: "官方 cordis 入口就是 default export(vendor/loader/src/index.ts:201 先 unwrap default),12 条正是 12 个 bundle 入口"
89
- },
90
- "import/no-named-export": {
91
- kind: "契约冲突",
92
- measured: 895,
93
- note: "13 个包与上游 deepseek-harness 一样以具名导出 + 子路径说明符对外暴露"
94
- },
95
- "import/prefer-default-export": {
96
- kind: "契约冲突",
97
- measured: 58,
98
- note: "与 no-default-export 同族、方向与 cordis 入口相反"
99
- },
100
- "import/no-nodejs-modules": {
101
- kind: "契约冲突",
102
- measured: 448,
103
- note: "host 半就跑在 node 上,`node:` 内置模块是运行环境本身而不是越界"
104
- },
105
- "import/no-named-as-default-member": {
106
- kind: "契约冲突",
107
- measured: 47,
108
- note: "对具名导出的默认对象做成员访问是本仓消费官方声明的常态"
109
- },
110
- "import/no-anonymous-default-export": {
111
- kind: "契约冲突",
112
- measured: 12,
113
- note: "官方 cordis 入口是匿名对象 default export(registry.ts:92 读 name/Config/apply),具名化等于改官方形态"
114
- },
115
- "eslint/no-duplicate-imports": {
116
- kind: "实测否证",
117
- measured: 166,
118
- note: "ESLint 官方已废弃(不区分 import type),由 import/no-duplicates 取代"
119
- },
120
- "import/group-exports": {
121
- kind: "上游不推荐",
122
- measured: 766,
123
- note: "要求单点导出,与具名导出面冲突"
124
- },
125
- "import/exports-last": {
126
- kind: "上游不推荐",
127
- measured: 528,
128
- note: "要求导出语句在文件末尾"
129
- },
130
- "import/no-relative-parent-imports": {
131
- kind: "上游不推荐",
132
- measured: 277,
133
- note: "包内 lib/ 与 test/ 的相对父级引用是既有结构"
134
- },
135
- "import/max-dependencies": {
136
- kind: "上游不推荐",
137
- measured: 25,
138
- note: "给 import 条数设上限;host 半消费官方服务面天然依赖多"
139
- },
140
- "node/no-process-env": {
141
- kind: "上游不推荐",
142
- measured: 51,
143
- note: "环境变量是本仓的输入面(DSH_HOME、profile 目录、构建期开关);eslint-plugin-n 的 recommended 也不含本条"
144
- },
145
- "eslint/no-magic-numbers": {
146
- kind: "上游不推荐",
147
- measured: 5774,
148
- note: "把 HTTP 状态码/毫秒阈值抽成常量的计数规则"
149
- },
150
- "eslint/sort-keys": {
151
- kind: "上游不推荐",
152
- measured: 4101,
153
- note: "对象键排序;成对字面量与 locale 字典的可读性会一起牺牲"
154
- },
155
- "eslint/sort-imports": {
156
- kind: "上游不推荐",
157
- measured: 675,
158
- note: "字母序 import,与 oxfmt 的分组形态冲突"
159
- },
160
- "eslint/capitalized-comments": {
161
- kind: "上游不推荐",
162
- measured: 2388,
163
- note: "本仓注释以中文为主,「首字母大写」对中文注释无从判定"
164
- },
165
- "eslint/no-ternary": {
166
- kind: "上游不推荐",
167
- measured: 1024,
168
- note: "禁三元;改写成 if/else 并不会更正确"
169
- },
170
- "eslint/no-undefined": {
171
- kind: "上游不推荐",
172
- measured: 2009,
173
- note: "禁 `undefined` 字面量,与本仓大量显式可选返回形态冲突"
174
- },
175
- "eslint/no-void": {
176
- kind: "上游不推荐",
177
- measured: 172,
178
- note: "禁 `void` 前缀,而 `void promise` 正是消解 no-floating-promises 的官方写法"
179
- },
180
- "unicorn/no-null": {
181
- kind: "上游不推荐",
182
- measured: 788,
183
- note: "禁 null;官方声明与 JSON 边界大量用 null,改成 undefined 会改变序列化形状"
184
- },
185
- "typescript/prefer-readonly-parameter-types": {
186
- kind: "上游不推荐",
187
- measured: 2433,
188
- note: "要求每个入参类型全 readonly;官方声明面(SessionEvent/ConfigForm 等)是可变的,改不动也不该改"
189
- },
190
- "oxc/no-async-await": {
191
- kind: "对侧互斥",
192
- measured: 2160,
193
- note: "禁 async/await,而同表里 promise-function-async、require-await、no-floating-promises 都在推 async"
194
- },
195
- "oxc/no-optional-chaining": {
196
- kind: "对侧互斥",
197
- measured: 1354,
198
- note: "禁 `?.`,而 typescript/prefer-optional-chain 正在要求把 && 链改成 `?.`"
199
- },
200
- "oxc/no-rest-spread-properties": {
201
- kind: "对侧互斥",
202
- measured: 550,
203
- note: "禁对象 spread,而 ES2024 目标与官方声明的投影写法都靠它"
204
- },
205
- "typescript/no-explicit-any": {
206
- kind: "文体作用域",
207
- measured: 1,
208
- note: "用例里的桩件面(mini-react 之类)需要 any 才塞得进官方签名"
209
- },
210
- "typescript/no-non-null-assertion": {
211
- kind: "文体作用域",
212
- measured: 536,
213
- note: "用例里 `!` 是「前驱条件已成立」的最短写法"
214
- },
215
- "typescript/no-unsafe-type-assertion": {
216
- kind: "文体作用域",
217
- measured: 844,
218
- note: "桩件需要 `as never` / `as unknown as` 跨官方名义类型"
219
- },
220
- "typescript/require-await": {
221
- kind: "文体作用域",
222
- measured: 467,
223
- note: "官方 async 契约的同步实现与测试替身撑不住这条;prod 侧的条数按代码修,不进豁免"
224
- },
225
- "eslint/require-await": {
226
- kind: "对侧互斥",
227
- measured: 481,
228
- note: "它与 typescript/require-await 是同一条判据的两份实现,不是两条判据:探针实测(一段无 await 的 async 函数、--type-aware)单开各自都报、同开报两次 ⇒ 2 errors / 1 defect。按 typescript-eslint 官方的 extension-rule 口径裁决:留带类型信息的扩展那条、关基线那条。覆盖面没有因此变小:全仓逐行去重实测 514 行两点同报、typescript 独有 0 行、eslint 独有只落在用例面与配置自身的中间态;而 --type-aware 实测对 .mjs 也会由扩展那条报出,所以 scripts/*.mjs 面不需要基线那条补位。用例文件由 TEST_OVERRIDES 整族豁免"
229
- },
230
- "eslint/max-statements": {
231
- kind: "文体作用域",
232
- measured: 549,
233
- note: "用例体天然长(given/when/then 展开)"
234
- },
235
- "eslint/max-lines-per-function": {
236
- kind: "文体作用域",
237
- measured: 365,
238
- note: "同上"
239
- },
240
- "eslint/max-lines": {
241
- kind: "文体作用域",
242
- measured: 54,
243
- note: "单个用例文件的长度不作为缺陷信号"
244
- },
245
- "node/no-sync": {
246
- kind: "文体作用域",
247
- measured: 299,
248
- note: "构建脚本与门禁脚本按设计同步读;生产代码的写侧由每包 syncWrites 逐项登记,不在豁免里"
249
- },
250
- "node/no-top-level-await": {
251
- kind: "文体作用域",
252
- measured: 53,
253
- note: "脚本以 node 直接执行,顶层 await 是 ESM 常态;prod 侧 0 条"
254
- },
255
- "typescript/strict-boolean-expressions": {
256
- kind: "文体作用域",
257
- measured: 0,
258
- note: "scripts/*.mjs 无类型面,隐式 any 上无从判定"
259
- },
260
- "typescript/no-implied-eval": {
261
- kind: "文体作用域",
262
- measured: 0,
263
- note: "同上:这条要类型信息才能判"
264
- },
265
- "typescript/no-unsafe-member-access": {
266
- kind: "文体作用域",
267
- measured: 0,
268
- note: "无类型标注的脚本里每个值都落进隐式 any"
269
- },
270
- "typescript/no-unsafe-call": {
271
- kind: "文体作用域",
272
- measured: 0,
273
- note: NOTE_UNSAFE_SAME_AS_MEMBER_ACCESS
274
- },
275
- "typescript/no-unsafe-assignment": {
276
- kind: "文体作用域",
277
- measured: 0,
278
- note: NOTE_UNSAFE_SAME_AS_MEMBER_ACCESS
279
- },
280
- "typescript/no-unsafe-return": {
281
- kind: "文体作用域",
282
- measured: 0,
283
- note: NOTE_UNSAFE_SAME_AS_MEMBER_ACCESS
284
- },
285
- "typescript/no-unsafe-argument": {
286
- kind: "文体作用域",
287
- measured: 0,
288
- note: NOTE_UNSAFE_SAME_AS_MEMBER_ACCESS
289
- },
290
- "typescript/require-array-sort-compare": {
291
- kind: "文体作用域",
292
- measured: 0,
293
- note: "无类型脚本里「比较器缺失」判不出来"
294
- },
295
- "unicorn/no-process-exit": {
296
- kind: "文体作用域",
297
- measured: 2,
298
- note: "CLI 脚本的 process.exit 就是它的返回方式"
299
- },
300
- "eslint/no-continue": {
301
- kind: "文体作用域",
302
- measured: 53,
303
- note: "只对 scripts/ 豁免:TS 侧实测 0 条(包代码本来就不用 continue),908 行的门禁脚本改成嵌套 if 只会更难读"
304
- },
305
- "unicorn/max-nested-calls": {
306
- kind: "文体作用域",
307
- measured: 59,
308
- note: "只对 scripts/ 豁免:TS 侧实测 0 条;CLI 脚本里链式 map/filter/join 的深度由数据形状决定,不指向缺陷"
309
- },
310
- "unicorn/no-useless-undefined": {
311
- kind: "文体作用域",
312
- measured: 0,
313
- note: "脚本侧同上调;prod 仍按配置项 checkArguments:false 生效"
314
- },
315
- "react/set-state-in-effect": {
316
- kind: "契约冲突",
317
- measured: 16,
318
- note: "卡片用 effect 同步宿主态到 store 是挂载适配器形态;Compiler 建议的 render 期推导要改写 12 份适配器的状态流"
319
- },
320
- "react/no-deriving-state-in-effects": {
321
- kind: "契约冲突",
322
- measured: 7,
323
- note: "适配器形态里「props/state 派生值在 effect 对齐宿主」是挂载动作,不是可下沉到 render 的纯派生"
324
- },
325
- "react/exhaustive-effect-dependencies": {
326
- kind: "契约冲突",
327
- measured: 8,
328
- note: "Compiler 版依赖审计对适配器的内联闭包全量追问;与 react-hooks/exhaustive-deps(保留并已修)形成双门,留后者"
329
- },
330
- "react/hooks": {
331
- kind: "契约冲突",
332
- measured: 13,
333
- note: "「同一 hook 函数跨渲染同引用」在适配器的内联闭包形态下整体不成立;收敛批次把闭包提升后再回来开"
334
- },
335
- "react/refs": {
336
- kind: "契约冲突",
337
- measured: 1,
338
- note: "挂载适配器在 render 期读 ref 对齐宿主布局;收敛批次改成挂载后读"
339
- },
340
- "react/todo": {
341
- kind: "上游不推荐",
342
- measured: 6,
343
- note: "报的是 React 编译器 BuildHIR 自己不支持 try/catch(-finally) 的上游 TODO,不是代码缺陷"
344
- },
345
- "react/rules-of-hooks": {
346
- kind: "文体作用域",
347
- measured: 3,
348
- note: "只对用例文件豁免(TEST_FILE_OVERRIDES):测试架在组件树外直接驱动卡片适配 hook(useCardOf / useSyncExternalStore 桩),调用次数顺序由桩件固定。键用表载前缀 react/(诊断文案显示 react-hooks(...) 是显示名,配置键以 --rules 的 Source 列为准)"
349
- }
350
- };
351
- /**
352
- * 旧 `.oxlintrc.json` 里继承来、此次**取消豁免**且确实零代价的规则。
353
- *
354
- * 这里原来记着三条。"零代价"那句话是用 `oxlint --deny=<rule>` 量的,而 `--deny` 盖不住
355
- * override 层的 off(本文件头注释里有这条实测口径)——重测之后:`eslint/no-continue` 在
356
- * `scripts/` 有 53 条、`eslint/no-await-in-loop` 有 2 条,于是这两条按文体收窄回
357
- * `SCRIPTS_OVERRIDES`(TS 侧仍 0 条、仍生效),从这份"已作废"名单里撤掉。留在这儿的
358
- * 才是真正确认过零代价的。
359
- */
360
- const RETIRED_OFFS = ["typescript/no-empty-object-type"];
361
- const READONLY_SYNC_ALLOWLIST = [
362
- "existsSync",
363
- "readFileSync",
364
- "readdirSync",
365
- "readSync",
366
- "realpathSync",
367
- "statSync"
368
- ];
369
- const BASE_PLUGINS = [
370
- "eslint",
371
- "typescript",
372
- "unicorn",
373
- "oxc",
374
- "node",
375
- "promise",
376
- "vitest",
377
- "import",
378
- "react",
379
- "jsx-a11y"
380
- ];
381
- /**
382
- * oxlint 的 **JS plugin** 面(`jsPlugins`,官方标注 alpha、不受 semver 保护)挂
383
- * `eslint-plugin-sonarjs`,补上"同形/重复"这一类 oxlint 内建没有的判据。
384
- *
385
- * 入口必须是**能解析到文件的路径**:实测裸包名 `eslint-plugin-sonarjs` 在 pnpm 布局里
386
- * 从包目录报 `Cannot find module`(它装在 workspace 根的 node_modules 下),配置就整份加载失败。
387
- * 所以这里在装载期用 `createRequire` 从本模块解析,绝对路径不落进任何配置文件。
388
- */
389
- const SONARJS_ENTRY = createRequire(import.meta.url).resolve("eslint-plugin-sonarjs/cjs/plugin.js");
390
- /**
391
- * 逐条点名而不是通配:JS plugin 的 alpha 面里,一条规则改名或新增就可能整份配置解析失败
392
- * (oxlint 对未知规则名的行为是**整份配置拒绝加载**,比静默保绿更糟)。
393
- * 挂载集合与实测条数一并写在这里(owner 的口径:挂上就当场全修,不留待办、
394
- * 不靠关规则过关):`no-duplicate-string` 全仓 291 条、`cognitive-complexity` 22 条按代码修完,
395
- * 两条都用**默认阈值**,不调 option(调阈值就是把判据改成能过的那副样子)。
396
- */
397
- const SONARJS_RULES = {
398
- "sonarjs/no-identical-functions": "error",
399
- "sonarjs/no-duplicated-branches": "error",
400
- "sonarjs/no-all-duplicated-branches": "error",
401
- "sonarjs/no-collapsible-if": "error",
402
- "sonarjs/no-identical-conditions": "error",
403
- "sonarjs/no-identical-expressions": "error",
404
- "sonarjs/no-duplicate-string": "error",
405
- "sonarjs/cognitive-complexity": "error",
406
- "sonarjs/max-switch-cases": "error",
407
- "sonarjs/no-inverted-boolean-check": "error",
408
- "sonarjs/no-duplicate-in-composite": "error",
409
- "sonarjs/duplicates-in-character-class": "error",
410
- "sonarjs/no-useless-catch": "error",
411
- "sonarjs/no-extra-arguments": "error",
412
- "sonarjs/no-redundant-assignments": "error",
413
- "sonarjs/prefer-single-boolean-return": "error",
414
- "sonarjs/label-position": "error"
415
- };
416
- const BASE_CATEGORIES = {
417
- correctness: "error",
418
- suspicious: "error",
419
- style: "error",
420
- perf: "error",
421
- restriction: "error",
422
- pedantic: "error",
423
- nursery: "error"
424
- };
425
- /**
426
- * `options` 是 **root-only** 字段:从上层目录一次 lint 多个包时,oxlint 会硬报
427
- * `The options.typeAware option is only supported in the root config`。
428
- * 因此「每包一份配置」的拓扑只能以包目录为 cwd 跑(`pnpm -r run lint` 天然满足),
429
- * workspace 根那份只管 `scripts/`。
430
- */
431
- const BASE_OPTIONS = {
432
- typeAware: true,
433
- typeCheck: true,
434
- denyWarnings: true,
435
- maxWarnings: 0,
436
- reportUnusedDisableDirectives: "error",
437
- respectEslintDisableDirectives: false
438
- };
439
- /**
440
- * 排除面只留"生成物与非源码树":`client.js`/`host.js` 是 build 产物,`coverage/`、`dist/`、
441
- * `.toolchain/`、`node_modules/` 同理。
442
- *
443
- * 这里原本还排着 `*.config.mjs`、`*.config.ts`、`build-client.mjs`、`build-host.mjs`,此次删掉:
444
- * 那四类是**人写的源码**,排掉就等于给它们开了盲区——实测因此攒过 5 条指向根本不会被 lint 的
445
- * 规则的僵尸 `oxlint-disable`(quality-gate/build-client.mjs,删除后无任何判据报出),
446
- * 而且和 `scripts/*.mjs` 的处理自相矛盾(同为 node 直跑的 .mjs,那边全量在 lint)。
447
- * 配置文件与 bundler 脚本现在和各包源码走同一套规则,`*.mjs` 仍由 SCRIPTS_OVERRIDES
448
- * 按文体收窄(类型信息类规则对无类型 .mjs 无从计算)。
449
- */
450
- const BASE_IGNORE_PATTERNS = [
451
- "**/node_modules/**",
452
- ".toolchain/**",
453
- "coverage/**",
454
- "dist/**",
455
- "client.js",
456
- "host.js"
457
- ];
458
- /**
459
- * oxlint 认识的 vitest 规则全集(取自 `oxlint --rules` 的表格,73 条)。整族在基线 off,再由
460
- * TEST_FILE_OVERRIDES 在用例文件上 on —— 理由见
461
- * OFF_JUSTIFICATIONS 里的 `vitest/*`。列成数组而不是散着写,是为了让「上游新增一条 vitest 规则」
462
- * 默认落在生产文件之外,而不是默认作用到每一个 host.ts。
463
- */
464
- const VITEST_ALL_RULES = [
465
- "vitest/consistent-each-for",
466
- "vitest/consistent-test-filename",
467
- "vitest/consistent-test-it",
468
- "vitest/consistent-vitest-vi",
469
- "vitest/expect-expect",
470
- "vitest/hoisted-apis-on-top",
471
- "vitest/max-expects",
472
- "vitest/max-nested-describe",
473
- "vitest/no-alias-methods",
474
- "vitest/no-commented-out-tests",
475
- "vitest/no-conditional-expect",
476
- "vitest/no-conditional-in-test",
477
- "vitest/no-conditional-tests",
478
- "vitest/no-disabled-tests",
479
- "vitest/no-duplicate-hooks",
480
- "vitest/no-focused-tests",
481
- "vitest/no-hooks",
482
- "vitest/no-identical-title",
483
- "vitest/no-import-node-test",
484
- "vitest/no-importing-vitest-globals",
485
- "vitest/no-interpolation-in-snapshots",
486
- "vitest/no-large-snapshots",
487
- "vitest/no-mocks-import",
488
- "vitest/no-restricted-matchers",
489
- "vitest/no-restricted-vi-methods",
490
- "vitest/no-standalone-expect",
491
- "vitest/no-test-prefixes",
492
- "vitest/no-test-return-statement",
493
- "vitest/no-unneeded-async-expect-function",
494
- "vitest/padding-around-after-all-blocks",
495
- "vitest/padding-around-test-blocks",
496
- "vitest/prefer-called-exactly-once-with",
497
- "vitest/prefer-called-once",
498
- "vitest/prefer-called-times",
499
- "vitest/prefer-called-with",
500
- "vitest/prefer-comparison-matcher",
501
- "vitest/prefer-describe-function-title",
502
- "vitest/prefer-each",
503
- "vitest/prefer-equality-matcher",
504
- "vitest/prefer-expect-assertions",
505
- "vitest/prefer-expect-resolves",
506
- "vitest/prefer-expect-type-of",
507
- "vitest/prefer-hooks-in-order",
508
- "vitest/prefer-hooks-on-top",
509
- "vitest/prefer-import-in-mock",
510
- "vitest/prefer-importing-vitest-globals",
511
- "vitest/prefer-lowercase-title",
512
- "vitest/prefer-mock-promise-shorthand",
513
- "vitest/prefer-mock-return-shorthand",
514
- "vitest/prefer-snapshot-hint",
515
- "vitest/prefer-spy-on",
516
- "vitest/prefer-strict-boolean-matchers",
517
- "vitest/prefer-strict-equal",
518
- "vitest/prefer-to-be",
519
- "vitest/prefer-to-be-falsy",
520
- "vitest/prefer-to-be-object",
521
- "vitest/prefer-to-be-truthy",
522
- "vitest/prefer-to-contain",
523
- "vitest/prefer-to-have-been-called-times",
524
- "vitest/prefer-to-have-length",
525
- "vitest/prefer-todo",
526
- "vitest/require-awaited-expect-poll",
527
- "vitest/require-hook",
528
- "vitest/require-local-test-context-for-concurrent-snapshots",
529
- "vitest/require-mock-type-parameters",
530
- "vitest/require-test-timeout",
531
- "vitest/require-to-throw-message",
532
- "vitest/require-top-level-describe",
533
- "vitest/valid-describe-callback",
534
- "vitest/valid-expect",
535
- "vitest/valid-expect-in-promise",
536
- "vitest/valid-title",
537
- "vitest/warn-todo"
538
- ];
539
- /** 整族在用例文件上开回来时仍然豁免的那几条(理由逐条在 OFF_JUSTIFICATIONS)。 */
540
- const VITEST_STILL_OFF_IN_TEST_FILES = /* @__PURE__ */ new Set([
541
- "vitest/max-expects",
542
- "vitest/no-conditional-in-test",
543
- "vitest/no-hooks",
544
- "vitest/no-importing-vitest-globals",
545
- "vitest/prefer-called-times",
546
- "vitest/prefer-describe-function-title",
547
- "vitest/prefer-expect-assertions",
548
- "vitest/prefer-import-in-mock",
549
- "vitest/prefer-importing-vitest-globals",
550
- "vitest/prefer-to-be-falsy",
551
- "vitest/prefer-to-be-truthy",
552
- "vitest/require-test-timeout"
553
- ]);
554
- const BASE_RULES = {
555
- "eslint/one-var": ["error", "never"],
556
- "eslint/func-style": [
557
- "error",
558
- "declaration",
559
- {
560
- allowArrowFunctions: true,
561
- overrides: { namedExports: "ignore" }
562
- }
563
- ],
564
- "eslint/id-length": ["error", { exceptions: [
565
- "a",
566
- "i",
567
- "t",
568
- "x",
569
- "y",
570
- "z"
571
- ] }],
572
- "eslint/init-declarations": [
573
- "error",
574
- "never",
575
- { ignoreForLoopInit: true }
576
- ],
577
- "eslint/max-lines": ["error", {
578
- max: 2e3,
579
- skipBlankLines: true,
580
- skipComments: true
581
- }],
582
- "eslint/max-lines-per-function": ["error", {
583
- max: 120,
584
- skipBlankLines: true,
585
- skipComments: true
586
- }],
587
- "eslint/complexity": "error",
588
- "eslint/max-params": ["error", { max: 5 }],
589
- "eslint/max-statements": ["error", 50],
590
- "eslint/no-console": ["error", { allow: [
591
- "warn",
592
- "error",
593
- "info"
594
- ] }],
595
- "eslint/no-param-reassign": "error",
596
- "eslint/no-underscore-dangle": ["error", { allow: [
597
- "__memoryInsightCardApplied",
598
- "__memoryTdaiCardApplied",
599
- "__sessionRescueApplied",
600
- "__wukilDevToolsCardApplied"
601
- ] }],
602
- "eslint/no-magic-numbers": "off",
603
- "eslint/sort-keys": "off",
604
- "eslint/sort-imports": "off",
605
- "eslint/capitalized-comments": "off",
606
- "eslint/no-ternary": "off",
607
- "eslint/no-undefined": "off",
608
- "eslint/no-void": "off",
609
- "eslint/no-duplicate-imports": "off",
610
- "eslint/require-await": "off",
611
- "import/no-named-export": "off",
612
- "import/group-exports": "off",
613
- "import/exports-last": "off",
614
- "import/prefer-default-export": "off",
615
- "import/no-nodejs-modules": "off",
616
- "import/no-relative-parent-imports": "off",
617
- "import/max-dependencies": "off",
618
- "import/no-default-export": "off",
619
- "import/no-anonymous-default-export": "off",
620
- "import/no-named-as-default-member": "off",
621
- "import/consistent-type-specifier-style": ["error", "prefer-top-level"],
622
- "import/no-duplicates": "error",
623
- "import/no-cycle": "error",
624
- "import/named": "error",
625
- "import/first": "error",
626
- "import/newline-after-import": "error",
627
- "node/no-process-env": "off",
628
- "node/no-sync": ["error", { ignores: READONLY_SYNC_ALLOWLIST }],
629
- "node/no-top-level-await": "error",
630
- "oxc/no-async-await": "off",
631
- "oxc/no-optional-chaining": "off",
632
- "oxc/no-rest-spread-properties": "off",
633
- "promise/always-return": "error",
634
- "promise/no-promise-in-callback": ["error", { exemptDeclarations: true }],
635
- "promise/no-return-wrap": "error",
636
- "promise/param-names": "error",
637
- "promise/prefer-await-to-then": "error",
638
- "typescript/consistent-type-imports": ["error", { disallowTypeAnnotations: false }],
639
- "typescript/explicit-function-return-type": ["error", {
640
- allowDirectConstAssertionInArrowFunctions: true,
641
- allowExpressions: true,
642
- allowHigherOrderFunctions: true,
643
- allowIIFEs: true,
644
- allowTypedFunctionExpressions: true,
645
- allowedNames: ["handler", "callback"]
646
- }],
647
- "typescript/no-explicit-any": "error",
648
- "typescript/no-floating-promises": "error",
649
- "typescript/no-unsafe-assignment": "error",
650
- "typescript/no-unsafe-type-assertion": "error",
651
- "typescript/no-non-null-assertion": "error",
652
- "typescript/require-await": "error",
653
- "typescript/prefer-readonly-parameter-types": "off",
654
- "typescript/promise-function-async": ["error", { checkArrowFunctions: false }],
655
- "typescript/consistent-return": ["error", { treatUndefinedAsUnspecified: true }],
656
- "typescript/only-throw-error": "error",
657
- "unicorn/no-array-reduce": ["error", { allowSimpleOperations: true }],
658
- "unicorn/no-null": "off",
659
- "unicorn/no-useless-undefined": ["error", { checkArguments: false }],
660
- "react/set-state-in-effect": "off",
661
- "react/no-deriving-state-in-effects": "off",
662
- "react/exhaustive-effect-dependencies": "off",
663
- "react/hooks": "off",
664
- "react/refs": "off",
665
- "react/todo": "off",
666
- ...Object.fromEntries(VITEST_ALL_RULES.map((rule) => [rule, "off"]))
667
- };
668
- /**
669
- * 用例文件(含 `test/*.ts` 夹具与 helper)共享的豁免:规模类,以及「桩件要跨官方名义类型」
670
- * 的类型类判据。vitest 的用例结构类判据不在这里 —— 它们在基线整族 off、只在
671
- * TEST_FILE_OVERRIDES 里开回来,所以 helper 模块天然不在射程内,旧配置那份按目录豁免的
672
- * TEST_HELPER_OVERRIDES 因此作废。
673
- */
674
- const TEST_OVERRIDES = {
675
- files: [
676
- "**/*.test.ts",
677
- "**/*.test.tsx",
678
- "**/test/**",
679
- "**/__tests__/**"
680
- ],
681
- rules: {
682
- "typescript/no-non-null-assertion": "off",
683
- "typescript/no-unsafe-type-assertion": "off",
684
- "typescript/no-explicit-any": "off",
685
- "typescript/require-await": "off",
686
- "eslint/max-statements": "off",
687
- "eslint/max-lines": "off",
688
- "eslint/max-lines-per-function": "off",
689
- "node/no-top-level-await": "off"
690
- }
691
- };
692
- /**
693
- * vitest 的用例结构类判据**只**在这里生效。
694
- * `expect-expect` 的 `assertFunctionNames` 取 vitest flavor 的官方默认
695
- * (`["expect", "expectTypeOf", "assert", "assertType"]`):本仓用例里有相当一部分用
696
- * `node:assert/strict` 断言,那不是「缺断言」,而是这条规则在 jest flavor 默认值下漏判
697
- * ——实测 17 条里 5 条属于这一族,补上默认值后就不报了。
698
- *
699
- * 剩下两族用规则自带的选项解决,不关规则、也不改判据方向:
700
- * - 断言写在包内 helper 里(`assertSameLedger()` 一类):`assertFunctionNames` 就是
701
- * "哪些函数算断言"的名单,走 {@link makeTestFileOverrides} 的 `assertionHelpers` 报名字。
702
- * 名单只收**裸标识符**:带 `*` / `.` 的通配会把"任何调用都算断言"写进配置,等于掏空规则,直接抛。
703
- * - 标题以大写术语开头(`PTC`、`W5`、`B1` 这类档位/模式名):`allowedPrefixes` 是
704
- * prefer-lowercase-title 给的同一条口径(官方例子就是 HTTP 方法前缀)。走 `titlePrefixes` 报名字。
705
- * 反过来的做法——把标题改成 `pTC` / `w5`——会把领域名字改成坏名字,本仓不采取。
706
- */
707
- const VITEST_ASSERTION_DEFAULTS = [
708
- "expect",
709
- "expectTypeOf",
710
- "assert",
711
- "assertType"
712
- ];
713
- const BARE_IDENTIFIER_RE = /^[A-Za-z_$][\w$]*$/u;
714
- const TEST_FILE_OVERRIDES = {
715
- files: [
716
- "**/*.test.ts",
717
- "**/*.test.tsx",
718
- "**/__tests__/**/*.spec.ts"
719
- ],
720
- rules: {
721
- ...Object.fromEntries(VITEST_ALL_RULES.filter((rule) => !VITEST_STILL_OFF_IN_TEST_FILES.has(rule)).map((rule) => [rule, "error"])),
722
- "vitest/expect-expect": ["error", { assertFunctionNames: VITEST_ASSERTION_DEFAULTS }],
723
- "react/rules-of-hooks": "off"
724
- }
725
- };
726
- /**
727
- * 生成用例文件的 override(vitest 结构类判据的开与关都在这张表里)。
728
- * @param opts - 本包需要用规则自带选项登记的名单(见文件头的两族说明)。
729
- * @returns 可直接放进 `overrides` 的配置项。
730
- */
731
- function makeTestFileOverrides(opts = {}) {
732
- const assertionHelpers = opts.assertionHelpers ?? [];
733
- const titlePrefixes = opts.titlePrefixes ?? [];
734
- const illegal = [...assertionHelpers, ...titlePrefixes].filter((name) => !BARE_IDENTIFIER_RE.test(name));
735
- if (illegal.length > 0) throw new Error(`assertionHelpers / titlePrefixes 只收裸标识符,不接受通配或带点形式(前者等于把 vitest/expect-expect 掏空):${illegal.join(", ")}`);
736
- const names = [.../* @__PURE__ */ new Set([...VITEST_ASSERTION_DEFAULTS, ...assertionHelpers])];
737
- return {
738
- ...TEST_FILE_OVERRIDES,
739
- rules: {
740
- ...TEST_FILE_OVERRIDES.rules,
741
- "vitest/expect-expect": ["error", { assertFunctionNames: names }],
742
- ...titlePrefixes.length === 0 ? {} : { "vitest/prefer-lowercase-title": ["error", { allowedPrefixes: [...new Set(titlePrefixes)] }] }
743
- }
744
- };
745
- }
746
- const SCRIPTS_OVERRIDES = {
747
- files: [
748
- "scripts/**",
749
- "*.mjs",
750
- "*.mts"
751
- ],
752
- rules: {
753
- "eslint/no-continue": "off",
754
- "unicorn/max-nested-calls": "off",
755
- "eslint/id-length": ["error", { exceptions: [
756
- "a",
757
- "i",
758
- "t",
759
- "x",
760
- "y",
761
- "z",
762
- "n",
763
- "p",
764
- "s",
765
- "v",
766
- "e",
767
- "f",
768
- "ok",
769
- "fs",
770
- "id"
771
- ] }],
772
- "unicorn/no-process-exit": "off",
773
- "unicorn/no-useless-undefined": "off",
774
- "typescript/no-non-null-assertion": "off",
775
- "node/no-sync": "off",
776
- "node/no-top-level-await": "off",
777
- "typescript/no-unsafe-member-access": "off",
778
- "typescript/no-unsafe-call": "off",
779
- "typescript/no-unsafe-assignment": "off",
780
- "typescript/no-unsafe-return": "off",
781
- "typescript/no-unsafe-argument": "off",
782
- "typescript/strict-boolean-expressions": "off",
783
- "typescript/no-implied-eval": "off",
784
- "typescript/require-array-sort-compare": "off"
785
- }
786
- };
787
- /**
788
- * 造一个"只读依据视图":基线的 OFF_JUSTIFICATIONS 加上本次调用带进来的包级例外。
789
- * **刻意不往导出的那张表里写**(早先的实现是 `OFF_JUSTIFICATIONS[rule] = justification`):
790
- * 配置模块在同一个 Node 进程里只求值一次,写过的那张表会让 A 包的例外替 B 包的无依据 off 背书
791
- * ——正是这道门要拦的情形,而且单测里已经复现过一次串味(上一条用例的例外让下一条负例变成正例)。
792
- */
793
- function makeJustificationView(offReasons) {
794
- const merged = {
795
- ...OFF_JUSTIFICATIONS,
796
- ...offReasons
797
- };
798
- return (rule) => {
799
- const direct = merged[rule];
800
- if (direct !== void 0) return direct;
801
- const slash = rule.indexOf("/");
802
- return slash === -1 ? void 0 : merged[`${rule.slice(0, slash)}/*`];
803
- };
804
- }
805
- /**
806
- * 每一条 `"off"` 都必须在 OFF_JUSTIFICATIONS 里有依据。抛错而不是「记一行日志」:
807
- * 配置加载失败会让 oxlint 当场红,把规则关掉这件事不可能静默穿过这道门
808
- * (实测过的同类失效形态:一条不存在的规则名让整份配置解析失败,13 个包全部静默 lint 0 条却 exit 0)。
809
- */
810
- function assertOffsDocumented(where, rules, lookup) {
811
- if (rules === void 0) return;
812
- const undocumented = [];
813
- for (const [rule, setting] of Object.entries(rules)) if ((Array.isArray(setting) ? setting[0] : setting) === "off") {
814
- const found = lookup(rule);
815
- if (found === void 0 || found.note.trim().length === 0) undocumented.push(rule);
816
- }
817
- if (undocumented.length > 0) throw new Error(`${where} 里这些规则被关成 off 但没有依据(OFF_JUSTIFICATIONS 查不到,或 note 为空):${undocumented.join(", ")}。要么在代码里修,要么给它补一条 kind + measured + note。`);
818
- }
819
- function definePluginConfig(options = {}) {
820
- const { syncWrites = [], extraRules = {}, extraOverrides = [], extraIgnorePatterns = [], jsPlugins, offReasons = {}, assertionHelpers = [], titlePrefixes = [] } = options;
821
- const lookup = makeJustificationView(offReasons);
822
- const rules = {
823
- ...BASE_RULES,
824
- ...SONARJS_RULES,
825
- ...syncWrites.length > 0 ? { "node/no-sync": ["error", { ignores: [.../* @__PURE__ */ new Set([...READONLY_SYNC_ALLOWLIST, ...syncWrites])] }] } : {},
826
- ...extraRules
827
- };
828
- const overrides = [
829
- TEST_OVERRIDES,
830
- makeTestFileOverrides({
831
- assertionHelpers,
832
- titlePrefixes
833
- }),
834
- SCRIPTS_OVERRIDES,
835
- ...extraOverrides
836
- ];
837
- assertOffsDocumented("rules", rules, lookup);
838
- for (const [index, override] of overrides.entries()) assertOffsDocumented(`overrides[${index}]`, override.rules, lookup);
839
- return defineConfig({
840
- plugins: BASE_PLUGINS,
841
- env: {
842
- node: true,
843
- es2024: true,
844
- browser: true
845
- },
846
- options: BASE_OPTIONS,
847
- categories: BASE_CATEGORIES,
848
- ignorePatterns: [...BASE_IGNORE_PATTERNS, ...extraIgnorePatterns],
849
- jsPlugins: [SONARJS_ENTRY, ...jsPlugins ?? []],
850
- rules,
851
- overrides
852
- });
853
- }
854
- //#endregion
855
- export { BASE_RULES, OFF_JUSTIFICATIONS, READONLY_SYNC_ALLOWLIST, RETIRED_OFFS, SCRIPTS_OVERRIDES, TEST_FILE_OVERRIDES, TEST_OVERRIDES, VITEST_ASSERTION_DEFAULTS, definePluginConfig, makeTestFileOverrides };
1
+ import{createRequire as e}from"node:module";import{defineConfig as t}from"oxlint";const n=`同 no-unsafe-member-access`,r={"vitest/*":{kind:`文体作用域`,measured:7,note:`用例结构类判据在非用例文件上无从计算`},"vitest/no-hooks":{kind:`上游不推荐`,measured:181,note:`禁用 hook 本身;本仓按官方 vitest 用法大量使用 before*/after*`},"vitest/no-conditional-in-test":{kind:`文体作用域`,measured:523,note:`平台/宿主版本分支是刻意的(macOS 与 Linux 的家目录形状、0.1.7 与 0.1.6 的 API 面)`},"vitest/max-expects":{kind:`上游不推荐`,measured:163,note:`给单条用例的断言条数设上限属口味,不指向缺陷`},"vitest/prefer-expect-assertions":{kind:`上游不推荐`,measured:2902,note:`要求每个 async 用例手写 expect.assertions(n),是与断言强度无关的计数仪式`},"vitest/require-test-timeout":{kind:`上游不推荐`,measured:2892,note:`要求每条用例显式 timeout;本仓用泵式假时钟,超时由 13 份 vitest 配置统一给`},"vitest/prefer-importing-vitest-globals":{kind:`实测否证`,measured:0,note:`autofix 方向是删掉显式 import 改回裸全局;13 份 vitest 配置都没开 globals:true,应用后当场 3605 处 TS2593、测试全废`},"vitest/no-importing-vitest-globals":{kind:`实测否证`,measured:133,note:`与上一条同族、方向相反,两条同时开就是把两种写法都判死`},"vitest/prefer-import-in-mock":{kind:`实测否证`,measured:6,note:"动态形态按完整模块面校验工厂返回值,而 @types/node 的 os、@types/react 都是 `export =` 形状、类型面上没有 default;实测三处 TS2769+TS2740/2741,react 那处无论怎么 spread 都补不出 default"},"vitest/prefer-lowercase-title":{kind:`上游不推荐`,measured:171,note:`只在用例文件里生效:它带 autofix,实测一轮 oxlint --fix 静默小写化了 260 条标题(含 nO_VERIFY 这种坏名)。包级 titlePrefixes 只留真正的技术名(GET/CSRF/组件名…):非技术名(工单号一类)不该借这条豁免留在标题里,而中文开头的标题天然满足判据(无大小写)⇒ 免检面能收多小收多小。实测 oxlint 前缀一命中就把整条标题免检,所以陈旧条目由配置基线自检按『免检面未被使用』点名,不靠人工巡检`},"vitest/prefer-called-times":{kind:`对侧互斥`,measured:0,note:`探针实测(同文件四种写法各跑一次):toHaveBeenCalledTimes(1) 被 prefer-called-once 判死、toHaveBeenCalledOnce() 被本条判死、expect(spy.mock.calls).toHaveLength(1) 被 prefer-to-have-been-called-times 判死,只有 n>=2 能同时满足三条 ⇒ 三条同开等于「单次调用」这条断言无法书写(实测逼得消费方改用 assert.equal(spy.mock.calls.length, 1) 绕行)。留 prefer-called-once 与 prefer-to-have-been-called-times 两条方向一致的组合,关本条`},"vitest/prefer-describe-function-title":{kind:`对侧互斥`,measured:48,note:"要求 `describe(fn)`,而 valid-title 要求字符串字面量(oxlint 1.85 的 valid-title 是 DummyRule、无任何选项);上游 recommended 收的是 valid-title,取它,26 处 `describe(fn)` 按代码改成同名字符串"},"vitest/prefer-to-be-truthy":{kind:`实测否证`,measured:117,note:`把 toStrictEqual(true) 放宽成 toTruthy(),接受任意真值是削弱断言`},"vitest/prefer-to-be-falsy":{kind:`实测否证`,measured:89,note:`同族,放宽成 toFalsy() 同样是削弱断言`},"import/no-default-export":{kind:`契约冲突`,measured:38,note:`官方 cordis 入口就是 default export(vendor/loader/src/index.ts:201 先 unwrap default),12 条正是 12 个 bundle 入口`},"import/no-named-export":{kind:`契约冲突`,measured:895,note:`13 个包与上游 deepseek-harness 一样以具名导出 + 子路径说明符对外暴露`},"import/prefer-default-export":{kind:`契约冲突`,measured:58,note:`与 no-default-export 同族、方向与 cordis 入口相反`},"import/no-nodejs-modules":{kind:`契约冲突`,measured:448,note:"host 半就跑在 node 上,`node:` 内置模块是运行环境本身而不是越界"},"import/no-named-as-default-member":{kind:`契约冲突`,measured:47,note:`对具名导出的默认对象做成员访问是本仓消费官方声明的常态`},"import/no-anonymous-default-export":{kind:`契约冲突`,measured:12,note:`官方 cordis 入口是匿名对象 default export(registry.ts:92 读 name/Config/apply),具名化等于改官方形态`},"eslint/no-duplicate-imports":{kind:`实测否证`,measured:166,note:`ESLint 官方已废弃(不区分 import type),由 import/no-duplicates 取代`},"import/group-exports":{kind:`上游不推荐`,measured:766,note:`要求单点导出,与具名导出面冲突`},"import/exports-last":{kind:`上游不推荐`,measured:528,note:`要求导出语句在文件末尾`},"import/no-relative-parent-imports":{kind:`上游不推荐`,measured:277,note:`包内 lib/ 与 test/ 的相对父级引用是既有结构`},"import/max-dependencies":{kind:`上游不推荐`,measured:25,note:`给 import 条数设上限;host 半消费官方服务面天然依赖多`},"node/no-process-env":{kind:`上游不推荐`,measured:51,note:`环境变量是本仓的输入面(DSH_HOME、profile 目录、构建期开关);eslint-plugin-n 的 recommended 也不含本条`},"eslint/no-magic-numbers":{kind:`上游不推荐`,measured:5774,note:`把 HTTP 状态码/毫秒阈值抽成常量的计数规则`},"eslint/sort-keys":{kind:`上游不推荐`,measured:4101,note:`对象键排序;成对字面量与 locale 字典的可读性会一起牺牲`},"eslint/sort-imports":{kind:`上游不推荐`,measured:675,note:`字母序 import,与 oxfmt 的分组形态冲突`},"eslint/capitalized-comments":{kind:`上游不推荐`,measured:2388,note:`本仓注释以中文为主,「首字母大写」对中文注释无从判定`},"eslint/no-ternary":{kind:`上游不推荐`,measured:1024,note:`禁三元;改写成 if/else 并不会更正确`},"eslint/no-undefined":{kind:`上游不推荐`,measured:2009,note:"禁 `undefined` 字面量,与本仓大量显式可选返回形态冲突"},"eslint/no-void":{kind:`上游不推荐`,measured:172,note:"禁 `void` 前缀,而 `void promise` 正是消解 no-floating-promises 的官方写法"},"unicorn/no-null":{kind:`上游不推荐`,measured:788,note:`禁 null;官方声明与 JSON 边界大量用 null,改成 undefined 会改变序列化形状`},"typescript/prefer-readonly-parameter-types":{kind:`上游不推荐`,measured:2433,note:`要求每个入参类型全 readonly;官方声明面(SessionEvent/ConfigForm 等)是可变的,改不动也不该改`},"oxc/no-async-await":{kind:`对侧互斥`,measured:2160,note:`禁 async/await,而同表里 promise-function-async、require-await、no-floating-promises 都在推 async`},"oxc/no-optional-chaining":{kind:`对侧互斥`,measured:1354,note:"禁 `?.`,而 typescript/prefer-optional-chain 正在要求把 && 链改成 `?.`"},"oxc/no-rest-spread-properties":{kind:`对侧互斥`,measured:550,note:`禁对象 spread,而 ES2024 目标与官方声明的投影写法都靠它`},"typescript/no-explicit-any":{kind:`文体作用域`,measured:1,note:`用例里的桩件面(mini-react 之类)需要 any 才塞得进官方签名`},"typescript/no-non-null-assertion":{kind:`文体作用域`,measured:536,note:"用例里 `!` 是「前驱条件已成立」的最短写法"},"typescript/no-unsafe-type-assertion":{kind:`文体作用域`,measured:844,note:"桩件需要 `as never` / `as unknown as` 跨官方名义类型"},"typescript/require-await":{kind:`文体作用域`,measured:467,note:`官方 async 契约的同步实现与测试替身撑不住这条;prod 侧的条数按代码修,不进豁免`},"eslint/require-await":{kind:`对侧互斥`,measured:481,note:`它与 typescript/require-await 是同一条判据的两份实现,不是两条判据:探针实测(一段无 await 的 async 函数、--type-aware)单开各自都报、同开报两次 ⇒ 2 errors / 1 defect。按 typescript-eslint 官方的 extension-rule 口径裁决:留带类型信息的扩展那条、关基线那条。覆盖面没有因此变小:全仓逐行去重实测 514 行两点同报、typescript 独有 0 行、eslint 独有只落在用例面与配置自身的中间态;而 --type-aware 实测对 .mjs 也会由扩展那条报出,所以 scripts/*.mjs 面不需要基线那条补位。用例文件由 TEST_OVERRIDES 整族豁免`},"eslint/max-statements":{kind:`文体作用域`,measured:549,note:`用例体天然长(given/when/then 展开)`},"eslint/max-lines-per-function":{kind:`文体作用域`,measured:365,note:`同上`},"eslint/max-lines":{kind:`文体作用域`,measured:54,note:`单个用例文件的长度不作为缺陷信号`},"node/no-sync":{kind:`文体作用域`,measured:299,note:`构建脚本与门禁脚本按设计同步读;生产代码的写侧由每包 syncWrites 逐项登记,不在豁免里`},"node/no-top-level-await":{kind:`文体作用域`,measured:53,note:`脚本以 node 直接执行,顶层 await 是 ESM 常态;prod 侧 0 条`},"typescript/strict-boolean-expressions":{kind:`文体作用域`,measured:0,note:`scripts/*.mjs 无类型面,隐式 any 上无从判定`},"typescript/no-implied-eval":{kind:`文体作用域`,measured:0,note:`同上:这条要类型信息才能判`},"typescript/no-unsafe-member-access":{kind:`文体作用域`,measured:0,note:`无类型标注的脚本里每个值都落进隐式 any`},"typescript/no-unsafe-call":{kind:`文体作用域`,measured:0,note:n},"typescript/no-unsafe-assignment":{kind:`文体作用域`,measured:0,note:n},"typescript/no-unsafe-return":{kind:`文体作用域`,measured:0,note:n},"typescript/no-unsafe-argument":{kind:`文体作用域`,measured:0,note:n},"typescript/require-array-sort-compare":{kind:`文体作用域`,measured:0,note:`无类型脚本里「比较器缺失」判不出来`},"unicorn/no-process-exit":{kind:`文体作用域`,measured:2,note:`CLI 脚本的 process.exit 就是它的返回方式`},"eslint/no-continue":{kind:`文体作用域`,measured:53,note:`只对 scripts/ 豁免:TS 侧实测 0 条(包代码本来就不用 continue),908 行的门禁脚本改成嵌套 if 只会更难读`},"unicorn/max-nested-calls":{kind:`文体作用域`,measured:59,note:`只对 scripts/ 豁免:TS 侧实测 0 条;CLI 脚本里链式 map/filter/join 的深度由数据形状决定,不指向缺陷`},"unicorn/no-useless-undefined":{kind:`文体作用域`,measured:0,note:`脚本侧同上调;prod 仍按配置项 checkArguments:false 生效`},"react/set-state-in-effect":{kind:`契约冲突`,measured:16,note:`卡片用 effect 同步宿主态到 store 是挂载适配器形态;Compiler 建议的 render 期推导要改写 12 份适配器的状态流`},"react/no-deriving-state-in-effects":{kind:`契约冲突`,measured:7,note:`适配器形态里「props/state 派生值在 effect 对齐宿主」是挂载动作,不是可下沉到 render 的纯派生`},"react/exhaustive-effect-dependencies":{kind:`契约冲突`,measured:8,note:`Compiler 版依赖审计对适配器的内联闭包全量追问;与 react-hooks/exhaustive-deps(保留并已修)形成双门,留后者`},"react/hooks":{kind:`契约冲突`,measured:13,note:`「同一 hook 函数跨渲染同引用」在适配器的内联闭包形态下整体不成立;收敛批次把闭包提升后再回来开`},"react/refs":{kind:`契约冲突`,measured:1,note:`挂载适配器在 render 期读 ref 对齐宿主布局;收敛批次改成挂载后读`},"react/todo":{kind:`上游不推荐`,measured:6,note:`报的是 React 编译器 BuildHIR 自己不支持 try/catch(-finally) 的上游 TODO,不是代码缺陷`},"react/rules-of-hooks":{kind:`文体作用域`,measured:3,note:`只对用例文件豁免(TEST_FILE_OVERRIDES):测试架在组件树外直接驱动卡片适配 hook(useCardOf / useSyncExternalStore 桩),调用次数顺序由桩件固定。键用表载前缀 react/(诊断文案显示 react-hooks(...) 是显示名,配置键以 --rules 的 Source 列为准)`}},i=[`typescript/no-empty-object-type`],a=[`existsSync`,`readFileSync`,`readdirSync`,`readSync`,`realpathSync`,`statSync`],o=[`eslint`,`typescript`,`unicorn`,`oxc`,`node`,`promise`,`vitest`,`import`,`react`,`jsx-a11y`],s=[`eslint-plugin-sonarjs`,`eslint-plugin-sonarjs/cjs/plugin.js`];function c(){let t=e(import.meta.url),n=[];for(let e of s)try{return t.resolve(e)}catch{n.push(e)}throw Error(`oxlint 基线要挂 eslint-plugin-sonarjs,但从本包解析不到它(试过 ${n.join(`、`)})。要么在本包 devDependencies 里装 eslint-plugin-sonarjs,要么把自己解析到的入口经 definePluginConfig({ jsPlugins: [...] }) 传进来——带了入口就不会再解析(那条提示才真正兑现)。`)}function l(e,t=c){let n=e??[],r=null;try{r=t()}catch(e){if(n.length===0)throw e}return r===null?[...n]:[r,...n.filter(e=>e!==r)]}const u={"sonarjs/no-identical-functions":`error`,"sonarjs/no-duplicated-branches":`error`,"sonarjs/no-all-duplicated-branches":`error`,"sonarjs/no-collapsible-if":`error`,"sonarjs/no-identical-conditions":`error`,"sonarjs/no-identical-expressions":`error`,"sonarjs/no-duplicate-string":`error`,"sonarjs/cognitive-complexity":`error`,"sonarjs/max-switch-cases":`error`,"sonarjs/no-inverted-boolean-check":`error`,"sonarjs/no-duplicate-in-composite":`error`,"sonarjs/duplicates-in-character-class":`error`,"sonarjs/no-useless-catch":`error`,"sonarjs/no-extra-arguments":`error`,"sonarjs/no-redundant-assignments":`error`,"sonarjs/prefer-single-boolean-return":`error`,"sonarjs/label-position":`error`},d={correctness:`error`,suspicious:`error`,style:`error`,perf:`error`,restriction:`error`,pedantic:`error`,nursery:`error`},f={typeAware:!0,typeCheck:!0,denyWarnings:!0,maxWarnings:0,reportUnusedDisableDirectives:`error`,respectEslintDisableDirectives:!1},p=[`**/node_modules/**`,`.toolchain/**`,`coverage/**`,`dist/**`,`client.js`,`host.js`],m=/* @__PURE__ */ `vitest/consistent-each-for.vitest/consistent-test-filename.vitest/consistent-test-it.vitest/consistent-vitest-vi.vitest/expect-expect.vitest/hoisted-apis-on-top.vitest/max-expects.vitest/max-nested-describe.vitest/no-alias-methods.vitest/no-commented-out-tests.vitest/no-conditional-expect.vitest/no-conditional-in-test.vitest/no-conditional-tests.vitest/no-disabled-tests.vitest/no-duplicate-hooks.vitest/no-focused-tests.vitest/no-hooks.vitest/no-identical-title.vitest/no-import-node-test.vitest/no-importing-vitest-globals.vitest/no-interpolation-in-snapshots.vitest/no-large-snapshots.vitest/no-mocks-import.vitest/no-restricted-matchers.vitest/no-restricted-vi-methods.vitest/no-standalone-expect.vitest/no-test-prefixes.vitest/no-test-return-statement.vitest/no-unneeded-async-expect-function.vitest/padding-around-after-all-blocks.vitest/padding-around-test-blocks.vitest/prefer-called-exactly-once-with.vitest/prefer-called-once.vitest/prefer-called-times.vitest/prefer-called-with.vitest/prefer-comparison-matcher.vitest/prefer-describe-function-title.vitest/prefer-each.vitest/prefer-equality-matcher.vitest/prefer-expect-assertions.vitest/prefer-expect-resolves.vitest/prefer-expect-type-of.vitest/prefer-hooks-in-order.vitest/prefer-hooks-on-top.vitest/prefer-import-in-mock.vitest/prefer-importing-vitest-globals.vitest/prefer-lowercase-title.vitest/prefer-mock-promise-shorthand.vitest/prefer-mock-return-shorthand.vitest/prefer-snapshot-hint.vitest/prefer-spy-on.vitest/prefer-strict-boolean-matchers.vitest/prefer-strict-equal.vitest/prefer-to-be.vitest/prefer-to-be-falsy.vitest/prefer-to-be-object.vitest/prefer-to-be-truthy.vitest/prefer-to-contain.vitest/prefer-to-have-been-called-times.vitest/prefer-to-have-length.vitest/prefer-todo.vitest/require-awaited-expect-poll.vitest/require-hook.vitest/require-local-test-context-for-concurrent-snapshots.vitest/require-mock-type-parameters.vitest/require-test-timeout.vitest/require-to-throw-message.vitest/require-top-level-describe.vitest/valid-describe-callback.vitest/valid-expect.vitest/valid-expect-in-promise.vitest/valid-title.vitest/warn-todo`.split(`.`),h=/* @__PURE__ */ new Set([`vitest/max-expects`,`vitest/no-conditional-in-test`,`vitest/no-hooks`,`vitest/no-importing-vitest-globals`,`vitest/prefer-called-times`,`vitest/prefer-describe-function-title`,`vitest/prefer-expect-assertions`,`vitest/prefer-import-in-mock`,`vitest/prefer-importing-vitest-globals`,`vitest/prefer-to-be-falsy`,`vitest/prefer-to-be-truthy`,`vitest/require-test-timeout`]),g={"eslint/one-var":[`error`,`never`],"eslint/func-style":[`error`,`declaration`,{allowArrowFunctions:!0,overrides:{namedExports:`ignore`}}],"eslint/id-length":[`error`,{exceptions:[`a`,`i`,`t`,`x`,`y`,`z`]}],"eslint/init-declarations":[`error`,`never`,{ignoreForLoopInit:!0}],"eslint/max-lines":[`error`,{max:2e3,skipBlankLines:!0,skipComments:!0}],"eslint/max-lines-per-function":[`error`,{max:120,skipBlankLines:!0,skipComments:!0}],"eslint/complexity":`error`,"eslint/max-params":[`error`,{max:5}],"eslint/max-statements":[`error`,50],"eslint/no-console":[`error`,{allow:[`warn`,`error`,`info`]}],"eslint/no-param-reassign":`error`,"eslint/no-underscore-dangle":[`error`,{allow:[`__memoryInsightCardApplied`,`__memoryTdaiCardApplied`,`__sessionRescueApplied`,`__wukilDevToolsCardApplied`]}],"eslint/no-magic-numbers":`off`,"eslint/sort-keys":`off`,"eslint/sort-imports":`off`,"eslint/capitalized-comments":`off`,"eslint/no-ternary":`off`,"eslint/no-undefined":`off`,"eslint/no-void":`off`,"eslint/no-duplicate-imports":`off`,"eslint/require-await":`off`,"import/no-named-export":`off`,"import/group-exports":`off`,"import/exports-last":`off`,"import/prefer-default-export":`off`,"import/no-nodejs-modules":`off`,"import/no-relative-parent-imports":`off`,"import/max-dependencies":`off`,"import/no-default-export":`off`,"import/no-anonymous-default-export":`off`,"import/no-named-as-default-member":`off`,"import/consistent-type-specifier-style":[`error`,`prefer-top-level`],"import/no-duplicates":`error`,"import/no-cycle":`error`,"import/named":`error`,"import/first":`error`,"import/newline-after-import":`error`,"node/no-process-env":`off`,"node/no-sync":[`error`,{ignores:a}],"node/no-top-level-await":`error`,"oxc/no-async-await":`off`,"oxc/no-optional-chaining":`off`,"oxc/no-rest-spread-properties":`off`,"promise/always-return":`error`,"promise/no-promise-in-callback":[`error`,{exemptDeclarations:!0}],"promise/no-return-wrap":`error`,"promise/param-names":`error`,"promise/prefer-await-to-then":`error`,"typescript/consistent-type-imports":[`error`,{disallowTypeAnnotations:!1}],"typescript/explicit-function-return-type":[`error`,{allowDirectConstAssertionInArrowFunctions:!0,allowExpressions:!0,allowHigherOrderFunctions:!0,allowIIFEs:!0,allowTypedFunctionExpressions:!0,allowedNames:[`handler`,`callback`]}],"typescript/no-explicit-any":`error`,"typescript/no-floating-promises":`error`,"typescript/no-unsafe-assignment":`error`,"typescript/no-unsafe-type-assertion":`error`,"typescript/no-non-null-assertion":`error`,"typescript/require-await":`error`,"typescript/prefer-readonly-parameter-types":`off`,"typescript/promise-function-async":[`error`,{checkArrowFunctions:!1}],"typescript/consistent-return":[`error`,{treatUndefinedAsUnspecified:!0}],"typescript/only-throw-error":`error`,"unicorn/no-array-reduce":[`error`,{allowSimpleOperations:!0}],"unicorn/no-null":`off`,"unicorn/no-useless-undefined":[`error`,{checkArguments:!1}],"react/set-state-in-effect":`off`,"react/no-deriving-state-in-effects":`off`,"react/exhaustive-effect-dependencies":`off`,"react/hooks":`off`,"react/refs":`off`,"react/todo":`off`,...Object.fromEntries(m.map(e=>[e,`off`]))},_={files:[`**/*.test.ts`,`**/*.test.tsx`,`**/test/**`,`**/__tests__/**`],rules:{"typescript/no-non-null-assertion":`off`,"typescript/no-unsafe-type-assertion":`off`,"typescript/no-explicit-any":`off`,"typescript/require-await":`off`,"eslint/max-statements":`off`,"eslint/max-lines":`off`,"eslint/max-lines-per-function":`off`,"node/no-top-level-await":`off`}},v=[`expect`,`expectTypeOf`,`assert`,`assertType`],y=/^[A-Za-z_$][\w$]*$/u,b={files:[`**/*.test.ts`,`**/*.test.tsx`,`**/__tests__/**/*.spec.ts`],rules:{...Object.fromEntries(m.filter(e=>!h.has(e)).map(e=>[e,`error`])),"vitest/expect-expect":[`error`,{assertFunctionNames:v}],"react/rules-of-hooks":`off`}};function x(e={}){let t=e.assertionHelpers??[],n=e.titlePrefixes??[],r=[...t,...n].filter(e=>!y.test(e));if(r.length>0)throw Error(`assertionHelpers / titlePrefixes 只收裸标识符,不接受通配或带点形式(前者等于把 vitest/expect-expect 掏空):${r.join(`, `)}`);let i=[.../* @__PURE__ */ new Set([...v,...t])];return{...b,rules:{...b.rules,"vitest/expect-expect":[`error`,{assertFunctionNames:i}],...n.length===0?{}:{"vitest/prefer-lowercase-title":[`error`,{allowedPrefixes:[...new Set(n)]}]}}}}const S={files:[`scripts/**`,`*.mjs`,`*.mts`],rules:{"eslint/no-continue":`off`,"unicorn/max-nested-calls":`off`,"eslint/id-length":[`error`,{exceptions:[`a`,`i`,`t`,`x`,`y`,`z`,`n`,`p`,`s`,`v`,`e`,`f`,`ok`,`fs`,`id`]}],"unicorn/no-process-exit":`off`,"unicorn/no-useless-undefined":`off`,"typescript/no-non-null-assertion":`off`,"node/no-sync":`off`,"node/no-top-level-await":`off`,"typescript/no-unsafe-member-access":`off`,"typescript/no-unsafe-call":`off`,"typescript/no-unsafe-assignment":`off`,"typescript/no-unsafe-return":`off`,"typescript/no-unsafe-argument":`off`,"typescript/strict-boolean-expressions":`off`,"typescript/no-implied-eval":`off`,"typescript/require-array-sort-compare":`off`}};function C(e){let t={...r,...e};return e=>{let n=t[e];if(n!==void 0)return n;let r=e.indexOf(`/`);return r===-1?void 0:t[`${e.slice(0,r)}/*`]}}function w(e,t,n){if(t===void 0)return;let r=[];for(let[e,i]of Object.entries(t))if((Array.isArray(i)?i[0]:i)===`off`){let t=n(e);(t===void 0||t.note.trim().length===0)&&r.push(e)}if(r.length>0)throw Error(`${e} 里这些规则被关成 off 但没有依据(OFF_JUSTIFICATIONS 查不到,或 note 为空):${r.join(`, `)}。要么在代码里修,要么给它补一条 kind + measured + note。`)}function T(e={}){let{syncWrites:n=[],extraRules:r={},extraOverrides:i=[],extraIgnorePatterns:s=[],jsPlugins:c,offReasons:m={},assertionHelpers:h=[],titlePrefixes:v=[]}=e,y=C(m),b={...g,...u,...n.length>0?{"node/no-sync":[`error`,{ignores:[.../* @__PURE__ */ new Set([...a,...n])]}]}:{},...r},T=[_,x({assertionHelpers:h,titlePrefixes:v}),S,...i];w(`rules`,b,y);for(let[e,t]of T.entries())w(`overrides[${e}]`,t.rules,y);return t({plugins:o,env:{node:!0,es2024:!0,browser:!0},options:f,categories:d,ignorePatterns:[...p,...s],jsPlugins:l(c),rules:b,overrides:T})}export{g as BASE_RULES,r as OFF_JUSTIFICATIONS,a as READONLY_SYNC_ALLOWLIST,i as RETIRED_OFFS,S as SCRIPTS_OVERRIDES,b as TEST_FILE_OVERRIDES,_ as TEST_OVERRIDES,v as VITEST_ASSERTION_DEFAULTS,T as definePluginConfig,x as makeTestFileOverrides,l as resolveJsPlugins};