@7n/rules 1.27.0 → 1.28.1

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 (268) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +1 -5
  3. package/schemas/v8r-catalog.json +10 -1
  4. package/scripts/auto-rules.mjs +2 -3
  5. package/scripts/docs/auto-rules.md +1 -1
  6. package/scripts/lib/docs/glob-to-regex.md +31 -0
  7. package/scripts/lib/docs/index.md +2 -0
  8. package/scripts/lib/docs/js-source-signals.md +32 -0
  9. package/scripts/lib/glob-to-regex.mjs +79 -0
  10. package/scripts/lib/js-source-signals.mjs +50 -0
  11. package/scripts/utils/ast-scan-utils.mjs +2 -2
  12. package/scripts/utils/docs/ast-scan-utils.md +1 -1
  13. package/rules/bun/bunfig/bunfig.mdc +0 -17
  14. package/rules/bun/bunfig/bunfig.rego +0 -29
  15. package/rules/bun/bunfig/concern.json +0 -9
  16. package/rules/bun/bunfig/template/bunfig.toml.snippet.toml +0 -2
  17. package/rules/bun/docs/index.md +0 -11
  18. package/rules/bun/layout/concern.json +0 -16
  19. package/rules/bun/layout/docs/fix-layout.md +0 -29
  20. package/rules/bun/layout/docs/main.md +0 -34
  21. package/rules/bun/layout/fix-layout.mjs +0 -63
  22. package/rules/bun/layout/layout.mdc +0 -60
  23. package/rules/bun/layout/main.mjs +0 -53
  24. package/rules/bun/licensee/concern.json +0 -7
  25. package/rules/bun/licensee/docs/fix-licensee.md +0 -27
  26. package/rules/bun/licensee/docs/index.md +0 -12
  27. package/rules/bun/licensee/docs/main.md +0 -30
  28. package/rules/bun/licensee/fix-licensee.mjs +0 -31
  29. package/rules/bun/licensee/main.mjs +0 -68
  30. package/rules/bun/lint-surface/concern.json +0 -3
  31. package/rules/bun/lint-surface/lint-surface.mdc +0 -13
  32. package/rules/bun/main.json +0 -1
  33. package/rules/bun/main.mdc +0 -11
  34. package/rules/bun/package_json/concern.json +0 -9
  35. package/rules/bun/package_json/docs/fix-package_json.md +0 -28
  36. package/rules/bun/package_json/docs/index.md +0 -9
  37. package/rules/bun/package_json/fix-package_json.mjs +0 -311
  38. package/rules/bun/package_json/package_json.mdc +0 -14
  39. package/rules/bun/package_json/package_json.rego +0 -64
  40. package/rules/bun/package_json/template/package.json.deny.json +0 -4
  41. package/rules/js/check/check.mdc +0 -26
  42. package/rules/js/check/concern.json +0 -19
  43. package/rules/js/check/docs/eslint-config.md +0 -56
  44. package/rules/js/check/docs/fix-check.md +0 -48
  45. package/rules/js/check/docs/index.md +0 -13
  46. package/rules/js/check/docs/main.md +0 -48
  47. package/rules/js/check/eslint-config.mjs +0 -262
  48. package/rules/js/check/fix-check.mjs +0 -82
  49. package/rules/js/check/main.mjs +0 -310
  50. package/rules/js/dep-policy/concern.json +0 -4
  51. package/rules/js/dep-policy/dep-policy.mdc +0 -36
  52. package/rules/js/dep-policy/docs/main.md +0 -35
  53. package/rules/js/dep-policy/main.mjs +0 -99
  54. package/rules/js/docs/index.md +0 -11
  55. package/rules/js/eslint/concern.json +0 -8
  56. package/rules/js/eslint/docs/fix-eslint.md +0 -50
  57. package/rules/js/eslint/docs/fix-worker.md +0 -31
  58. package/rules/js/eslint/docs/index.md +0 -11
  59. package/rules/js/eslint/docs/main.md +0 -35
  60. package/rules/js/eslint/fix-eslint.mjs +0 -160
  61. package/rules/js/eslint/fix-worker.mjs +0 -139
  62. package/rules/js/eslint/main.mjs +0 -115
  63. package/rules/js/file-extensions/concern.json +0 -3
  64. package/rules/js/file-extensions/file-extensions.mdc +0 -12
  65. package/rules/js/jscpd_config/concern.json +0 -11
  66. package/rules/js/jscpd_config/docs/fix-jscpd_config.md +0 -26
  67. package/rules/js/jscpd_config/docs/index.md +0 -9
  68. package/rules/js/jscpd_config/fix-jscpd_config.mjs +0 -3
  69. package/rules/js/jscpd_config/jscpd_config.mdc +0 -42
  70. package/rules/js/jscpd_config/jscpd_config.rego +0 -44
  71. package/rules/js/jscpd_config/template/.jscpd.json.snippet.json +0 -7
  72. package/rules/js/jscpd_duplicates/concern.json +0 -7
  73. package/rules/js/jscpd_duplicates/docs/main.md +0 -29
  74. package/rules/js/jscpd_duplicates/main.mjs +0 -69
  75. package/rules/js/knip/concern.json +0 -7
  76. package/rules/js/knip/docs/main.md +0 -32
  77. package/rules/js/knip/knip.mdc +0 -15
  78. package/rules/js/knip/main.mjs +0 -68
  79. package/rules/js/lint-findings/concern.json +0 -3
  80. package/rules/js/lint-findings/docs/main.md +0 -41
  81. package/rules/js/lint-findings/main.mjs +0 -125
  82. package/rules/js/main.json +0 -1
  83. package/rules/js/main.mdc +0 -18
  84. package/rules/js/package_json/concern.json +0 -9
  85. package/rules/js/package_json/docs/fix-package_json.md +0 -26
  86. package/rules/js/package_json/docs/index.md +0 -9
  87. package/rules/js/package_json/fix-package_json.mjs +0 -3
  88. package/rules/js/package_json/package_json.mdc +0 -15
  89. package/rules/js/package_json/package_json.rego +0 -142
  90. package/rules/js/package_json/template/package.json.snippet.json +0 -6
  91. package/rules/js/tooling/concern.json +0 -3
  92. package/rules/js/tooling/data/tooling/knip-canonical.json +0 -30
  93. package/rules/js/tooling/data/tooling/oxlint-canonical.json +0 -400
  94. package/rules/js/tooling/docs/main.md +0 -53
  95. package/rules/js/tooling/main.mjs +0 -183
  96. package/rules/js/utils_imports/concern.json +0 -4
  97. package/rules/js/utils_imports/docs/main.md +0 -50
  98. package/rules/js/utils_imports/main.mjs +0 -185
  99. package/rules/js/utils_imports/utils_imports.mdc +0 -15
  100. package/rules/js/vscode_extensions/concern.json +0 -11
  101. package/rules/js/vscode_extensions/docs/fix-vscode_extensions.md +0 -25
  102. package/rules/js/vscode_extensions/docs/index.md +0 -11
  103. package/rules/js/vscode_extensions/fix-vscode_extensions.mjs +0 -1
  104. package/rules/js/vscode_extensions/template/extensions.json.snippet.json +0 -6
  105. package/rules/js/vscode_extensions/vscode_extensions.mdc +0 -11
  106. package/rules/js/vscode_extensions/vscode_extensions.rego +0 -12
  107. package/rules/js-bun-db/connection/concern.json +0 -3
  108. package/rules/js-bun-db/connection/connection.mdc +0 -42
  109. package/rules/js-bun-db/docs/index.md +0 -11
  110. package/rules/js-bun-db/lib/bun-sql-scan.mjs +0 -1055
  111. package/rules/js-bun-db/lib/docs/bun-sql-scan.md +0 -63
  112. package/rules/js-bun-db/lib/docs/index.md +0 -11
  113. package/rules/js-bun-db/main.json +0 -1
  114. package/rules/js-bun-db/main.mdc +0 -8
  115. package/rules/js-bun-db/package_json/concern.json +0 -9
  116. package/rules/js-bun-db/package_json/package_json.mdc +0 -31
  117. package/rules/js-bun-db/package_json/package_json.rego +0 -15
  118. package/rules/js-bun-db/package_json/template/package.json.deny.json +0 -6
  119. package/rules/js-bun-db/pg_format_identifiers/concern.json +0 -3
  120. package/rules/js-bun-db/pg_format_identifiers/pg_format_identifiers.mdc +0 -104
  121. package/rules/js-bun-db/safety/concern.json +0 -4
  122. package/rules/js-bun-db/safety/docs/main.md +0 -34
  123. package/rules/js-bun-db/safety/main.mjs +0 -430
  124. package/rules/js-bun-db/safety/safety.mdc +0 -458
  125. package/rules/js-bun-redis/docs/index.md +0 -11
  126. package/rules/js-bun-redis/imports/concern.json +0 -4
  127. package/rules/js-bun-redis/imports/docs/main.md +0 -36
  128. package/rules/js-bun-redis/imports/imports.mdc +0 -47
  129. package/rules/js-bun-redis/imports/main.mjs +0 -88
  130. package/rules/js-bun-redis/lib/docs/index.md +0 -11
  131. package/rules/js-bun-redis/lib/docs/redis-imports.md +0 -227
  132. package/rules/js-bun-redis/lib/redis-imports.mjs +0 -130
  133. package/rules/js-bun-redis/main.json +0 -1
  134. package/rules/js-bun-redis/main.mdc +0 -8
  135. package/rules/js-bun-redis/package_json/concern.json +0 -9
  136. package/rules/js-bun-redis/package_json/package_json.mdc +0 -11
  137. package/rules/js-bun-redis/package_json/package_json.rego +0 -15
  138. package/rules/js-bun-redis/package_json/template/package.json.deny.json +0 -12
  139. package/rules/js-mssql/deps/concern.json +0 -4
  140. package/rules/js-mssql/deps/docs/main.md +0 -33
  141. package/rules/js-mssql/deps/main.mjs +0 -297
  142. package/rules/js-mssql/docs/index.md +0 -11
  143. package/rules/js-mssql/lib/docs/index.md +0 -11
  144. package/rules/js-mssql/lib/docs/mssql-pool-scan.md +0 -380
  145. package/rules/js-mssql/lib/mssql-pool-scan.mjs +0 -610
  146. package/rules/js-mssql/main.json +0 -1
  147. package/rules/js-mssql/main.mdc +0 -144
  148. package/rules/js-mssql/mssql-tvp/concern.json +0 -3
  149. package/rules/js-mssql/mssql-tvp/mssql-tvp.mdc +0 -77
  150. package/rules/js-mssql/package_json/concern.json +0 -9
  151. package/rules/js-mssql/package_json/package_json.mdc +0 -9
  152. package/rules/js-mssql/package_json/package_json.rego +0 -57
  153. package/rules/js-run/configmap/concern.json +0 -9
  154. package/rules/js-run/configmap/configmap.mdc +0 -37
  155. package/rules/js-run/configmap/configmap.rego +0 -21
  156. package/rules/js-run/configmap/template/configmap.yaml.contains.yml +0 -4
  157. package/rules/js-run/docs/index.md +0 -11
  158. package/rules/js-run/jsconfig/concern.json +0 -9
  159. package/rules/js-run/jsconfig/docs/fix-jsconfig.md +0 -29
  160. package/rules/js-run/jsconfig/docs/index.md +0 -9
  161. package/rules/js-run/jsconfig/fix-jsconfig.mjs +0 -119
  162. package/rules/js-run/jsconfig/jsconfig.mdc +0 -48
  163. package/rules/js-run/jsconfig/jsconfig.rego +0 -59
  164. package/rules/js-run/jsconfig/template/jsconfig.json.snippet.json +0 -10
  165. package/rules/js-run/lib/bunyan-imports.mjs +0 -98
  166. package/rules/js-run/lib/check-env-scan.mjs +0 -338
  167. package/rules/js-run/lib/conn-file-rules.mjs +0 -214
  168. package/rules/js-run/lib/conn-imports-scan.mjs +0 -154
  169. package/rules/js-run/lib/docs/bunyan-imports.md +0 -121
  170. package/rules/js-run/lib/docs/check-env-scan.md +0 -438
  171. package/rules/js-run/lib/docs/conn-file-rules.md +0 -304
  172. package/rules/js-run/lib/docs/conn-imports-scan.md +0 -208
  173. package/rules/js-run/lib/docs/index.md +0 -16
  174. package/rules/js-run/lib/docs/promise-settimeout-scan.md +0 -334
  175. package/rules/js-run/lib/docs/temporal-scan.md +0 -29
  176. package/rules/js-run/lib/promise-settimeout-scan.mjs +0 -128
  177. package/rules/js-run/lib/temporal-scan.mjs +0 -52
  178. package/rules/js-run/main.json +0 -1
  179. package/rules/js-run/main.mdc +0 -16
  180. package/rules/js-run/package_json/concern.json +0 -9
  181. package/rules/js-run/package_json/package_json.mdc +0 -44
  182. package/rules/js-run/package_json/package_json.rego +0 -37
  183. package/rules/js-run/package_json/template/package.json.deny.json +0 -22
  184. package/rules/js-run/project-structure/concern.json +0 -3
  185. package/rules/js-run/project-structure/project-structure.mdc +0 -11
  186. package/rules/js-run/runtime/concern.json +0 -12
  187. package/rules/js-run/runtime/docs/fix-runtime.md +0 -27
  188. package/rules/js-run/runtime/docs/main.md +0 -35
  189. package/rules/js-run/runtime/fix-runtime.mjs +0 -46
  190. package/rules/js-run/runtime/main.mjs +0 -494
  191. package/rules/js-run/runtime/runtime.mdc +0 -184
  192. package/rules/js-run/scope/concern.json +0 -3
  193. package/rules/js-run/scope/scope.mdc +0 -11
  194. package/rules/npm-module/docs/index.md +0 -11
  195. package/rules/npm-module/emit_types_config/concern.json +0 -9
  196. package/rules/npm-module/emit_types_config/docs/fix-emit_types_config.md +0 -25
  197. package/rules/npm-module/emit_types_config/docs/index.md +0 -9
  198. package/rules/npm-module/emit_types_config/emit_types_config.mdc +0 -43
  199. package/rules/npm-module/emit_types_config/emit_types_config.rego +0 -28
  200. package/rules/npm-module/emit_types_config/fix-emit_types_config.mjs +0 -5
  201. package/rules/npm-module/emit_types_config/template/tsconfig.emit-types.json.snippet.json +0 -9
  202. package/rules/npm-module/header_doc_pointer/concern.json +0 -5
  203. package/rules/npm-module/header_doc_pointer/docs/main.md +0 -40
  204. package/rules/npm-module/header_doc_pointer/header_doc_pointer.mdc +0 -18
  205. package/rules/npm-module/header_doc_pointer/main.mjs +0 -131
  206. package/rules/npm-module/main.json +0 -1
  207. package/rules/npm-module/main.mdc +0 -36
  208. package/rules/npm-module/npm_package_json/concern.json +0 -9
  209. package/rules/npm-module/npm_package_json/docs/fix-npm_package_json.md +0 -27
  210. package/rules/npm-module/npm_package_json/docs/index.md +0 -9
  211. package/rules/npm-module/npm_package_json/fix-npm_package_json.mjs +0 -5
  212. package/rules/npm-module/npm_package_json/npm_package_json.mdc +0 -57
  213. package/rules/npm-module/npm_package_json/npm_package_json.rego +0 -73
  214. package/rules/npm-module/npm_package_json/template/package.json.snippet.json +0 -1
  215. package/rules/npm-module/package_structure/concern.json +0 -5
  216. package/rules/npm-module/package_structure/docs/main.md +0 -37
  217. package/rules/npm-module/package_structure/main.mjs +0 -515
  218. package/rules/npm-module/package_structure/package_structure.mdc +0 -63
  219. package/rules/npm-module/root_package_json/concern.json +0 -9
  220. package/rules/npm-module/root_package_json/docs/fix-root_package_json.md +0 -26
  221. package/rules/npm-module/root_package_json/docs/index.md +0 -9
  222. package/rules/npm-module/root_package_json/fix-root_package_json.mjs +0 -5
  223. package/rules/npm-module/root_package_json/root_package_json.mdc +0 -41
  224. package/rules/npm-module/root_package_json/root_package_json.rego +0 -28
  225. package/rules/npm-module/root_package_json/template/package.json.snippet.json +0 -1
  226. package/rules/npm-module/rule_meta/concern.json +0 -8
  227. package/rules/npm-module/rule_meta/docs/main.md +0 -35
  228. package/rules/npm-module/rule_meta/main.mjs +0 -119
  229. package/rules/npm-module/rule_meta/rule_meta.mdc +0 -11
  230. package/rules/npm-module/skill_meta/concern.json +0 -5
  231. package/rules/npm-module/skill_meta/docs/main.md +0 -149
  232. package/rules/npm-module/skill_meta/main.mjs +0 -91
  233. package/rules/npm-module/skill_meta/skill_meta.mdc +0 -11
  234. package/rules/tool-surface/docs/index.md +0 -11
  235. package/rules/tool-surface/main.json +0 -6
  236. package/rules/tool-surface/main.mdc +0 -72
  237. package/rules/vue/composition-api/composition-api.mdc +0 -82
  238. package/rules/vue/composition-api/concern.json +0 -3
  239. package/rules/vue/docs/index.md +0 -11
  240. package/rules/vue/lib/docs/index.md +0 -11
  241. package/rules/vue/lib/docs/vue-forbidden-imports.md +0 -265
  242. package/rules/vue/lib/vue-forbidden-imports.mjs +0 -264
  243. package/rules/vue/main.json +0 -1
  244. package/rules/vue/main.mdc +0 -18
  245. package/rules/vue/nheader-layout/concern.json +0 -3
  246. package/rules/vue/nheader-layout/nheader-layout.mdc +0 -171
  247. package/rules/vue/package_json/concern.json +0 -9
  248. package/rules/vue/package_json/package_json.mdc +0 -30
  249. package/rules/vue/package_json/package_json.rego +0 -140
  250. package/rules/vue/packages/concern.json +0 -6
  251. package/rules/vue/packages/docs/index.md +0 -11
  252. package/rules/vue/packages/docs/main.md +0 -35
  253. package/rules/vue/packages/main.mjs +0 -575
  254. package/rules/vue/packages/packages.mdc +0 -56
  255. package/rules/vue/quasar-ui/concern.json +0 -3
  256. package/rules/vue/quasar-ui/quasar-ui.mdc +0 -32
  257. package/rules/vue/structure/concern.json +0 -3
  258. package/rules/vue/structure/structure.mdc +0 -101
  259. package/rules/vue/testing/concern.json +0 -3
  260. package/rules/vue/testing/testing.mdc +0 -40
  261. package/rules/vue/tfm-translations/concern.json +0 -7
  262. package/rules/vue/tfm-translations/docs/main.md +0 -29
  263. package/rules/vue/tfm-translations/main.mjs +0 -55
  264. package/rules/vue/tfm-translations/tfm-translations.mdc +0 -32
  265. package/rules/vue/vite-config/concern.json +0 -3
  266. package/rules/vue/vite-config/vite-config.mdc +0 -153
  267. package/rules/vue/vite-env/concern.json +0 -3
  268. package/rules/vue/vite-env/vite-env.mdc +0 -61
@@ -1,64 +0,0 @@
1
- # Перевірка кореневого `package.json` для bun (bun.mdc).
2
- #
3
- # Канон надходить через --data: { "template": { "deny": ... } }
4
- # Структура --data сформована з template/package.json.deny.json
5
- # (top-level fields заборонені у root).
6
- #
7
- # Логіка, що ЛИШАЄТЬСЯ у rego (inverse-patterns, не виносяться у template):
8
- # - `devDependencies` лише `@nitra/*` + root-only тестові peer/tools для `@7n/test coverage`
9
- # (правило `test` enabled завжди — див. `test/auto.md`; published workspace-и не мають
10
- # `devDependencies` за `npm-module.mdc`)
11
- #
12
- # Перевірки, які потребують FS / cross-file контексту, лишаються у JS.
13
- package bun.package_json
14
-
15
- import rego.v1
16
-
17
- # ── deny: заборонені top-level поля (template-driven) ─────────────────────
18
-
19
- # Сентинельний value відрізняє «поле відсутнє» від «поле є з будь-яким значенням»
20
- # (наприклад `dependencies: {}` — присутнє але порожнє → теж заборонено).
21
- deny contains msg if {
22
- some field, reason in data.template.deny
23
- object.get(input, field, "__bun_missing__") != "__bun_missing__"
24
- msg := sprintf("package.json: поле %s — %s", [field, reason])
25
- }
26
-
27
- # ── deny: scripts.lint / scripts.lint-* заборонені (bun.mdc lint) ────────
28
-
29
- deny contains msg if {
30
- is_object(input.scripts)
31
- some script_name, _ in input.scripts
32
- regex.match(`^lint(-.*)?$`, script_name)
33
- msg := sprintf(
34
- "package.json: scripts.%s заборонений — лінт запускається через n-rules lint, не через package.json-скрипти (bun.mdc)",
35
- [script_name],
36
- )
37
- }
38
-
39
- # ── deny: devDependencies — лише `@nitra/*`/`@7n/*` + root-only тестові peer/tools ─
40
-
41
- deny contains msg if {
42
- is_object(input.devDependencies)
43
- some name, _ in input.devDependencies
44
- not allowed_root_dev_dependency(name)
45
- msg := sprintf("Кореневі devDependencies: дозволені лише @nitra/*/@7n/* або root-only test peers — прибери або перенеси: %s (bun.mdc)", [name])
46
- }
47
-
48
- # ── helpers ────────────────────────────────────────────────────────────────
49
-
50
- # @stryker-mutator/core — обов'язковий exact-pin peer vitest-runner@9+ (раніше тягнувся транзитивно)
51
- # @7n/test — оркестратор `coverage` (npx @7n/test coverage); devDependency, щоб npx резолвив
52
- # локально без мережевого fetch щоразу.
53
- allowed_root_test_deps := {"vitest", "@vitest/coverage-v8", "@stryker-mutator/vitest-runner", "@stryker-mutator/core", "@playwright/test", "@7n/test"}
54
-
55
- allowed_root_dev_dependency(name) if {
56
- startswith(name, "@nitra/")
57
- } else if {
58
- startswith(name, "@7n/")
59
- } else if {
60
- # Vitest/Stryker peer/tools + @7n/test (`coverage`) для `@7n/test coverage` живуть у корені
61
- # будь-якого монорепо-споживача: правило `test` enabled завжди, а published workspace-и
62
- # не мають `devDependencies` (`npm-module.mdc`).
63
- name in allowed_root_test_deps
64
- }
@@ -1,4 +0,0 @@
1
- {
2
- "packageManager": "видали поле — Bun не потребує packageManager (bun.mdc)",
3
- "dependencies": "кореневий package.json не повинен містити dependencies — додай у workspace-пакети (bun.mdc)"
4
- }
@@ -1,26 +0,0 @@
1
- ## `for...in` заборонено — рефакторити на `for...of`
2
-
3
- Конструкція `for (const k in obj)` обходить успадковані ключі прототипу, тому майже завжди тягне `Object.hasOwn(obj, k)`-guard. Заборонена у `@nitra/eslint-config` через `no-restricted-syntax` для `ForInStatement` (з версії **3.8.0**). У каноні oxlint (`guard-for-in: "deny"` у [oxlint-canonical.json](../tooling/data/tooling/oxlint-canonical.json)) лишається `guard-for-in` як часткова страховка (oxlint не підтримує `no-restricted-syntax`) — `checkOxlintRc` у `check/main.mjs` звіряє `.oxlintrc.json` проєкту з цим каноном.
4
-
5
- Замість цього обходь масив напряму, а об'єкт — через `Object.entries` / `Object.keys` / `Object.values`. У такому коді guard за `Object.hasOwn` стає непотрібним і має зникнути разом із `for...in`.
6
-
7
- ```javascript title="❌ до"
8
- for (const k in obj) {
9
- if (!Object.hasOwn(obj, k)) continue
10
- use(k, obj[k])
11
- }
12
-
13
- for (const i in arr) {
14
- use(arr[i])
15
- }
16
- ```
17
-
18
- ```javascript title="✅ після"
19
- for (const [k, v] of Object.entries(obj)) {
20
- use(k, v)
21
- }
22
-
23
- for (const item of arr) {
24
- use(item)
25
- }
26
- ```
@@ -1,19 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "lint": {
4
- "scope": "full",
5
- "glob": [
6
- "eslint.config.js",
7
- "eslint.config.mjs",
8
- "**/package.json",
9
- ".oxlintrc.json",
10
- ".github/workflows/lint-js.yml",
11
- ".github/workflows/lint.yml",
12
- "knip.json",
13
- ".eslintrc",
14
- ".eslintrc.js",
15
- ".eslintrc.json",
16
- ".eslintrc.yml"
17
- ]
18
- }
19
- }
@@ -1,56 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: eslint-config.mjs
4
- resource: npm/rules/js/check/eslint-config.mjs
5
- docgen:
6
- crc: 6626a862
7
- model: manual
8
- ---
9
-
10
- ## Огляд
11
-
12
- Детекція воркспейс-типів (node/vue) репозиторію і чистий (read-only) планувальник
13
- scaffold/merge для `eslint.config.js` consumer-репо. Спільне джерело правди для
14
- detector-а `main.mjs` (перевірка, що кожен vue-воркспейс присутній у `vue: [...]`
15
- аргументах `getConfig`) і T0-фіксера `fix-check.mjs` (детерміноване створення або
16
- хірургічне оновлення конфігу — без повного перезапису файлу).
17
-
18
- ## Поведінка
19
-
20
- - Тип воркспейсу визначається за root `package.json`: записи `workspaces`
21
- (glob-и типу `packages/*` розгортаються у наявні директорії); воркспейс — vue,
22
- якщо його `package.json` має `vue`/`nuxt` у deps або в ньому є хоч один `.vue`
23
- файл (`node_modules`/`dist` ігноруються). Без `workspaces` класифікується сам
24
- корінь як `.`.
25
- - Merge наявного конфігу — хірургічний: додає відсутній `**/auto-imports.d.ts`
26
- у перший `ignores: [...]`, вставляє відсутні vue-воркспейси у `vue: [...]`
27
- (або нову властивість одразу після `getConfig({`), вилучає ці ж воркспейси з
28
- `node: [...]`. Кастомні ignores, overrides і коментарі не змінюються.
29
- - Fail-safe: якщо структура конфігу не розпізнається (немає `getConfig({`),
30
- файл не чіпається — порушення лишається для ручного розгляду.
31
-
32
- ## Публічний API
33
-
34
- - `detectWorkspaceTypes(cwd)` → `{ node: string[], vue: string[] }` — директорії
35
- за типами.
36
- - `parseVueList(raw)` → нормалізовані записи `vue: [...]` з тексту конфігу.
37
- - `renderEslintConfigScaffold(types)` → повний шаблон `eslint.config.js`
38
- (лише непорожні типи).
39
- - `mergeEslintConfig(raw, types)` → новий вміст (`=== raw`, якщо merge
40
- неможливий чи не потрібен).
41
- - `planEslintConfigFix(cwd)` → `{ path, content, message } | null` — ідемпотентний
42
- план для T0 (`null` на виправленому дереві).
43
- - Константи reason-ів: `ESLINT_CONFIG_MISSING`, `ESLINT_CONFIG_IGNORES`,
44
- `ESLINT_CONFIG_VUE_WORKSPACE`; `AUTO_IMPORTS_IGNORE`.
45
-
46
- ## Де використовується
47
-
48
- - `npm/rules/js/check/main.mjs` — detector (reason-и порушень, перевірка
49
- vue-воркспейсів).
50
- - `npm/rules/js/check/fix-check.mjs` — T0-фіксер (виконання плану).
51
-
52
- ## Гарантії поведінки
53
-
54
- - Read-only: модуль лише читає ФС і повертає план; запис виконує викликач.
55
- - Ідемпотентність: повторний `planEslintConfigFix` на виправленому дереві → `null`.
56
- - Биті/відсутні JSON-файли не кидають виняток (повертається `null`-об'єкт).
@@ -1,48 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: fix-check.mjs
4
- resource: npm/rules/js/check/fix-check.mjs
5
- docgen:
6
- crc: 48182147
7
- model: manual
8
- ---
9
-
10
- ## Огляд
11
-
12
- T0-autofix для `js/check`: два детерміновані патерни, обидва без LLM.
13
-
14
- - `js-check-eslint-config` — scaffold/merge `eslint.config.js` за планом
15
- `planEslintConfigFix` (детекція воркспейс-типів node/vue). Раніше ці
16
- порушення йшли у LLM-ладдер, який переписував конфіг цілком і вгадував типи
17
- (інцидент: у vue-монорепо записано `getConfig({ node: ['npm'] })` — eslint
18
- перестав обробляти .vue файли).
19
- - `js-check-oxlintrc` — scaffold/merge `.oxlintrc.json` за планом
20
- `planOxlintrcFix` (`../tooling/main.mjs`). Раніше ці порушення теж йшли у
21
- LLM-ладдер: 15 КБ канону дешева модель не відтворює byte-perfect (verify
22
- fail), а дорожча не встигає за один rung-таймаут.
23
-
24
- ## Поведінка
25
-
26
- - `js-check-eslint-config` тригериться на reason-и `eslint-config-missing` /
27
- `eslint-config-ignores` / `eslint-config-vue-workspace`. `apply` перераховує
28
- план із поточного стану диска (ідемпотентно): відсутній конфіг —
29
- створюється з детектованими типами; наявний — зазнає хірургічного merge, без
30
- повного перезапису. Якщо план порожній (`null`) — жодних змін
31
- (`touchedFiles: []`).
32
- - `js-check-oxlintrc` тригериться на reason-и `oxlintrc-missing` /
33
- `oxlintrc-drift` (`OXLINTRC_MISSING` / `OXLINTRC_DRIFT` з `../tooling/main.mjs`).
34
- `apply` читає наявний `.oxlintrc.json` (`null`, якщо відсутній або невалідний
35
- JSON) і канон, будує злитий обʼєкт через `planOxlintrcFix` і перезаписує
36
- файл цілком (JSON, 2-space, з кінцевим переносом рядка).
37
- - Решта порушень `js/check` (engines, workflows) — поза цим T0, стандартний
38
- шлях (ladder/manual).
39
- - Обидва патерни викликають `ctx.recordWrite` перед записом (pre-image для
40
- central rollback).
41
-
42
- ## Гарантії поведінки
43
-
44
- - `js-check-eslint-config` записує лише `eslint.config.js`/`eslint.config.mjs`
45
- у корені репо; наявний конфіг ніколи не перезаписується цілком — тільки
46
- точкові вставки/заміни.
47
- - `js-check-oxlintrc` записує лише `.oxlintrc.json` у корені репо; результат
48
- завжди проходить `verifyOxlintRcAgainstCanonical` без ручного втручання.
@@ -1,13 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: npm/rules/js/check
4
- resource: npm/rules/js/check/
5
- ---
6
-
7
- # npm/rules/js/check
8
-
9
- | Файл | Тип |
10
- | ------------------------------------- | --------- |
11
- | [eslint-config.mjs](eslint-config.md) | JS Module |
12
- | [fix-check.mjs](fix-check.md) | JS Module |
13
- | [main.mjs](main.md) | JS Module |
@@ -1,48 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: main.mjs
4
- resource: npm/rules/js/check/main.mjs
5
- docgen:
6
- crc: 128c6700
7
- model: manual
8
- ---
9
-
10
- ## Огляд
11
-
12
- Detector concern-а `js/check`: read-only перевірка відповідності проєкту правилам
13
- `js.mdc`. Накопичує порушення через violation-reporter і повертає їх runner-у;
14
- нічого не друкує і (за одним винятком — baseline `knip.json`) нічого не пише.
15
-
16
- ## Поведінка
17
-
18
- - `eslint.config.js`/`eslint.config.mjs`: файл існує (інакше reason
19
- `eslint-config-missing`), містить `getConfig` та імпорт `@nitra/eslint-config`,
20
- в ignores є `**/auto-imports.d.ts` (reason `eslint-config-ignores`).
21
- - Воркспейс-типи: кожен vue-воркспейс (детекція з `eslint-config.mjs` — root
22
- `workspaces` + vue/nuxt-залежність або `.vue` файли) має бути у `vue: [...]`
23
- аргументах `getConfig`, інакше reason `eslint-config-vue-workspace` — без
24
- цього eslint не парсить `.vue` файли воркспейсу. Ці три reason-и детерміновано
25
- виправляє T0 `fix-check.mjs`.
26
- - Кожен workspace `package.json`: `"type": "module"`, `engines.node >= 24`,
27
- `engines.bun >= 1.3` (кореневий `package.json` валідує Rego
28
- `npm/policy/js_lint/package_json/`).
29
- - `.oxlintrc.json`: існує, валідний JSON, збігається з каноном oxlint із пакета
30
- `@7n/rules` (`verifyOxlintRcAgainstCanonical` з `../tooling/main.mjs`).
31
- Відсутність — reason `oxlintrc-missing`, розходження з каноном — reason
32
- `oxlintrc-drift` (константи `OXLINTRC_MISSING`/`OXLINTRC_DRIFT`); обидва
33
- детерміновано виправляє T0 `fix-check.mjs` (патерн `js-check-oxlintrc`).
34
- - `.github/workflows/lint-js.yml`: існує; `lint.yml` (якщо є) не дублює
35
- oxlint/eslint/jscpd кроки.
36
- - `knip.json`: якщо відсутній — копіюється канонічний baseline із пакета
37
- (side effect, описаний у js.mdc); подальший вміст локально не валідується.
38
- - Застарілі конфіги ESLint (`.eslintrc`, `.eslintrc.js`, `.eslintrc.json`,
39
- `.eslintrc.yml`) — порушення: лише flat config.
40
-
41
- ## Публічний API
42
-
43
- - `lint(ctx)` → `LintResult` — перелік порушень concern-а.
44
-
45
- ## Гарантії поведінки
46
-
47
- - Read-only, крім єдиного side effect — створення `knip.json` з канону.
48
- - Порушення повертаються reporter-ом, не друкуються.
@@ -1,262 +0,0 @@
1
- /** @see ./docs/eslint-config.md */
2
-
3
- /**
4
- * Детекція воркспейс-типів (node vs vue) і чистий планувальник scaffold/merge
5
- * для `eslint.config.js` consumer-репо. Використовується двома споживачами:
6
- * - detector `main.mjs` (read-only): перевірка, що кожен vue-воркспейс
7
- * присутній у `vue: [...]` аргументах getConfig — інакше .vue файли не
8
- * парсяться eslint-ом;
9
- * - T0 `fix-check.mjs`: детермінований scaffold відсутнього конфігу або
10
- * хірургічний merge наявного (додати vue-воркспейси, прибрати їх із
11
- * node-списку, доставити ignores) — БЕЗ повного перезапису файлу.
12
- *
13
- * Merge fail-safe: якщо структура конфігу не розпізнається regex-ами
14
- * (немає `getConfig({`), планувальник повертає null і файл не чіпається.
15
- */
16
- import { existsSync } from 'node:fs'
17
- import { readFile } from 'node:fs/promises'
18
- import { join } from 'node:path'
19
-
20
- import { globby } from 'globby'
21
-
22
- /** Відсутній eslint.config.{js,mjs} — T0 скаффолдить із детектованих типів. */
23
- export const ESLINT_CONFIG_MISSING = 'eslint-config-missing'
24
- /** У ignores немає `**\/auto-imports.d.ts` — T0 дописує в наявний масив. */
25
- export const ESLINT_CONFIG_IGNORES = 'eslint-config-ignores'
26
- /** Vue-воркспейс відсутній у `vue: [...]` getConfig — .vue файли не парсяться. */
27
- export const ESLINT_CONFIG_VUE_WORKSPACE = 'eslint-config-vue-workspace'
28
-
29
- export const AUTO_IMPORTS_IGNORE = '**/auto-imports.d.ts'
30
-
31
- const VUE_LIST_RE = /\bvue\s*:\s*\[([^\]]*)\]/u
32
- const NODE_LIST_RE = /\bnode\s*:\s*\[([^\]]*)\]/u
33
- const GET_CONFIG_OBJ_RE = /getConfig\(\s*\{/u
34
- const IGNORES_OPEN_RE = /\bignores\s*:\s*\[/u
35
- const STRING_ENTRY_RE = /'([^']*)'|"([^"]*)"/gu
36
-
37
- /**
38
- * @param {string} p шлях воркспейсу з package.json або запис зі списку getConfig
39
- * @returns {string} канонічна форма без `./` на початку і `/` в кінці
40
- */
41
- function normalizeWs(p) {
42
- let s = p.startsWith('./') ? p.slice(2) : p
43
- while (s.endsWith('/')) s = s.slice(0, -1)
44
- return s || '.'
45
- }
46
-
47
- /**
48
- * @param {string} abs абсолютний шлях до JSON-файлу
49
- * @returns {Promise<object|null>} розпарсений об'єкт або null (немає/битий)
50
- */
51
- async function readJsonOrNull(abs) {
52
- try {
53
- return JSON.parse(await readFile(abs, 'utf8'))
54
- } catch {
55
- return null
56
- }
57
- }
58
-
59
- /**
60
- * Розгортає записи `workspaces` root package.json у список наявних директорій
61
- * (glob-записи типу `packages/*` — через globby onlyDirectories).
62
- * @param {string} cwd корінь репозиторію
63
- * @param {unknown[]} patterns поле workspaces
64
- * @returns {Promise<string[]>} posix-relative директорії воркспейсів, що існують
65
- */
66
- async function expandWorkspaces(cwd, patterns) {
67
- const dirs = []
68
- for (const p of patterns) {
69
- if (typeof p !== 'string' || p.length === 0) continue
70
- const norm = normalizeWs(p)
71
- if (norm.includes('*')) {
72
- dirs.push(...(await globby(norm, { cwd, onlyDirectories: true, gitignore: false })))
73
- } else if (existsSync(join(cwd, norm))) {
74
- dirs.push(norm)
75
- }
76
- }
77
- return [...new Set(dirs.map(d => normalizeWs(d)))]
78
- }
79
-
80
- /**
81
- * Чи є директорія Vue-кодом: `vue`/`nuxt` у deps її package.json (дешева
82
- * перевірка першою) або наявність бодай одного `.vue` файлу.
83
- * @param {string} cwd корінь репозиторію
84
- * @param {string} ws posix-relative директорія воркспейсу (або `.` для кореня)
85
- * @returns {Promise<boolean>} true — vue-воркспейс
86
- */
87
- async function isVueWorkspace(cwd, ws) {
88
- const dir = ws === '.' ? cwd : join(cwd, ws)
89
- const pkg = await readJsonOrNull(join(dir, 'package.json'))
90
- const deps = { ...pkg?.dependencies, ...pkg?.devDependencies }
91
- if ('vue' in deps || 'nuxt' in deps) return true
92
- const vueFiles = await globby('**/*.vue', {
93
- cwd: dir,
94
- gitignore: false,
95
- ignore: ['**/node_modules/**', '**/dist/**'],
96
- deep: 8
97
- })
98
- return vueFiles.length > 0
99
- }
100
-
101
- /**
102
- * Класифікує репозиторій за воркспейс-типами для аргументів getConfig.
103
- * Монорепо (workspaces у root package.json) — кожен воркспейс окремо;
104
- * без workspaces — сам корінь як `.`.
105
- * @param {string} cwd корінь репозиторію
106
- * @returns {Promise<{ node: string[], vue: string[] }>} директорії за типами
107
- */
108
- export async function detectWorkspaceTypes(cwd) {
109
- const pkg = await readJsonOrNull(join(cwd, 'package.json'))
110
- const wsField = Array.isArray(pkg?.workspaces) ? pkg.workspaces : []
111
- const dirs = await expandWorkspaces(cwd, wsField)
112
-
113
- if (dirs.length === 0) {
114
- const rootIsVue = await isVueWorkspace(cwd, '.')
115
- return rootIsVue ? { node: [], vue: ['.'] } : { node: ['.'], vue: [] }
116
- }
117
-
118
- /** @type {{ node: string[], vue: string[] }} */
119
- const types = { node: [], vue: [] }
120
- for (const ws of dirs) {
121
- if (await isVueWorkspace(cwd, ws)) {
122
- types.vue.push(ws)
123
- } else {
124
- types.node.push(ws)
125
- }
126
- }
127
- return types
128
- }
129
-
130
- /**
131
- * Записи string-літералів усередині вмісту списку (`vue: [...]`/`node: [...]`).
132
- * @param {string|undefined} inner текст між дужками списку
133
- * @returns {string[]} нормалізовані записи списку
134
- */
135
- function listEntries(inner) {
136
- if (inner === undefined) return []
137
- return inner
138
- .matchAll(STRING_ENTRY_RE)
139
- .map(m => normalizeWs(m[1] ?? m[2]))
140
- .toArray()
141
- }
142
-
143
- /**
144
- * Vue-записи з тексту конфігу — для перевірки detector-ом.
145
- * @param {string} raw вміст eslint.config
146
- * @returns {string[]} нормалізовані записи `vue: [...]`
147
- */
148
- export function parseVueList(raw) {
149
- return listEntries(raw.match(VUE_LIST_RE)?.[1])
150
- }
151
-
152
- /**
153
- * @param {string} ws запис для вставки у список
154
- * @returns {string} одинарно-квотований літерал
155
- */
156
- function quote(ws) {
157
- return `'${ws}'`
158
- }
159
-
160
- /**
161
- * Повний шаблон eslint.config.js для scaffold (файл відсутній). Включає лише
162
- * непорожні типи; порядок ключів — node, vue (стиль власного конфігу репо).
163
- * @param {{ node: string[], vue: string[] }} types детектовані воркспейс-типи
164
- * @returns {string} вміст файлу
165
- */
166
- export function renderEslintConfigScaffold(types) {
167
- const args = []
168
- if (types.node.length > 0) args.push(` node: [${types.node.map(w => quote(w)).join(', ')}]`)
169
- if (types.vue.length > 0) args.push(` vue: [${types.vue.map(w => quote(w)).join(', ')}]`)
170
- return [
171
- "import { getConfig } from '@nitra/eslint-config'",
172
- '',
173
- 'export default [',
174
- ' {',
175
- ` ignores: ['${AUTO_IMPORTS_IGNORE}']`,
176
- ' },',
177
- ' ...getConfig({',
178
- args.join(',\n'),
179
- ' })',
180
- ']',
181
- ''
182
- ].join('\n')
183
- }
184
-
185
- /**
186
- * Хірургічний merge наявного конфігу під детектовані типи:
187
- * 1) відсутній `**\/auto-imports.d.ts` → вставка в перший `ignores: [`;
188
- * 2) vue-воркспейси поза `vue: [...]` → вставка у список (або нова властивість
189
- * одразу після `getConfig({`);
190
- * 3) ті самі воркспейси у `node: [...]` → вилучення звідти.
191
- * Решта файлу (кастомні ignores, overrides, коментарі) — недоторкана.
192
- * @param {string} raw вміст eslint.config
193
- * @param {{ node: string[], vue: string[] }} types детектовані воркспейс-типи
194
- * @returns {string} новий вміст (=== raw, якщо merge неможливий/не потрібен)
195
- */
196
- export function mergeEslintConfig(raw, types) {
197
- let out = raw
198
-
199
- if (!out.includes(AUTO_IMPORTS_IGNORE) && IGNORES_OPEN_RE.test(out)) {
200
- out = out.replace(IGNORES_OPEN_RE, m => `${m}'${AUTO_IMPORTS_IGNORE}', `)
201
- }
202
-
203
- const missingVue = types.vue.filter(ws => !parseVueList(out).includes(ws))
204
- if (missingVue.length > 0) {
205
- const inserted = missingVue.map(w => quote(w)).join(', ')
206
- const vueMatch = out.match(VUE_LIST_RE)
207
- if (vueMatch) {
208
- const rest = vueMatch[1].trim().length > 0 ? `, ${vueMatch[1]}` : vueMatch[1]
209
- out = out.replace(VUE_LIST_RE, () => `vue: [${inserted}${rest}]`)
210
- } else if (GET_CONFIG_OBJ_RE.test(out)) {
211
- out = out.replace(GET_CONFIG_OBJ_RE, m => `${m}\n vue: [${inserted}],`)
212
- }
213
- // без getConfig({ — merge неможливий, лишаємо як є (fail-safe)
214
- }
215
-
216
- const nodeMatch = out.match(NODE_LIST_RE)
217
- if (nodeMatch) {
218
- const entries = listEntries(nodeMatch[1])
219
- const kept = entries.filter(e => !types.vue.includes(e))
220
- if (kept.length !== entries.length) {
221
- out = out.replace(NODE_LIST_RE, () => `node: [${kept.map(w => quote(w)).join(', ')}]`)
222
- }
223
- }
224
-
225
- return out
226
- }
227
-
228
- /**
229
- * План детермінованого фіксу eslint.config для T0: scaffold відсутнього файлу
230
- * або merge наявного. Ідемпотентний — повторний виклик на виправленому дереві
231
- * повертає null.
232
- * @param {string} cwd корінь репозиторію
233
- * @returns {Promise<{ path: string, content: string, message: string }|null>} запис для виконання або null
234
- */
235
- export async function planEslintConfigFix(cwd) {
236
- const types = await detectWorkspaceTypes(cwd)
237
-
238
- const existing = ['eslint.config.js', 'eslint.config.mjs'].find(f => existsSync(join(cwd, f)))
239
- if (!existing) {
240
- const summary = [
241
- types.node.length > 0 ? `node: [${types.node.join(', ')}]` : '',
242
- types.vue.length > 0 ? `vue: [${types.vue.join(', ')}]` : ''
243
- ]
244
- .filter(Boolean)
245
- .join(', ')
246
- return {
247
- path: join(cwd, 'eslint.config.js'),
248
- content: renderEslintConfigScaffold(types),
249
- message: `створено eslint.config.js (${summary})`
250
- }
251
- }
252
-
253
- const abs = join(cwd, existing)
254
- const raw = await readFile(abs, 'utf8')
255
- const merged = mergeEslintConfig(raw, types)
256
- if (merged === raw) return null
257
- return {
258
- path: abs,
259
- content: merged,
260
- message: `${existing}: merge під воркспейс-типи (vue: [${types.vue.join(', ')}])`
261
- }
262
- }
@@ -1,82 +0,0 @@
1
- /** @see ./docs/fix-check.md */
2
-
3
- /**
4
- * T0-autofix для `js/check` — детермінований scaffold/merge `eslint.config.js`
5
- * і `.oxlintrc.json`. Раніше обидва йшли у LLM-ладдер, який переписував конфіги
6
- * цілком: для eslint.config.js це вгадувало воркспейс-типи (інцидент: у
7
- * vue-монорепо записано `getConfig({ node: ['npm'] })` — eslint перестав
8
- * обробляти .vue файли), а для .oxlintrc.json — 15 КБ канону, який дешевій
9
- * моделі не відтворити byte-perfect (verify fail), а дорожчій — не встигнути
10
- * за один rung-таймаут.
11
- *
12
- * Джерело правди для eslint.config.js — `planEslintConfigFix` (детекція типів
13
- * із workspaces root package.json + vue-залежність/.vue-файли), наявний конфіг
14
- * оновлюється точковим merge хірургічно, без повного перезапису.
15
- *
16
- * Джерело правди для .oxlintrc.json — `planOxlintrcFix`, що дзеркалить
17
- * `verifyOxlintRcAgainstCanonical`: відсутній файл копіює канон, наявний —
18
- * доповнюється до канону без втрати project-specific розширень (зайві
19
- * `rules`-ключі й `ignorePatterns` зберігаються).
20
- *
21
- * Решта порушень js/check (engines, workflows) — поза цим T0, стандартний шлях
22
- * (ladder/manual).
23
- */
24
- import { existsSync } from 'node:fs'
25
- import { readFile, writeFile } from 'node:fs/promises'
26
- import { join } from 'node:path'
27
-
28
- import {
29
- ESLINT_CONFIG_IGNORES,
30
- ESLINT_CONFIG_MISSING,
31
- ESLINT_CONFIG_VUE_WORKSPACE,
32
- planEslintConfigFix
33
- } from './eslint-config.mjs'
34
- import { OXLINT_CANONICAL_JSON_PATH, OXLINTRC_DRIFT, OXLINTRC_MISSING, planOxlintrcFix } from '../tooling/main.mjs'
35
-
36
- const ESLINT_CONFIG_REASONS = new Set([ESLINT_CONFIG_MISSING, ESLINT_CONFIG_IGNORES, ESLINT_CONFIG_VUE_WORKSPACE])
37
- const OXLINTRC_REASONS = new Set([OXLINTRC_MISSING, OXLINTRC_DRIFT])
38
-
39
- /**
40
- * Читає `.oxlintrc.json`, якщо є й валідний JSON; інакше `null` (трактується
41
- * як відсутній файл — `planOxlintrcFix` будує з канону).
42
- * @param {string} oxPath абсолютний шлях до `.oxlintrc.json`
43
- * @returns {Promise<unknown>} розпарсений вміст або `null`
44
- */
45
- async function readOxlintrcOrNull(oxPath) {
46
- if (!existsSync(oxPath)) return null
47
- try {
48
- return JSON.parse(await readFile(oxPath, 'utf8'))
49
- } catch {
50
- return null
51
- }
52
- }
53
-
54
- /** @type {import('../../../scripts/lib/lint-surface/types.mjs').T0Pattern[]} */
55
- export const patterns = [
56
- {
57
- id: 'js-check-eslint-config',
58
- test: violations => violations.some(v => ESLINT_CONFIG_REASONS.has(v.reason)),
59
- apply: async (violations, ctx) => {
60
- const plan = await planEslintConfigFix(ctx.cwd)
61
- if (plan === null) return { touchedFiles: [] }
62
- ctx.recordWrite?.(plan.path)
63
- await writeFile(plan.path, plan.content, 'utf8')
64
- return { touchedFiles: [plan.path], message: plan.message }
65
- }
66
- },
67
- {
68
- id: 'js-check-oxlintrc',
69
- test: violations => violations.some(v => OXLINTRC_REASONS.has(v.reason)),
70
- apply: async (violations, ctx) => {
71
- const oxPath = join(ctx.cwd, '.oxlintrc.json')
72
- const [actual, canonicalRaw] = await Promise.all([
73
- readOxlintrcOrNull(oxPath),
74
- readFile(OXLINT_CANONICAL_JSON_PATH, 'utf8')
75
- ])
76
- const merged = planOxlintrcFix(actual, JSON.parse(canonicalRaw))
77
- ctx.recordWrite?.(oxPath)
78
- await writeFile(oxPath, `${JSON.stringify(merged, null, 2)}\n`, 'utf8')
79
- return { touchedFiles: [oxPath], message: '.oxlintrc.json: T0 merge до канону oxlint (@7n/rules)' }
80
- }
81
- }
82
- ]