@nitra/cursor 5.3.4 → 6.0.0

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.
Files changed (173) hide show
  1. package/.claude-template/settings.template.json +2 -2
  2. package/.pi-template/extensions/n-cursor-adr/docs/index.md +13 -24
  3. package/CHANGELOG.md +21 -0
  4. package/bin/n-cursor.js +47 -24
  5. package/lib/docs/models.md +29 -18
  6. package/lib/docs/omlx-trace.md +51 -0
  7. package/lib/docs/omlx.md +31 -15
  8. package/lib/omlx.mjs +2 -5
  9. package/package.json +1 -1
  10. package/rules/abie/docs/fix.md +17 -11
  11. package/rules/adr/docs/fix.md +25 -140
  12. package/rules/bun/docs/fix.md +18 -151
  13. package/rules/capacitor/docs/fix.md +16 -13
  14. package/rules/capacitor/js/docs/platforms.md +31 -43
  15. package/rules/changelog/docs/fix.md +25 -169
  16. package/rules/ci4/docs/fix.md +11 -14
  17. package/rules/doc-files/doc-files.mdc +60 -0
  18. package/rules/doc-files/docs/fix.md +31 -0
  19. package/rules/doc-files/fix.mjs +19 -0
  20. package/{skills → rules}/doc-files/js/docgen-extract.mjs +42 -19
  21. package/{skills → rules}/doc-files/js/docgen-files-batch.mjs +18 -5
  22. package/{skills → rules}/doc-files/js/docgen-gen.mjs +46 -5
  23. package/{skills → rules}/doc-files/js/docgen-ignore.mjs +2 -1
  24. package/{skills → rules}/doc-files/js/docgen-scan.mjs +11 -3
  25. package/{skills → rules}/doc-files/js/docs/docgen-crc.md +1 -1
  26. package/{skills → rules}/doc-files/js/docs/docgen-extract-anchors.md +1 -1
  27. package/{skills → rules}/doc-files/js/docs/docgen-extract.md +2 -2
  28. package/{skills → rules}/doc-files/js/docs/docgen-files-batch.md +2 -2
  29. package/{skills → rules}/doc-files/js/docs/docgen-gen.md +2 -2
  30. package/{skills → rules}/doc-files/js/docs/docgen-ignore.md +4 -4
  31. package/rules/doc-files/js/docs/docgen-prompts.md +39 -0
  32. package/rules/doc-files/js/docs/docgen-scan.md +54 -0
  33. package/rules/doc-files/js/docs/lint.md +36 -0
  34. package/rules/doc-files/js/docs/units-js.md +31 -0
  35. package/rules/doc-files/js/docs/units-rs.md +35 -0
  36. package/rules/doc-files/js/docs/units.md +30 -0
  37. package/rules/doc-files/js/lint.mjs +96 -0
  38. package/{skills → rules}/doc-files/js/units-rs.mjs +37 -17
  39. package/rules/doc-files/lint/docs/lint.md +37 -0
  40. package/rules/doc-files/lint/lint.mjs +105 -0
  41. package/rules/doc-files/meta.json +1 -0
  42. package/rules/docker/docs/fix.md +21 -161
  43. package/rules/efes/docs/fix.md +23 -194
  44. package/rules/feedback/docs/fix.md +10 -8
  45. package/rules/ga/docs/fix.md +10 -5
  46. package/rules/ga/meta.json +1 -1
  47. package/rules/graphql/docs/fix.md +23 -119
  48. package/rules/hasura/docs/fix.md +19 -5
  49. package/rules/hasura/js/docs/internal_urls.md +34 -307
  50. package/rules/image-avif/docs/fix.md +16 -127
  51. package/rules/image-compress/docs/fix.md +20 -141
  52. package/rules/image-compress/js/docs/package_setup.md +22 -182
  53. package/rules/js-bun-db/docs/fix.md +23 -139
  54. package/rules/js-bun-db/js/docs/safety.md +33 -221
  55. package/rules/js-bun-redis/docs/fix.md +25 -114
  56. package/rules/js-bun-redis/js/docs/imports.md +18 -166
  57. package/rules/js-lint/docs/fix.md +30 -108
  58. package/rules/js-lint/js/docs/lint-findings.md +37 -17
  59. package/rules/js-lint/js/docs/lint.md +22 -238
  60. package/rules/js-lint/js/docs/tooling.md +34 -331
  61. package/rules/js-lint/js/lint.mjs +19 -12
  62. package/rules/js-lint/js-lint.mdc +1 -1
  63. package/rules/js-lint/meta.json +1 -1
  64. package/rules/js-lint-ci/docs/fix.md +16 -149
  65. package/rules/js-lint-ci/js/docs/lint.md +16 -136
  66. package/rules/js-lint-ci/js-lint-ci.mdc +1 -1
  67. package/rules/js-lint-ci/meta.json +1 -1
  68. package/rules/js-mssql/docs/fix.md +18 -123
  69. package/rules/js-mssql/js/docs/deps.md +28 -251
  70. package/rules/js-run/docs/fix.md +23 -138
  71. package/rules/js-run/js/docs/runtime.md +24 -378
  72. package/rules/k8s/docs/fix.md +18 -123
  73. package/rules/nginx-default-tpl/docs/fix.md +22 -118
  74. package/rules/nginx-default-tpl/js/docs/template.md +38 -360
  75. package/rules/npm-module/docs/fix.md +27 -89
  76. package/rules/npm-module/js/docs/header_doc_pointer.md +15 -15
  77. package/rules/npm-module/js/docs/package_structure.md +36 -258
  78. package/rules/npm-module/js/docs/rule_meta.md +25 -127
  79. package/rules/npm-module/js/docs/skill_meta.md +18 -180
  80. package/rules/npm-module/js/rule_meta.mjs +3 -3
  81. package/rules/php/docs/fix.md +21 -98
  82. package/rules/php/js/docs/tooling.md +20 -143
  83. package/rules/python/docs/fix.md +25 -157
  84. package/rules/python/js/docs/applies.md +20 -98
  85. package/rules/python/js/docs/tooling.md +27 -144
  86. package/rules/rego/docs/fix.md +24 -112
  87. package/rules/rego/js/docs/applies.md +20 -164
  88. package/rules/rego/js/docs/lint.md +15 -110
  89. package/rules/rego/meta.json +1 -1
  90. package/rules/release/docs/fix.md +16 -114
  91. package/rules/rust/docs/fix.md +24 -119
  92. package/rules/rust/js/docs/applies.md +20 -129
  93. package/rules/security/docs/fix.md +21 -78
  94. package/rules/security/js/docs/sample_secret.md +23 -182
  95. package/rules/security/js/docs/trufflehog.md +19 -128
  96. package/rules/security/meta.json +1 -1
  97. package/rules/style-lint/docs/fix.md +16 -150
  98. package/rules/style-lint/js/docs/lint.md +21 -172
  99. package/rules/style-lint/js/docs/tooling.md +19 -184
  100. package/rules/style-lint/js/lint.mjs +4 -3
  101. package/rules/style-lint/meta.json +1 -1
  102. package/rules/tauri/docs/fix.md +26 -152
  103. package/rules/tauri/js/docs/cargo_mutants_config.md +21 -159
  104. package/rules/tauri/js/docs/tooling.md +20 -217
  105. package/rules/test/docs/fix.md +19 -127
  106. package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +15 -127
  107. package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +17 -153
  108. package/rules/test/js/docs/cargo_mutants_config.md +24 -164
  109. package/rules/test/js/docs/location.md +24 -126
  110. package/rules/test/js/docs/no-process-chdir.md +20 -151
  111. package/rules/test/js/docs/no-relative-fs-path.md +24 -261
  112. package/rules/test/js/docs/stryker_config.md +48 -148
  113. package/rules/test/js/docs/vitest-config-pool-forks.md +21 -164
  114. package/rules/text/docs/fix.md +25 -113
  115. package/rules/text/js/docs/forbidden-prettier.md +21 -132
  116. package/rules/text/js/docs/formatting.md +60 -251
  117. package/rules/text/js/docs/lint.md +17 -114
  118. package/rules/text/js/lint.mjs +5 -3
  119. package/rules/text/lint/docs/lint.md +1 -1
  120. package/rules/text/lint/docs/run-dotenv-linter.md +1 -1
  121. package/rules/text/lint/docs/run-shellcheck.md +1 -1
  122. package/rules/text/lint/lint.mjs +13 -9
  123. package/rules/text/lint/run-dotenv-linter.mjs +13 -10
  124. package/rules/text/lint/run-shellcheck.mjs +10 -6
  125. package/rules/text/meta.json +1 -1
  126. package/rules/vue/docs/fix.md +25 -118
  127. package/rules/vue/js/docs/packages.md +25 -323
  128. package/rules/worktree/docs/fix.md +31 -150
  129. package/scripts/coverage-classify/docs/index.md +23 -209
  130. package/scripts/coverage-classify/docs/verdict-schema.md +14 -159
  131. package/scripts/dispatcher/docs/trace.md +35 -0
  132. package/scripts/docs/auto-rules.md +37 -361
  133. package/scripts/docs/lint-cli.md +12 -13
  134. package/scripts/docs/post-tool-use-fix.md +16 -15
  135. package/scripts/docs/skills-cli.md +26 -23
  136. package/scripts/docs/sync-claude-config.md +94 -34
  137. package/scripts/docs/worktree-cli.md +11 -34
  138. package/scripts/lib/docs/assert-project-root.md +14 -16
  139. package/scripts/lib/docs/changed-files.md +24 -139
  140. package/scripts/lib/docs/discover-check-rules-from-cursor.md +14 -146
  141. package/scripts/lib/docs/rule-meta.md +1 -1
  142. package/scripts/lib/docs/rule-predicates.md +20 -17
  143. package/scripts/lib/docs/run-rule-cli.md +14 -18
  144. package/scripts/lib/docs/run-rule.md +13 -20
  145. package/scripts/lib/docs/run-standard-rule.md +12 -15
  146. package/scripts/lib/docs/sync-gitignore-worktree.md +15 -18
  147. package/scripts/lib/rule-meta.mjs +10 -6
  148. package/scripts/lib/rule-predicates.mjs +1 -1
  149. package/scripts/lint-cli.mjs +28 -20
  150. package/scripts/sync-claude-config.mjs +4 -1
  151. package/scripts/utils/docs/with-lock.md +19 -12
  152. package/scripts/utils/with-lock.mjs +4 -2
  153. package/skills/doc-aggregate/SKILL.md +2 -2
  154. package/skills/doc-aggregate/js/docgen-ignore.mjs +6 -6
  155. package/skills/doc-aggregate/js/docs/docgen-ignore.md +1 -1
  156. package/skills/doc-aggregate/js/docs/docgen-scan.md +78 -0
  157. package/skills/doc-files/.changes/260612-0031.md +5 -0
  158. package/skills/doc-files/.changes/260612-0036.md +5 -0
  159. package/skills/doc-files/.changes/260612-0114.md +5 -0
  160. package/skills/doc-files/SKILL.md +6 -6
  161. package/skills/fix/js/docs/llm-worker.md +17 -15
  162. package/skills/fix/js/docs/orchestrator.md +30 -23
  163. package/skills/fix/js/docs/t0.md +26 -16
  164. package/skills/start-check/js/docs/check.md +26 -22
  165. package/skills/taze/js/docs/diff.md +44 -20
  166. package/skills/doc-files/js/docs/docgen-prompts.md +0 -32
  167. package/skills/doc-files/js/docs/docgen-scan.md +0 -25
  168. package/skills/doc-files/js/docs/units-rs.md +0 -35
  169. /package/{skills → rules}/doc-files/js/docgen-crc.mjs +0 -0
  170. /package/{skills → rules}/doc-files/js/docgen-extract-anchors.mjs +0 -0
  171. /package/{skills → rules}/doc-files/js/docgen-prompts.mjs +0 -0
  172. /package/{skills → rules}/doc-files/js/units-js.mjs +0 -0
  173. /package/{skills → rules}/doc-files/js/units.mjs +0 -0
@@ -1,348 +1,51 @@
1
- # tooling.mjs
2
-
3
- ## Огляд
4
-
5
- Модуль `tooling.mjs` реалізує перевірку (check-частину) правила **`js-lint.mdc`** для проєктів-споживачів пакета `@nitra/cursor`. Він гарантує, що репозиторій налаштований на канонічний JS-toolchain Nitra:
6
-
7
- - наявність та коректний вміст **flat ESLint config** (`eslint.config.js` або `eslint.config.mjs`) з виклику `getConfig` із `@nitra/eslint-config` та `ignores` для `**/auto-imports.d.ts`;
8
- - **`.oxlintrc.json`** має збігатися з канонічним JSON у пакеті (`npm/rules/js-lint/js/data/tooling/oxlint-canonical.json`) — `plugins`, `jsPlugins`, `categories`, усі правила з канону, `settings`, `env`, `globals`, `ignorePatterns`; додаткові записи дозволені лише у блоці `rules`;
9
- - кожен workspace-`package.json` має `"type": "module"`, `engines.node >= 24` та `engines.bun >= 1.3`;
10
- - існує workflow `.github/workflows/lint-js.yml` (структуру валідує Rego-policy `js_lint.lint_js_yml`), а `.github/workflows/lint.yml` (якщо є) **не дублює** кроки `bunx oxlint` / `bunx eslint` / `jscpd`;
11
- - існує `knip.json` (якщо відсутній — копіюється канонічний baseline з пакета);
12
- - у репозиторії **немає** застарілих legacy-конфігів ESLint (`.eslintrc`, `.eslintrc.js`, `.eslintrc.json`, `.eslintrc.yml`).
13
-
14
- Per-document вимоги (`lint-js` script, мінімальна версія `@nitra/eslint-config ≥ 3.10.0`, root `engines`, `.jscpd.json`, `.vscode/extensions.json`, структура `lint-js.yml`) вже **не** реалізуються тут — їх валідують Rego-policy-пакети `js_lint.*` (`npm/policy/js_lint/package_json/`, `npm/policy/js_lint/lint_js_yml/`). Це усуває дублювання істини між JS-перевіркою та Rego-політиками.
15
-
16
- Модуль використовує спільний `createCheckReporter` зі скриптів пакета: збирає `pass` / `fail`-повідомлення та повертає числовий exit-код (0 — OK, 1 — є проблеми).
17
-
18
- ## Експорти / API
19
-
20
- | Експорт | Тип | Призначення |
21
- | ------------------------------------------------ | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
22
- | `OXLINT_CANONICAL_JSON_PATH` | `string` (named const) | Абсолютний шлях до канонічного `oxlint-canonical.json` у цьому пакеті (`<dirname>/data/tooling/oxlint-canonical.json`). Використовується перевіркою та тестами. |
23
- | `KNIP_CANONICAL_JSON_PATH` | `string` (named const) | Абсолютний шлях до канонічного `knip-canonical.json` у цьому пакеті. Копіюється у корінь проєкту-споживача, якщо `knip.json` відсутній. |
24
- | `verifyOxlintRcAgainstCanonical(cfg, canonical)` | `function` (named export) | Чиста функція звірення розпарсеного `.oxlintrc.json` із канонічним JSON. Повертає `{ ok: boolean, failures: string[] }`. |
25
- | `check(cwd?)` | `async function` (named export) | Точка входу: запускає всі under-the-hood перевірки і повертає `Promise<number>` (0 — все добре, 1 — є fail). |
26
-
27
- Усі решта функцій (`deepEqualOxlintCanonical`, `asRecordOrEmpty`, `compareOxlintRules`, `compareOxlintIgnorePatterns`, `checkEslintConfig`, `checkPackageJsonTypeModule`, `checkWorkspacePackages`, `checkEnginesNode`, `checkEnginesBun`, `checkPackageJsonJsLint`, `checkOxlintRc`, `checkLintJsWorkflows`, `checkKnipConfig`) — **module-private** (не експортуються).
28
-
29
- ## Функції
30
-
31
- ### `deepEqualOxlintCanonical(actual, expected)`
32
-
33
- **Сигнатура:** `function deepEqualOxlintCanonical(actual: unknown, expected: unknown): boolean`
34
-
35
- **Параметри:**
36
-
37
- - `actual` — значення з `.oxlintrc.json` (будь-який тип);
38
- - `expected` — еталонне значення з канону.
39
-
40
- **Повертає:** `true`, якщо `actual` повністю збігається з `expected` за правилами канону:
41
-
42
- - примітиви — `===`;
43
- - масиви — однакові за `JSON.stringify` (тобто **порядок** теж важливий);
44
- - об’єкти — **той самий набір ключів** (`expKeys.length === actKeys.length` і всі ключі канону присутні в actual) та рекурсивно рівні значення.
45
-
46
- **Side effects:** немає (чиста функція).
47
-
48
- ---
49
-
50
- ### `asRecordOrEmpty(v)`
51
-
52
- **Сигнатура:** `function asRecordOrEmpty(v: unknown): Record<string, unknown>`
53
-
54
- **Параметри:**
55
-
56
- - `v` — будь-яке значення.
57
-
58
- **Повертає:** саме значення (із cast-коментарем), якщо це plain-object (`typeof v === 'object'`, не `null`, не масив); інакше — `{}`.
59
-
60
- **Side effects:** немає.
61
-
62
- ---
63
-
64
- ### `compareOxlintRules(expected, actual, failures)`
65
-
66
- **Сигнатура:** `function compareOxlintRules(expected: unknown, actual: unknown, failures: string[]): void`
67
-
68
- **Параметри:**
69
-
70
- - `expected` — канонічний об’єкт `rules`;
71
- - `actual` — значення `rules` з поточного `.oxlintrc.json`;
72
- - `failures` — буфер-масив, у який функція **дописує** повідомлення про невідповідності.
73
-
74
- **Поведінка:** ітерує по ключах канону; якщо `actual[ruleKey] !== expected[ruleKey]` (строге `!==` — отже об’єктні значення правил мають бути тим самим референсом, що рідко зустрічається — на практиці значення правил у oxlint це числа / рядки / прості масиви), додає повідомлення у форматі:
75
-
76
- ```
77
- .oxlintrc.json: rules["<key>"] очікується <expected>, зараз <actual>
78
- ```
79
-
80
- Додаткові ключі правил у `actual` **дозволені** (не повідомляються).
81
-
82
- **Повертає:** `undefined`.
83
-
84
- **Side effects:** мутує `failures`.
85
-
86
- ---
87
-
88
- ### `compareOxlintIgnorePatterns(expected, actual, failures)`
89
-
90
- **Сигнатура:** `function compareOxlintIgnorePatterns(expected: unknown, actual: unknown, failures: string[]): void`
91
-
92
- **Параметри:**
93
-
94
- - `expected` — канонічний масив `ignorePatterns`;
95
- - `actual` — поточний масив `ignorePatterns`;
96
- - `failures` — буфер для повідомлень.
97
-
98
- **Поведінка:**
99
-
100
- - якщо `expected` не є масивом — функція мовчки виходить (канон не задає очікувань);
101
- - якщо `actual` не є масивом — додає повідомлення про обов’язковість масиву;
102
- - порівнює як **підмножину**: усі канонічні патерни мають бути в `actual` (через `Set`); відсутні патерни перелічуються одним повідомленням. Додаткові локальні патерни **дозволені**.
103
-
104
- **Повертає:** `undefined`.
105
-
106
- **Side effects:** мутує `failures`.
107
-
108
- ---
109
-
110
- ### `verifyOxlintRcAgainstCanonical(cfg, canonical)` _(експортовано)_
111
-
112
- **Сигнатура:** `function verifyOxlintRcAgainstCanonical(cfg: unknown, canonical: unknown): { ok: boolean, failures: string[] }`
113
-
114
- **Параметри:**
115
-
116
- - `cfg` — розпарсений корінь `.oxlintrc.json`;
117
- - `canonical` — розпарсений `oxlint-canonical.json`.
118
-
119
- **Поведінка:**
120
-
121
- 1. Якщо `cfg` чи `canonical` не є plain-object — повертає `ok: false` з відповідним повідомленням (для `cfg` — про невалідний корінь; для `canonical` — як «внутрішня помилка»).
122
- 2. Для кожного ключа канону:
123
- - `rules` → делегує `compareOxlintRules`;
124
- - `ignorePatterns` → делегує `compareOxlintIgnorePatterns`;
125
- - решта ключів → перевіряється через `deepEqualOxlintCanonical`; при невідповідності в `failures` додається повідомлення, що поле має збігатися з каноном пакета `@nitra/cursor`.
126
- 3. Повертає `{ ok: failures.length === 0, failures }`.
127
-
128
- **Повертає:** результат-об’єкт із прапором `ok` і масивом fail-повідомлень для подальшого репортинга.
129
-
130
- **Side effects:** немає (чиста функція; працює з аргументами, не читає файли).
131
-
132
- ---
133
-
134
- ### `checkEslintConfig(passFn, failFn, cwd)`
135
-
136
- **Сигнатура:** `async function checkEslintConfig(passFn: (msg:string)=>void, failFn: (msg:string)=>void, cwd: string): Promise<void>`
137
-
138
- **Параметри:**
139
-
140
- - `passFn` — колбек успіху;
141
- - `failFn` — колбек помилки;
142
- - `cwd` — корінь репозиторію.
143
-
144
- **Поведінка:**
145
-
146
- - шукає `eslint.config.js`, потім `eslint.config.mjs`; якщо жодного — fail і ранній вихід;
147
- - читає вміст знайденого файла як `utf8` і застосовує три суто **текстові** перевірки наявності підрядків:
148
- 1. `getConfig` — обов’язковий виклик;
149
- 2. `@nitra/eslint-config` — обов’язковий імпорт;
150
- 3. `**/auto-imports.d.ts` — обов’язковий запис у `ignores`.
151
-
152
- **Повертає:** `Promise<void>`.
153
-
154
- **Side effects:** читає файли з ФС (`existsSync`, `readFile`); викликає `passFn`/`failFn`.
155
-
156
1
  ---
157
-
158
- ### `checkPackageJsonTypeModule(label, pkg, passFn, failFn)`
159
-
160
- **Сигнатура:** `function checkPackageJsonTypeModule(label: string, pkg: { type?: string }, passFn: (msg:string)=>void, failFn: (msg:string)=>void): void`
161
-
162
- **Поведінка:** якщо `pkg.type === 'module'` → `passFn`; інакше → `failFn` з вимогою додати поле.
163
-
164
- **Side effects:** виклики переданих колбеків.
165
-
166
- ---
167
-
168
- ### `checkWorkspacePackages(workspaces, passFn, failFn, cwd)`
169
-
170
- **Сигнатура:** `async function checkWorkspacePackages(workspaces: unknown[], passFn, failFn, cwd: string): Promise<void>`
171
-
172
- **Поведінка:** для кожного запису workspaces:
173
-
174
- - будує шлях `<ws>/package.json`;
175
- - якщо файл існує — читає та парсить JSON; запускає послідовно `checkPackageJsonTypeModule`, `checkEnginesNode`, `checkEnginesBun`.
176
-
177
- **Side effects:** читання ФС, виклики колбеків.
178
-
179
- ---
180
-
181
- ### `checkEnginesNode(label, pkg, passFn, failFn)`
182
-
183
- **Сигнатура:** `function checkEnginesNode(label: string, pkg: { engines?: { node?: string } }, passFn, failFn): void`
184
-
185
- **Поведінка:** дістає `pkg.engines?.node`; через регекс `NON_DIGITS_RE = /\D+/u` бере **перший** числовий токен (наприклад, з `">=24"` → `"24"`); якщо це число ≥ 24 → pass, інакше → fail; якщо поле відсутнє → fail з пропозицією додати `"engines": { "node": ">=24" }`.
186
-
187
- **Side effects:** виклики колбеків.
188
-
189
- ---
190
-
191
- ### `checkEnginesBun(label, pkg, passFn, failFn)`
192
-
193
- **Сигнатура:** `function checkEnginesBun(label: string, pkg: { engines?: { bun?: string } }, passFn, failFn): void`
194
-
195
- **Поведінка:** дістає `pkg.engines?.bun`; розбиває рядок по `NON_DIGITS_RE`, фільтрує порожні токени, бере перші два як `[major, minor]`; pass якщо `major > 1` або `major === 1 && minor >= 3`; інакше fail; відсутнє поле → fail з пропозицією додати `"engines": { "bun": ">=1.3" }`.
196
-
197
- **Side effects:** виклики колбеків.
198
-
199
- ---
200
-
201
- ### `checkPackageJsonJsLint(passFn, failFn, cwd)`
202
-
203
- **Сигнатура:** `async function checkPackageJsonJsLint(passFn, failFn, cwd: string): Promise<void>`
204
-
205
- **Поведінка:** читає кореневий `package.json` (якщо відсутній — мовчки виходить); бере поле `workspaces` (масив або `[]`) і делегує `checkWorkspacePackages`. Кореневий `package.json` тут **не** валідується — це робить Rego-policy `npm/policy/js_lint/package_json/`.
206
-
207
- **Side effects:** читання ФС, виклики колбеків.
208
-
209
- ---
210
-
211
- ### `checkOxlintRc(passFn, failFn, cwd)`
212
-
213
- **Сигнатура:** `async function checkOxlintRc(passFn, failFn, cwd: string): Promise<void>`
214
-
215
- **Поведінка:**
216
-
217
- 1. Якщо `.oxlintrc.json` відсутній → fail і ранній вихід.
218
- 2. Спроба JSON-парсингу; при помилці → fail про невалідний JSON.
219
- 3. Pass-повідомлення про наявність файла.
220
- 4. Читає канонічний `oxlint-canonical.json` (шлях `OXLINT_CANONICAL_JSON_PATH`); при помилці → fail-повідомлення «внутрішня помилка».
221
- 5. Викликає `verifyOxlintRcAgainstCanonical(oxCfg, canonical)`; pass якщо `ok`, інакше — повторно викликає `failFn` для кожного повідомлення зі списку `failures`.
222
-
223
- **Side effects:** читання ФС, виклики колбеків.
224
-
225
- ---
226
-
227
- ### `checkLintJsWorkflows(passFn, failFn, cwd)`
228
-
229
- **Сигнатура:** `async function checkLintJsWorkflows(passFn, failFn, cwd: string): Promise<void>`
230
-
231
- **Поведінка:**
232
-
233
- - перевіряє існування `.github/workflows/lint-js.yml` → pass з нагадуванням, що структуру валідує Rego (`js_lint.lint_js_yml`); відсутність → fail;
234
- - якщо існує `.github/workflows/lint.yml` — читає його як текст і перевіряє, що він **не містить одночасно** трьох підрядків `bunx oxlint`, `bunx eslint`, `jscpd`. Якщо містить — fail (дубль кроків JS-лінта), інакше — pass.
235
-
236
- **Side effects:** читання ФС, виклики колбеків.
237
-
238
- ---
239
-
240
- ### `checkKnipConfig(passFn, failFn, cwd)`
241
-
242
- **Сигнатура:** `async function checkKnipConfig(passFn, failFn, cwd: string): Promise<void>`
243
-
244
- **Поведінка:**
245
-
246
- - якщо `knip.json` існує у корені — pass і вихід;
247
- - якщо відсутній і канонічний шаблон `KNIP_CANONICAL_JSON_PATH` теж недоступний (`existsSync` повертає false) — fail з пропозицією перевстановити `@nitra/cursor`;
248
- - інакше — **копіює** канонічний JSON у `cwd/knip.json` через `copyFile` і повідомляє pass.
249
-
250
- **Side effects:** читання ФС, **запис** нового файла `knip.json`, виклики колбеків. Це єдина функція модуля з write-side-effect.
251
-
2
+ docgen:
3
+ source: npm/rules/js-lint/js/tooling.mjs
4
+ crc: e847996e
5
+ score: 80
252
6
  ---
253
7
 
254
- ### `check(cwd?)` _(експортовано — публічна точка входу)_
255
-
256
- **Сигнатура:** `async function check(cwd?: string): Promise<number>` (за замовчуванням `cwd = process.cwd()`).
257
-
258
- **Поведінка:**
259
-
260
- 1. Створює репортер через `createCheckReporter()` і отримує `pass` / `fail` колбеки.
261
- 2. Послідовно `await`-ить:
262
- - `checkEslintConfig` — flat-config;
263
- - `checkPackageJsonJsLint` — workspace-`package.json`;
264
- - `checkOxlintRc` — `.oxlintrc.json` vs canonical;
265
- - `checkLintJsWorkflows` — `lint-js.yml` + дубль у `lint.yml`;
266
- - `checkKnipConfig` — `knip.json` (з можливим створенням).
267
- 3. Перевіряє наявність legacy-файлів у списку `['.eslintrc', '.eslintrc.js', '.eslintrc.json', '.eslintrc.yml']`; для кожного знайденого — `fail`.
268
- 4. Повертає `reporter.getExitCode()` — `0` якщо не було жодного `fail`, інакше `1`.
269
-
270
- **Повертає:** `Promise<number>` — exit-код для CLI.
271
-
272
- **Side effects:** усі ефекти із дочірніх функцій (читання ФС, можливий запис `knip.json`, виведення pass/fail через репортер).
273
-
274
- ## Залежності
275
-
276
- ### Стандартна бібліотека Node.js
277
-
278
- - `node:fs` → `existsSync` — синхронна перевірка наявності файлів.
279
- - `node:fs/promises` → `copyFile`, `readFile` — асинхронні файлові операції.
280
- - `node:path` → `dirname`, `join` — побудова шляхів.
281
- - `node:url` → `fileURLToPath` — перетворення `import.meta.url` для отримання абсолютної директорії модуля.
282
-
283
- ### Внутрішні
284
-
285
- - `../../../scripts/lib/check-reporter.mjs` → `createCheckReporter` — фабрика репортера зі стандартними `pass` / `fail` колбеками та методом `getExitCode()`.
286
-
287
- ### Канонічні артефакти даних (читаються/копіюються, але не імпортуються як JS-модулі)
288
-
289
- - `data/tooling/oxlint-canonical.json` — еталон `.oxlintrc.json`.
290
- - `data/tooling/knip-canonical.json` — baseline для `knip.json` (копіюється у проєкт-споживач).
291
-
292
- ### Зовнішні
293
-
294
- Жодних рантайм-залежностей з `node_modules` модуль не використовує. (Перевірки на наявність `@nitra/eslint-config` виконуються **текстово** як пошук підрядка у файлі ESLint-конфіга.)
295
-
296
- ## Потік виконання / Використання
8
+ # tooling.mjs
297
9
 
298
- ### Типовий запуск
10
+ ## Огляд
299
11
 
300
- Модуль використовується інфраструктурою `@nitra/cursor` як check-частина правила `js-lint`. Виклик:
12
+ OXLINT_CANONICAL_JSON_PATH
13
+ Повертає шлях до канонічного JSON-файлу oxlint у пакету
301
14
 
302
- ```js
303
- import { check } from '@nitra/cursor/rules/js-lint/js/tooling.mjs'
15
+ KNIP_CANONICAL_JSON_PATH
16
+ Повертає шлях до канонічного JSON-файлу knip у пакету
304
17
 
305
- const exitCode = await check(process.cwd())
306
- process.exit(exitCode)
307
- ```
18
+ verifyOxlintRcAgainstCanonical
19
+ Звіряє блок rules з `.oxlintrc.json` проти канону пакета @nitra/cursor
308
20
 
309
- або через загальний раннер пакета, який ітерує всі `check-*.mjs` правил.
21
+ check
22
+ Перевіряє конфігурацію проєкту відповідно до правил js-lint.mdc
310
23
 
311
- ### Послідовність кроків у середині `check`
24
+ ## Поведінка
312
25
 
313
- 1. **ESLint config** → шукаємо `eslint.config.js`/`.mjs`, перевіряємо вміст на `getConfig`, `@nitra/eslint-config`, `**/auto-imports.d.ts`.
314
- 2. **package.json workspaces** ітеруємо `workspaces[]`, перевіряємо `type: "module"` + `engines.node` + `engines.bun` у кожному.
315
- 3. **.oxlintrc.json** → читаємо JSON, парсимо канон із пакета, повне дерев’яне порівняння через `verifyOxlintRcAgainstCanonical` (особливі правила для `rules` і `ignorePatterns`).
316
- 4. **GitHub workflows** → перевіряємо існування `lint-js.yml`; перевіряємо, що `lint.yml` не дублює `bunx oxlint` + `bunx eslint` + `jscpd`.
317
- 5. **knip.json** → якщо відсутній, копіюємо канонічний baseline; інакше — лише фіксуємо наявність.
318
- 6. **Legacy ESLint configs** → fail для будь-якого з `.eslintrc`, `.eslintrc.js`, `.eslintrc.json`, `.eslintrc.yml` у корені.
319
- 7. Повертаємо `0` або `1` через репортер.
26
+ OXLINT_CANONICAL_JSON_PATH
27
+ Шлях до канонічного oxlint JSON у цьому пакеті
320
28
 
321
- ### Розподіл відповідальності з Rego-policies
29
+ KNIP_CANONICAL_JSON_PATH
30
+ Шлях до канонічного knip JSON у цьому пакеті
322
31
 
323
- | Що перевіряється | Де живе |
324
- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
325
- | Наявність `eslint.config.{js,mjs}` + ключові підрядки | **тут** (`checkEslintConfig`) |
326
- | `type: "module"`, `engines.{node,bun}` у workspace-пакетах | **тут** (`checkWorkspacePackages`) |
327
- | Той самий набір полів у кореневому `package.json`, мінімальна версія `@nitra/eslint-config`, `lint-js`-script, `prettier`, `@nitra/prettier-config` | Rego: `npm/policy/js_lint/package_json/`, `npm/policy/text/package_json/` |
328
- | Збіг `.oxlintrc.json` з канонічним JSON | **тут** (`checkOxlintRc` + `verifyOxlintRcAgainstCanonical`) |
329
- | Наявність `lint-js.yml`, заборона дубля у `lint.yml` | **тут** (`checkLintJsWorkflows`) |
330
- | Структура `lint-js.yml` (`actions/checkout@v6`, `persist-credentials: false`, `setup-bun-deps`, заборона `--fix`) | Rego: `npm/policy/js_lint/lint_js_yml/` |
331
- | Bootstrap `knip.json` (копіювання канону) | **тут** (`checkKnipConfig`) |
332
- | Заборона legacy `.eslintrc*` | **тут** (`check`, фінальний цикл) |
32
+ verifyOxlintRcAgainstCanonical
33
+ Звіряє блок rules з `.oxlintrc.json` проти канону пакета @nitra/cursor
333
34
 
334
- ### Семантика звіту
35
+ check
36
+ Перевіряє конфігурацію проєкту відповідно до правил js-lint.mdc
335
37
 
336
- - Кожна перевірка дописує **окремі** повідомлення у репортер; `check` не зупиняється на першому `fail`, а збирає повний набір проблем — це навмисно, щоб CI-користувач бачив усі порушення одним прогоном.
337
- - Винятки JSON-парсингу `.oxlintrc.json` ловляться локально та трансформуються у fail-повідомлення; невдала зчитка канону також не кидає виняток назовні (стає «внутрішня помилка»). Інші помилки (наприклад, `JSON.parse` для workspace-`package.json`) **не** ловляться і прокидаються через `Promise<number>` як rejection — це сигналізує про серйозну поломку (битий JSON у відомому файлі).
38
+ ## Публічний API
338
39
 
339
- ### Rebuild Test
40
+ OXLINT_CANONICAL_JSON_PATH Шлях до канонічного oxlint JSON у пакету (для перевірки та тестів)
41
+ KNIP_CANONICAL_JSON_PATH — Шлях до канонічного knip JSON у пакету (копіюється у корінь проєкту-споживача, якщо відсутній)
42
+ verifyOxlintRcAgainstCanonical — Порівнює `.oxlintrc.json` з каноном пакета `@nitra/cursor` (включає всі правила з канону та інші поля з `oxlint-canonical.json`). Дозволено додати лише ключі в `rules`; інші поля мають збігатися з каноном.
43
+ check — Перевіряє відповідність проєкту правилам js-lint.mdc
340
44
 
341
- Документ описує одиничний файл `tooling.mjs`. Перебудова за документом має містити:
45
+ ## Гарантії поведінки
342
46
 
343
- - два названих експорти-шляхи (`OXLINT_CANONICAL_JSON_PATH`, `KNIP_CANONICAL_JSON_PATH`), обчислені через `dirname(fileURLToPath(import.meta.url))` + `data/tooling/<name>.json`;
344
- - регекс `NON_DIGITS_RE = /\D+/u` як модульну константу;
345
- - експорт `verifyOxlintRcAgainstCanonical(cfg, canonical)` із семантикою «`rules` — підмножина значень, `ignorePatterns` підмножина елементів, решта полів — повний `deepEqualOxlintCanonical`»;
346
- - допоміжні приватні функції `deepEqualOxlintCanonical`, `asRecordOrEmpty`, `compareOxlintRules`, `compareOxlintIgnorePatterns` з описаною семантикою (для масивів — порівняння через `JSON.stringify`; для об’єктів — однаковий розмір ключів і рекурсія; primitives — `===`);
347
- - приватні чекери (`checkEslintConfig`, `checkPackageJsonTypeModule`, `checkWorkspacePackages`, `checkEnginesNode`, `checkEnginesBun`, `checkPackageJsonJsLint`, `checkOxlintRc`, `checkLintJsWorkflows`, `checkKnipConfig`) з зазначеними сигнатурами та повідомленнями;
348
- - експорт `check(cwd = process.cwd())`, який створює репортер через `createCheckReporter`, послідовно `await`-ить п’ять чекерів у порядку _ESLint → package.json → oxlint → workflows → knip_, потім перевіряє legacy-конфіги ESLint і повертає `reporter.getExitCode()`.
47
+ - Read-only: файл не виконує операцій запису у файлову систему.
48
+ - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
49
+ - За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
50
+ - Свідомо пропускає шляхи: `.github`, `.git`.
51
+ - Не звертається до мережі.
@@ -54,14 +54,15 @@ function runJson(args, cwd) {
54
54
  }
55
55
 
56
56
  /**
57
- * Full-режим (ci): лінт усього проєкту зі стрімінгом і fail-fast (без класифікації).
57
+ * Full-режим (--full): лінт усього проєкту зі стрімінгом і fail-fast (без класифікації).
58
58
  * @param {string} cwd корінь
59
+ * @param {boolean} readOnly true → без `--fix` (детект, нуль мутацій — CI)
59
60
  * @returns {number} exit code
60
61
  */
61
- function lintFullProject(cwd) {
62
- const ox = runInherit(['oxlint', '--fix'], cwd)
62
+ function lintFullProject(cwd, readOnly) {
63
+ const ox = runInherit(readOnly ? ['oxlint'] : ['oxlint', '--fix'], cwd)
63
64
  if (ox !== 0) return ox
64
- return runInherit(['eslint', '--fix', '.'], cwd)
65
+ return runInherit(readOnly ? ['eslint', '.'] : ['eslint', '--fix', '.'], cwd)
65
66
  }
66
67
 
67
68
  /**
@@ -69,12 +70,16 @@ function lintFullProject(cwd) {
69
70
  * на introduced / pre-existing (беклог #6/A). Блокування на будь-якому finding.
70
71
  * @param {string[]} js js-подібні змінені файли
71
72
  * @param {string} cwd корінь
73
+ * @param {boolean} readOnly true → пропустити фікс-пас (детект, нуль мутацій)
72
74
  * @returns {number} exit code (0 — чисто; 1 — лишились findings)
73
75
  */
74
- function lintChangedClassified(js, cwd) {
76
+ function lintChangedClassified(js, cwd, readOnly) {
75
77
  // Фікс-пас обох інструментів (послідовно; обидва — щоб репорт показав повну картину).
76
- runFix(['oxlint', '--fix', ...js], cwd)
77
- runFix(['eslint', '--fix', ...js], cwd)
78
+ // У read-only пропускаємо — лише детект без мутацій (CI / pre-commit).
79
+ if (!readOnly) {
80
+ runFix(['oxlint', '--fix', ...js], cwd)
81
+ runFix(['eslint', '--fix', ...js], cwd)
82
+ }
78
83
 
79
84
  // Репорт-пас по ФІНАЛЬНОМУ (пост-фікс) файлу — рядки findings і diff узгоджені.
80
85
  const oxRes = runJson(['oxlint', '--format=json', ...js], cwd)
@@ -99,16 +104,18 @@ function lintChangedClassified(js, cwd) {
99
104
  }
100
105
 
101
106
  /**
102
- * Запускає oxlint+eslint з автофіксом.
103
- * @param {string[] | undefined} files quick: лише ці файли; undefined: весь проєкт
107
+ * Запускає oxlint+eslint. За замовчуванням — з автофіксом; `opts.readOnly` — лише детект.
108
+ * @param {string[] | undefined} files per-file: лише ці файли; undefined: весь проєкт (--full)
104
109
  * @param {string} [cwd] корінь репо
110
+ * @param {{ readOnly?: boolean }} [opts] readOnly → без `--fix` (нуль мутацій)
105
111
  * @returns {Promise<number>} 0 — OK, ≠0 — порушення
106
112
  */
107
- export function lint(files, cwd = process.cwd()) {
113
+ export function lint(files, cwd = process.cwd(), opts = {}) {
114
+ const readOnly = opts.readOnly === true
108
115
  if (files === undefined) {
109
- return Promise.resolve(lintFullProject(cwd))
116
+ return Promise.resolve(lintFullProject(cwd, readOnly))
110
117
  }
111
118
  const js = filterJsFiles(files)
112
119
  if (js.length === 0) return Promise.resolve(0)
113
- return Promise.resolve(lintChangedClassified(js, cwd))
120
+ return Promise.resolve(lintChangedClassified(js, cwd, readOnly))
114
121
  }
@@ -75,7 +75,7 @@ version: '1.30'
75
75
 
76
76
  ## knip
77
77
 
78
- Залежнісний аналіз (knip — невикористані залежності/експорти, `knip.json` канон) крос-файловий, тож винесений у правило `js-lint-ci` (`lint: ci`). Див. `js-lint-ci`.
78
+ Залежнісний аналіз (knip — невикористані залежності/експорти, `knip.json` канон) крос-файловий, тож винесений у правило `js-lint-ci` (`lint: full`). Див. `js-lint-ci`.
79
79
 
80
80
  ## jscpd: рефакторинг і структура
81
81
 
@@ -1 +1 @@
1
- { "auto": { "glob": ["**/*.mjs", "**/*.cjs", "**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] }, "lint": "quick" }
1
+ { "auto": { "glob": ["**/*.mjs", "**/*.cjs", "**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] }, "lint": "per-file" }