@7n/rules-lang-js 0.3.1 → 0.4.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 (258) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +14 -4
  3. package/rules/bun/bunfig/bunfig.mdc +17 -0
  4. package/rules/bun/bunfig/bunfig.rego +29 -0
  5. package/rules/bun/bunfig/concern.json +9 -0
  6. package/rules/bun/bunfig/template/bunfig.toml.snippet.toml +2 -0
  7. package/rules/bun/docs/index.md +11 -0
  8. package/rules/bun/layout/concern.json +16 -0
  9. package/rules/bun/layout/docs/fix-layout.md +29 -0
  10. package/rules/bun/layout/docs/main.md +34 -0
  11. package/rules/bun/layout/fix-layout.mjs +63 -0
  12. package/rules/bun/layout/layout.mdc +60 -0
  13. package/rules/bun/layout/main.mjs +53 -0
  14. package/rules/bun/licensee/concern.json +7 -0
  15. package/rules/bun/licensee/docs/fix-licensee.md +27 -0
  16. package/rules/bun/licensee/docs/index.md +12 -0
  17. package/rules/bun/licensee/docs/main.md +30 -0
  18. package/rules/bun/licensee/fix-licensee.mjs +31 -0
  19. package/rules/bun/licensee/main.mjs +68 -0
  20. package/rules/bun/lint-surface/concern.json +3 -0
  21. package/rules/bun/lint-surface/lint-surface.mdc +13 -0
  22. package/rules/bun/main.json +1 -0
  23. package/rules/bun/main.mdc +11 -0
  24. package/rules/bun/package_json/concern.json +9 -0
  25. package/rules/bun/package_json/docs/fix-package_json.md +28 -0
  26. package/rules/bun/package_json/docs/index.md +9 -0
  27. package/rules/bun/package_json/fix-package_json.mjs +311 -0
  28. package/rules/bun/package_json/package_json.mdc +14 -0
  29. package/rules/bun/package_json/package_json.rego +64 -0
  30. package/rules/bun/package_json/template/package.json.deny.json +4 -0
  31. package/rules/js/check/check.mdc +26 -0
  32. package/rules/js/check/concern.json +19 -0
  33. package/rules/js/check/docs/eslint-config.md +56 -0
  34. package/rules/js/check/docs/fix-check.md +48 -0
  35. package/rules/js/check/docs/index.md +13 -0
  36. package/rules/js/check/docs/main.md +48 -0
  37. package/rules/js/check/eslint-config.mjs +262 -0
  38. package/rules/js/check/fix-check.mjs +82 -0
  39. package/rules/js/check/main.mjs +310 -0
  40. package/rules/js/dep-policy/concern.json +4 -0
  41. package/rules/js/dep-policy/dep-policy.mdc +36 -0
  42. package/rules/js/dep-policy/docs/main.md +35 -0
  43. package/rules/js/dep-policy/main.mjs +99 -0
  44. package/rules/js/docs/index.md +11 -0
  45. package/rules/js/eslint/concern.json +8 -0
  46. package/rules/js/eslint/docs/fix-eslint.md +50 -0
  47. package/rules/js/eslint/docs/fix-worker.md +31 -0
  48. package/rules/js/eslint/docs/index.md +11 -0
  49. package/rules/js/eslint/docs/main.md +35 -0
  50. package/rules/js/eslint/fix-eslint.mjs +160 -0
  51. package/rules/js/eslint/fix-worker.mjs +139 -0
  52. package/rules/js/eslint/main.mjs +115 -0
  53. package/rules/js/file-extensions/concern.json +3 -0
  54. package/rules/js/file-extensions/file-extensions.mdc +12 -0
  55. package/rules/js/jscpd_config/concern.json +11 -0
  56. package/rules/js/jscpd_config/docs/fix-jscpd_config.md +25 -0
  57. package/rules/js/jscpd_config/docs/index.md +9 -0
  58. package/rules/js/jscpd_config/fix-jscpd_config.mjs +3 -0
  59. package/rules/js/jscpd_config/jscpd_config.mdc +42 -0
  60. package/rules/js/jscpd_config/jscpd_config.rego +44 -0
  61. package/rules/js/jscpd_config/template/.jscpd.json.snippet.json +7 -0
  62. package/rules/js/jscpd_duplicates/concern.json +7 -0
  63. package/rules/js/jscpd_duplicates/docs/main.md +29 -0
  64. package/rules/js/jscpd_duplicates/main.mjs +69 -0
  65. package/rules/js/knip/concern.json +7 -0
  66. package/rules/js/knip/docs/main.md +31 -0
  67. package/rules/js/knip/knip.mdc +15 -0
  68. package/rules/js/knip/main.mjs +89 -0
  69. package/rules/js/lint-findings/concern.json +3 -0
  70. package/rules/js/lint-findings/docs/main.md +40 -0
  71. package/rules/js/lint-findings/main.mjs +125 -0
  72. package/rules/js/main.json +1 -0
  73. package/rules/js/main.mdc +18 -0
  74. package/rules/js/package_json/concern.json +9 -0
  75. package/rules/js/package_json/docs/fix-package_json.md +25 -0
  76. package/rules/js/package_json/docs/index.md +9 -0
  77. package/rules/js/package_json/fix-package_json.mjs +3 -0
  78. package/rules/js/package_json/package_json.mdc +15 -0
  79. package/rules/js/package_json/package_json.rego +142 -0
  80. package/rules/js/package_json/template/package.json.snippet.json +6 -0
  81. package/rules/js/tooling/concern.json +3 -0
  82. package/rules/js/tooling/data/tooling/knip-canonical.json +30 -0
  83. package/rules/js/tooling/data/tooling/oxlint-canonical.json +400 -0
  84. package/rules/js/tooling/docs/main.md +53 -0
  85. package/rules/js/tooling/main.mjs +183 -0
  86. package/rules/js/utils_imports/concern.json +4 -0
  87. package/rules/js/utils_imports/docs/main.md +50 -0
  88. package/rules/js/utils_imports/main.mjs +185 -0
  89. package/rules/js/utils_imports/utils_imports.mdc +15 -0
  90. package/rules/js/vscode_extensions/concern.json +11 -0
  91. package/rules/js/vscode_extensions/docs/fix-vscode_extensions.md +24 -0
  92. package/rules/js/vscode_extensions/docs/index.md +11 -0
  93. package/rules/js/vscode_extensions/fix-vscode_extensions.mjs +1 -0
  94. package/rules/js/vscode_extensions/template/extensions.json.snippet.json +6 -0
  95. package/rules/js/vscode_extensions/vscode_extensions.mdc +11 -0
  96. package/rules/js/vscode_extensions/vscode_extensions.rego +12 -0
  97. package/rules/js-bun-db/connection/concern.json +3 -0
  98. package/rules/js-bun-db/connection/connection.mdc +42 -0
  99. package/rules/js-bun-db/docs/index.md +11 -0
  100. package/rules/js-bun-db/lib/bun-sql-scan.mjs +1047 -0
  101. package/rules/js-bun-db/lib/docs/bun-sql-scan.md +63 -0
  102. package/rules/js-bun-db/lib/docs/index.md +11 -0
  103. package/rules/js-bun-db/main.json +1 -0
  104. package/rules/js-bun-db/main.mdc +8 -0
  105. package/rules/js-bun-db/package_json/concern.json +9 -0
  106. package/rules/js-bun-db/package_json/package_json.mdc +31 -0
  107. package/rules/js-bun-db/package_json/package_json.rego +15 -0
  108. package/rules/js-bun-db/package_json/template/package.json.deny.json +6 -0
  109. package/rules/js-bun-db/pg_format_identifiers/concern.json +3 -0
  110. package/rules/js-bun-db/pg_format_identifiers/pg_format_identifiers.mdc +104 -0
  111. package/rules/js-bun-db/safety/concern.json +4 -0
  112. package/rules/js-bun-db/safety/docs/main.md +34 -0
  113. package/rules/js-bun-db/safety/main.mjs +430 -0
  114. package/rules/js-bun-db/safety/safety.mdc +458 -0
  115. package/rules/js-bun-redis/docs/index.md +11 -0
  116. package/rules/js-bun-redis/imports/concern.json +4 -0
  117. package/rules/js-bun-redis/imports/docs/main.md +36 -0
  118. package/rules/js-bun-redis/imports/imports.mdc +47 -0
  119. package/rules/js-bun-redis/imports/main.mjs +88 -0
  120. package/rules/js-bun-redis/lib/docs/index.md +11 -0
  121. package/rules/js-bun-redis/lib/docs/redis-imports.md +227 -0
  122. package/rules/js-bun-redis/lib/redis-imports.mjs +130 -0
  123. package/rules/js-bun-redis/main.json +1 -0
  124. package/rules/js-bun-redis/main.mdc +8 -0
  125. package/rules/js-bun-redis/package_json/concern.json +9 -0
  126. package/rules/js-bun-redis/package_json/package_json.mdc +11 -0
  127. package/rules/js-bun-redis/package_json/package_json.rego +15 -0
  128. package/rules/js-bun-redis/package_json/template/package.json.deny.json +12 -0
  129. package/rules/js-mssql/deps/concern.json +4 -0
  130. package/rules/js-mssql/deps/docs/main.md +33 -0
  131. package/rules/js-mssql/deps/main.mjs +297 -0
  132. package/rules/js-mssql/docs/index.md +11 -0
  133. package/rules/js-mssql/lib/docs/index.md +11 -0
  134. package/rules/js-mssql/lib/docs/mssql-pool-scan.md +380 -0
  135. package/rules/js-mssql/lib/mssql-pool-scan.mjs +610 -0
  136. package/rules/js-mssql/main.json +1 -0
  137. package/rules/js-mssql/main.mdc +144 -0
  138. package/rules/js-mssql/mssql-tvp/concern.json +3 -0
  139. package/rules/js-mssql/mssql-tvp/mssql-tvp.mdc +77 -0
  140. package/rules/js-mssql/package_json/concern.json +9 -0
  141. package/rules/js-mssql/package_json/package_json.mdc +9 -0
  142. package/rules/js-mssql/package_json/package_json.rego +57 -0
  143. package/rules/js-run/configmap/concern.json +9 -0
  144. package/rules/js-run/configmap/configmap.mdc +37 -0
  145. package/rules/js-run/configmap/configmap.rego +21 -0
  146. package/rules/js-run/configmap/template/configmap.yaml.contains.yml +4 -0
  147. package/rules/js-run/docs/index.md +11 -0
  148. package/rules/js-run/jsconfig/concern.json +9 -0
  149. package/rules/js-run/jsconfig/docs/fix-jsconfig.md +28 -0
  150. package/rules/js-run/jsconfig/docs/index.md +9 -0
  151. package/rules/js-run/jsconfig/fix-jsconfig.mjs +119 -0
  152. package/rules/js-run/jsconfig/jsconfig.mdc +48 -0
  153. package/rules/js-run/jsconfig/jsconfig.rego +59 -0
  154. package/rules/js-run/jsconfig/template/jsconfig.json.snippet.json +10 -0
  155. package/rules/js-run/lib/bunyan-imports.mjs +98 -0
  156. package/rules/js-run/lib/check-env-scan.mjs +338 -0
  157. package/rules/js-run/lib/conn-file-rules.mjs +214 -0
  158. package/rules/js-run/lib/conn-imports-scan.mjs +154 -0
  159. package/rules/js-run/lib/docs/bunyan-imports.md +121 -0
  160. package/rules/js-run/lib/docs/check-env-scan.md +438 -0
  161. package/rules/js-run/lib/docs/conn-file-rules.md +304 -0
  162. package/rules/js-run/lib/docs/conn-imports-scan.md +208 -0
  163. package/rules/js-run/lib/docs/index.md +16 -0
  164. package/rules/js-run/lib/docs/promise-settimeout-scan.md +334 -0
  165. package/rules/js-run/lib/docs/temporal-scan.md +29 -0
  166. package/rules/js-run/lib/promise-settimeout-scan.mjs +128 -0
  167. package/rules/js-run/lib/temporal-scan.mjs +52 -0
  168. package/rules/js-run/main.json +1 -0
  169. package/rules/js-run/main.mdc +16 -0
  170. package/rules/js-run/package_json/concern.json +9 -0
  171. package/rules/js-run/package_json/package_json.mdc +44 -0
  172. package/rules/js-run/package_json/package_json.rego +37 -0
  173. package/rules/js-run/package_json/template/package.json.deny.json +22 -0
  174. package/rules/js-run/project-structure/concern.json +3 -0
  175. package/rules/js-run/project-structure/project-structure.mdc +11 -0
  176. package/rules/js-run/runtime/concern.json +12 -0
  177. package/rules/js-run/runtime/docs/fix-runtime.md +27 -0
  178. package/rules/js-run/runtime/docs/main.md +35 -0
  179. package/rules/js-run/runtime/fix-runtime.mjs +46 -0
  180. package/rules/js-run/runtime/main.mjs +496 -0
  181. package/rules/js-run/runtime/runtime.mdc +184 -0
  182. package/rules/js-run/scope/concern.json +3 -0
  183. package/rules/js-run/scope/scope.mdc +11 -0
  184. package/rules/npm-module/docs/index.md +11 -0
  185. package/rules/npm-module/emit_types_config/concern.json +9 -0
  186. package/rules/npm-module/emit_types_config/docs/fix-emit_types_config.md +25 -0
  187. package/rules/npm-module/emit_types_config/docs/index.md +9 -0
  188. package/rules/npm-module/emit_types_config/emit_types_config.mdc +43 -0
  189. package/rules/npm-module/emit_types_config/emit_types_config.rego +28 -0
  190. package/rules/npm-module/emit_types_config/fix-emit_types_config.mjs +5 -0
  191. package/rules/npm-module/emit_types_config/template/tsconfig.emit-types.json.snippet.json +9 -0
  192. package/rules/npm-module/header_doc_pointer/concern.json +5 -0
  193. package/rules/npm-module/header_doc_pointer/docs/main.md +40 -0
  194. package/rules/npm-module/header_doc_pointer/header_doc_pointer.mdc +18 -0
  195. package/rules/npm-module/header_doc_pointer/main.mjs +131 -0
  196. package/rules/npm-module/main.json +1 -0
  197. package/rules/npm-module/main.mdc +36 -0
  198. package/rules/npm-module/npm_package_json/concern.json +9 -0
  199. package/rules/npm-module/npm_package_json/docs/fix-npm_package_json.md +26 -0
  200. package/rules/npm-module/npm_package_json/docs/index.md +9 -0
  201. package/rules/npm-module/npm_package_json/fix-npm_package_json.mjs +5 -0
  202. package/rules/npm-module/npm_package_json/npm_package_json.mdc +57 -0
  203. package/rules/npm-module/npm_package_json/npm_package_json.rego +73 -0
  204. package/rules/npm-module/npm_package_json/template/package.json.snippet.json +1 -0
  205. package/rules/npm-module/package_structure/concern.json +5 -0
  206. package/rules/npm-module/package_structure/docs/main.md +37 -0
  207. package/rules/npm-module/package_structure/main.mjs +448 -0
  208. package/rules/npm-module/package_structure/package_structure.mdc +63 -0
  209. package/rules/npm-module/root_package_json/concern.json +9 -0
  210. package/rules/npm-module/root_package_json/docs/fix-root_package_json.md +25 -0
  211. package/rules/npm-module/root_package_json/docs/index.md +9 -0
  212. package/rules/npm-module/root_package_json/fix-root_package_json.mjs +5 -0
  213. package/rules/npm-module/root_package_json/root_package_json.mdc +41 -0
  214. package/rules/npm-module/root_package_json/root_package_json.rego +28 -0
  215. package/rules/npm-module/root_package_json/template/package.json.snippet.json +1 -0
  216. package/rules/npm-module/rule_meta/concern.json +8 -0
  217. package/rules/npm-module/rule_meta/docs/main.md +35 -0
  218. package/rules/npm-module/rule_meta/main.mjs +119 -0
  219. package/rules/npm-module/rule_meta/rule_meta.mdc +11 -0
  220. package/rules/npm-module/skill_meta/concern.json +5 -0
  221. package/rules/npm-module/skill_meta/docs/main.md +149 -0
  222. package/rules/npm-module/skill_meta/main.mjs +91 -0
  223. package/rules/npm-module/skill_meta/skill_meta.mdc +11 -0
  224. package/rules/tool-surface/docs/index.md +11 -0
  225. package/rules/tool-surface/main.json +6 -0
  226. package/rules/tool-surface/main.mdc +72 -0
  227. package/rules/vue/composition-api/composition-api.mdc +82 -0
  228. package/rules/vue/composition-api/concern.json +3 -0
  229. package/rules/vue/docs/index.md +11 -0
  230. package/rules/vue/lib/docs/index.md +11 -0
  231. package/rules/vue/lib/docs/vue-forbidden-imports.md +265 -0
  232. package/rules/vue/lib/vue-forbidden-imports.mjs +240 -0
  233. package/rules/vue/main.json +1 -0
  234. package/rules/vue/main.mdc +18 -0
  235. package/rules/vue/nheader-layout/concern.json +3 -0
  236. package/rules/vue/nheader-layout/nheader-layout.mdc +171 -0
  237. package/rules/vue/package_json/concern.json +9 -0
  238. package/rules/vue/package_json/package_json.mdc +30 -0
  239. package/rules/vue/package_json/package_json.rego +140 -0
  240. package/rules/vue/packages/concern.json +6 -0
  241. package/rules/vue/packages/docs/index.md +11 -0
  242. package/rules/vue/packages/docs/main.md +35 -0
  243. package/rules/vue/packages/main.mjs +575 -0
  244. package/rules/vue/packages/packages.mdc +56 -0
  245. package/rules/vue/quasar-ui/concern.json +3 -0
  246. package/rules/vue/quasar-ui/quasar-ui.mdc +32 -0
  247. package/rules/vue/structure/concern.json +3 -0
  248. package/rules/vue/structure/structure.mdc +101 -0
  249. package/rules/vue/testing/concern.json +3 -0
  250. package/rules/vue/testing/testing.mdc +40 -0
  251. package/rules/vue/tfm-translations/concern.json +7 -0
  252. package/rules/vue/tfm-translations/docs/main.md +29 -0
  253. package/rules/vue/tfm-translations/main.mjs +55 -0
  254. package/rules/vue/tfm-translations/tfm-translations.mdc +32 -0
  255. package/rules/vue/vite-config/concern.json +3 -0
  256. package/rules/vue/vite-config/vite-config.mdc +153 -0
  257. package/rules/vue/vite-env/concern.json +3 -0
  258. package/rules/vue/vite-env/vite-env.mdc +61 -0
@@ -0,0 +1,115 @@
1
+ /**
2
+ * lint-поверхня js/eslint: read-only detector (oxlint + eslint). Fix (oxlint --fix /
3
+ * eslint --fix) — окремий T0 `fix-eslint.mjs` (детермінований), не в detector-і.
4
+ */
5
+ import { resolve, relative } from 'node:path'
6
+
7
+ import { ESLint } from 'eslint'
8
+
9
+ import { addedLinesByFile } from '@7n/rules/scripts/lib/diff-added-lines.mjs'
10
+ import { spawnAsync } from '@7n/rules/scripts/utils/spawn-async.mjs'
11
+ import { WORKTREE_CHECKOUT_GLOBS } from '@7n/rules/scripts/utils/walkDir.mjs'
12
+ import { classifyFindings, eslintResultsToFindings, parseOxlint } from '../lint-findings/main.mjs'
13
+
14
+ const JS_EXT_RE = /\.(?:mjs|cjs|js|jsx|ts|tsx|vue)$/u
15
+
16
+ /**
17
+ * @param {string[]} files список шляхів
18
+ * @returns {string[]} лише JS-подібні файли
19
+ */
20
+ export function filterJsFiles(files) {
21
+ return files.filter(f => JS_EXT_RE.test(f))
22
+ }
23
+
24
+ /**
25
+ * Async (не блокує event loop) — детектор може виконуватись у parallel lane `detectAll()`
26
+ * (ADR 260716-1354).
27
+ * @param {string[]} args аргументи запуску oxlint
28
+ * @param {string} cwd робочий каталог
29
+ * @returns {Promise<{ status: number, stdout: string, stderr: string }>} код завершення, stdout і stderr процесу
30
+ */
31
+ async function runOxlintJson(args, cwd) {
32
+ const r = await spawnAsync('bunx', args, { cwd })
33
+ return { status: typeof r.exitCode === 'number' ? r.exitCode : 1, stdout: r.stdout ?? '', stderr: r.stderr ?? '' }
34
+ }
35
+
36
+ /**
37
+ * Finding → LintViolation.
38
+ * @param {{ file: string, line: number, rule: string, message: string, tool: string }} f знахідка лінтера
39
+ * @param {string} cwd робочий каталог
40
+ * @param {'error'|'warn'} severity рівень порушення
41
+ * @returns {import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintViolation} нормалізоване порушення
42
+ */
43
+ function toViolation(f, cwd, severity) {
44
+ return /** @type {import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintViolation} */ ({
45
+ reason: f.rule || `${f.tool}-error`,
46
+ message: `${f.message} (${f.tool})`,
47
+ file: relative(cwd, f.file).split('\\').join('/'),
48
+ severity,
49
+ data: { line: f.line, tool: f.tool }
50
+ })
51
+ }
52
+
53
+ /**
54
+ * Збирає findings (oxlint json + eslint read-only) по заданих файлах або всьому проєкту.
55
+ * @param {string[]|null} js null → весь проєкт
56
+ * @param {string} cwd робочий каталог
57
+ * @returns {Promise<Array<{ file: string, line: number, rule: string, message: string, tool: string }>>} зібрані знахідки oxlint+eslint
58
+ */
59
+ async function collectFindings(js, cwd) {
60
+ // warnIgnored:false — файли з delta, що матчать ignore-патерни конфігу, не є порушеннями.
61
+ // overrideConfig лише з ignores → global ignores: worktree-чекаути (.worktrees/,
62
+ // .claude/worktrees/) — копії репо, споживацький eslint-конфіг їх не виключає.
63
+ const eslint = new ESLint({ cwd, warnIgnored: false, overrideConfig: { ignores: WORKTREE_CHECKOUT_GLOBS } })
64
+ const esResults = await eslint.lintFiles(js === null ? [cwd] : js.map(f => resolve(cwd, f)))
65
+ const es = eslintResultsToFindings(esResults)
66
+
67
+ const worktreeIgnoreArgs = WORKTREE_CHECKOUT_GLOBS.map(g => `--ignore-pattern=${g}`)
68
+ const oxArgs =
69
+ js === null
70
+ ? ['oxlint', '--format=json', ...worktreeIgnoreArgs]
71
+ : ['oxlint', '--format=json', ...worktreeIgnoreArgs, ...js]
72
+ const oxRes = await runOxlintJson(oxArgs, cwd)
73
+ const ox = parseOxlint(oxRes.stdout)
74
+ if (ox === null && oxRes.status !== 0) {
75
+ // Хвости stdout/stderr — інакше на CI причина крашу (OOM, конфіг, версія) невидима.
76
+ const tail = [oxRes.stderr, oxRes.stdout]
77
+ .map(t => t.trim().slice(-500))
78
+ .filter(t => t !== '')
79
+ .join('\n--- stdout: ')
80
+ const suffix = tail === '' ? '' : `\n${tail}`
81
+ throw new Error(
82
+ `oxlint завершився з помилкою (exit ${oxRes.status}, не lint-порушення) — json не розпарсено${suffix}`
83
+ )
84
+ }
85
+ return [...(ox ?? []), ...es]
86
+ }
87
+
88
+ /**
89
+ * Detector js/eslint: per-file (classify introduced/pre-existing) або full-project.
90
+ * @param {import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст лінту
91
+ * @returns {Promise<import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintResult>} перелік порушень
92
+ */
93
+ export async function lint(ctx) {
94
+ const { cwd, files } = ctx
95
+
96
+ if (files === undefined) {
97
+ const findings = await collectFindings(null, cwd)
98
+ return { violations: findings.map(f => toViolation(f, cwd, 'error')) }
99
+ }
100
+
101
+ const js = filterJsFiles(files)
102
+ if (js.length === 0) return { violations: [] }
103
+
104
+ const findings = await collectFindings(js, cwd)
105
+ if (findings.length === 0) return { violations: [] }
106
+
107
+ // introduced (на доданих рядках) → error; pre-existing → warn.
108
+ const classified = classifyFindings(findings, addedLinesByFile(js, cwd), cwd)
109
+ return {
110
+ violations: [
111
+ ...classified.introduced.map(f => toViolation(f, cwd, 'error')),
112
+ ...classified.preExisting.map(f => toViolation(f, cwd, 'warn'))
113
+ ]
114
+ }
115
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json"
3
+ }
@@ -0,0 +1,12 @@
1
+ ## Розширення нових файлів — `.mjs` / `.cjs`, не `.js`
2
+
3
+ **Нові** JS-файли створюй з явним розширенням модуля:
4
+
5
+ - **`.mjs`** — для ESM (типовий випадок);
6
+ - **`.cjs`** — для CommonJS, де він справді потрібен.
7
+
8
+ Голий **`.js`** для нового файлу **заборонено**. Розширення `.js` інтерпретується як ESM чи CJS лише за полем `package.json#type`, тож той самий файл читається по-різному залежно від пакета. Явне `.mjs`/`.cjs` робить тип модуля однозначним **без читання `package.json`** — навіть якщо `type` зміниться або файл перемістять в інший пакет. Це доповнює вимогу `"type": "module"` у [package_json.mdc](../package_json/package_json.mdc): `type` лишається каноном для всього дерева, а розширення нового файлу прибирає залежність від нього.
9
+
10
+ Стосується **backend і frontend** — будь-який новий вихідний файл: `src/`, тести `*.test.*`, `scripts/`, `src/conn/` тощо.
11
+
12
+ **Існуючі `.js` лишаються як є** — масово перейменовувати не треба; це конвенція для нового коду. Автоматичної перевірки тут немає: stateless-скан не відрізнить новий файл від існуючого, тож `.js` нікого не фейлить.
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
+ "fixability": "config",
4
+ "policy": {
5
+ "files": {
6
+ "single": ".jscpd.json",
7
+ "required": true
8
+ },
9
+ "missingMessage": ".jscpd.json не існує — створи з полями згідно js.mdc"
10
+ }
11
+ }
@@ -0,0 +1,25 @@
1
+ ---
2
+ type: JS Module
3
+ title: fix-jscpd_config.mjs
4
+ resource: plugins/lang-js/rules/js/jscpd_config/fix-jscpd_config.mjs
5
+ docgen:
6
+ crc: 9c058fae
7
+ model: openai-codex/gpt-5.4-mini
8
+ score: 100
9
+ judgeModel: openai-codex/gpt-5.4-mini
10
+ ---
11
+
12
+ ## Огляд
13
+
14
+ `patterns` визначає узгоджені з правилами проєкту зміни для `.jscpd.json` і слугує централізованим орієнтиром для того, що саме має бути виправлено в цьому конфігу.
15
+
16
+ ## Поведінка
17
+
18
+ 1. `patterns` формує перелік правил для уніфікації шаблонного конфіга проєкту.
19
+ 2. Кожне правило орієнтується на конфіг `.jscpd.json`, щоб тримати його в узгодженому, очікуваному форматі.
20
+ 3. `patterns` потрібен для централізованого опису того, що саме має бути виправлено в `.jscpd.json`, без ручного втручання в інші файли чи артефакти.
21
+ 4. `patterns` не змінює файлову систему або базу даних і працює лише як опис набору правок для цього конфіга.
22
+
23
+ ## Гарантії поведінки
24
+
25
+ - Read-only: не виконує операцій запису (ФС/БД).
@@ -0,0 +1,9 @@
1
+ ---
2
+ type: Directory Index
3
+ title: npm/rules/js/jscpd_config
4
+ resource: plugins/lang-js/rules/js/jscpd_config/
5
+ ---
6
+
7
+ | Файл | Тип |
8
+ | ------------------------------------------- | --------- |
9
+ | [fix-jscpd_config.mjs](fix-jscpd_config.md) | JS Module |
@@ -0,0 +1,3 @@
1
+ import { createTemplateFixPattern } from '@7n/rules/scripts/lib/fix/template-deep-merge.mjs'
2
+
3
+ export const patterns = [createTemplateFixPattern({ id: 'js-jscpd_config-template', targetPath: '.jscpd.json' })]
@@ -0,0 +1,42 @@
1
+ ## Rego-gate `.jscpd.json`
2
+
3
+ Rego-пакет: `js_lint.jscpd`
4
+
5
+ Цільовий файл: `.jscpd.json` у корені проєкту.
6
+
7
+ Що перевіряється:
8
+
9
+ - `gitignore` — точна відповідність (`true`)
10
+ - `exitCode` — точна відповідність (`1`)
11
+ - `reporters` — subset-of: масив має містити всі елементи з канону (`["console"]`)
12
+ - `minLines` — число, значення >= канонічного порогу (`25`); більше дозволено
13
+
14
+ Канон-snippet: [.jscpd.json.snippet.json](./template/.jscpd.json.snippet.json)
15
+
16
+ ### Ігнорування `.claude/worktrees/`
17
+
18
+ Каталог `.claude/worktrees/` (робочі копії, які Claude Code створює через **superpowers:using-git-worktrees**) має ігноруватися:
19
+
20
+ - додай `.claude/worktrees/` у кореневий `.gitignore` (штатне місце для не-комітних робочих копій);
21
+ - у `.jscpd.json` додай `.claude/worktrees/**` у `ignore` як страховку на випадок запуску без `gitignore: true`.
22
+
23
+ Без цього jscpd сканує паралельну копію репо в worktree і фіксує самозбіги між дзеркальними файлами.
24
+
25
+ ```text title=".gitignore (фрагмент)"
26
+ .claude/worktrees/
27
+ ```
28
+
29
+ ### Ігнорування `**/CHANGELOG.md`
30
+
31
+ `**/CHANGELOG.md` у каноні `ignore`: release-журнали різних пакетів структурно повторюються (заголовки `## [x.y.z] - YYYY-MM-DD`, секції `### Added / Changed / Fixed` за Keep a Changelog), і `jscpd` за `minLines: 25` фіксує їх як клон — це false positive. Без цього в монорепо легко зловити критичний `bun run lint` на парі CHANGELOG-ів довжиною від ~25 рядків.
32
+
33
+ ### Рефакторинг vs конфіг
34
+
35
+ Коли **jscpd** знаходить клони, спочатку зменшуй дублювання кодом, а не конфігом.
36
+
37
+ - **Рефакторинг:** винеси спільні фрагменти в функції, модулі, утиліти, composables/hooks, спільні компоненти або базові типи — залежно від контексту.
38
+ - **Структура:** якщо одна й та сама логіка розмазана між файлами чи пакетами, запропонуй зміну структури (наприклад, спільний модуль, `shared/`, внутрішній пакет у monorepo), щоб була **одна канонічна реалізація** і повторні місця лише викликали її.
39
+
40
+ Розширення `ignore` чи завищення `minLines` лише щоб прибрати звіт — не заміна рефакторингу для справжніх клонів. Якщо збіг **семантично випадковий** (генерований код, формальні шаблони без спільної логіки), після оцінки допустимо точковий `ignore` або зміна порогу — з коротким обґрунтуванням.
41
+
42
+ Перед рефакторингом перевір чи є тести на блоки які підлягають зміні (Bun.test для js, playwright для vue). Якщо тестів немає або вони не покривають — спочатку створи їх, перевір що вони відпрацьовують коректно, потім роби рефакторинг і ще раз запускай тести.
@@ -0,0 +1,44 @@
1
+ # Перевірка `.jscpd.json` (js.mdc).
2
+ #
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/.jscpd.json.snippet.json.
5
+ # Особливості:
6
+ # - `reporters` — subset-of (масив string)
7
+ # - `minLines` — >= expected (semantic: дозволяється більше, не менше)
8
+ # - `gitignore`/`exitCode` — exact match
9
+ package js.jscpd_config
10
+
11
+ import rego.v1
12
+
13
+ # Top-level scalar leafs (exact match) для gitignore + exitCode.
14
+ deny contains msg if {
15
+ some key, expected_value in data.template.snippet
16
+ key != "minLines"
17
+ not is_array(expected_value)
18
+ actual := object.get(input, key, null)
19
+ actual != expected_value
20
+ msg := sprintf(".jscpd.json має містити \"%s\": %v (js.mdc)", [key, expected_value])
21
+ }
22
+
23
+ # Array subset-of для reporters.
24
+ deny contains msg if {
25
+ some field, expected_values in data.template.snippet
26
+ is_array(expected_values)
27
+ actual_set := {v | some v in object.get(input, field, [])}
28
+ some required in expected_values
29
+ not required in actual_set
30
+ msg := sprintf(".jscpd.json має містити \"%s\": [\"%s\"] (js.mdc)", [field, required])
31
+ }
32
+
33
+ # minLines: must be number and >= expected.
34
+ deny contains msg if {
35
+ expected := data.template.snippet.minLines
36
+ actual := object.get(input, "minLines", null)
37
+ not is_valid_min_lines(actual, expected)
38
+ msg := sprintf(".jscpd.json має містити \"minLines\" як число >= %d (js.mdc)", [expected])
39
+ }
40
+
41
+ is_valid_min_lines(actual, expected) if {
42
+ is_number(actual)
43
+ actual >= expected
44
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "gitignore": true,
3
+ "exitCode": 1,
4
+ "reporters": ["console"],
5
+ "minLines": 25,
6
+ "ignore": [".claude/worktrees/**", "**/dist/**", "**/CHANGELOG.md"]
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
+ "fixability": "structural",
4
+ "lint": {
5
+ "scope": "full"
6
+ }
7
+ }
@@ -0,0 +1,29 @@
1
+ ---
2
+ type: JS Module
3
+ title: main.mjs
4
+ resource: plugins/lang-js/rules/js/jscpd_duplicates/main.mjs
5
+ docgen:
6
+ crc: 0a9e3d15
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
9
+ issues: judge:inaccurate:0.99
10
+ judgeModel: openai-codex/gpt-5.4-mini
11
+ ---
12
+
13
+ ## Огляд
14
+
15
+ Публічна функція `lint` аналізує вихідний код у вказаному кореневому каталозі, використовуючи `jscpd`, щоб виявляти дублікати та інші порушення стилю і логіки коду. Функція повертає код виходу, який інформує про успішне завершення аналізу чи виявлення проблем.
16
+
17
+ ## Поведінка
18
+
19
+ 1. Викликається функція `lint`, яка ініціює перевірку коду у вказаному кореневому каталозі з метою виявлення дублікатів.
20
+ 2. Процес аналізу виконується за допомогою `jscpd` у середовищі `bunx`.
21
+ 3. Функція повертає код завершення процесу: 0 у разі успішного виконання (без знайдених проблем) або інше число, що вказує на помилки.
22
+
23
+ ## Публічний API
24
+
25
+ lint — виявляє дублікати коду за допомогою `bunx jscpd` у файлах JavaScript/JSCPD.
26
+
27
+ ## Гарантії поведінки
28
+
29
+ - Read-only: не виконує операцій запису (ФС/БД).
@@ -0,0 +1,69 @@
1
+ /**
2
+ * lint-поверхня js/jscpd_duplicates: read-only detector дублікатів коду (`jscpd`).
3
+ * Кожен клон → одне порушення (anchored на `firstFile`), із посиланням на `secondFile`
4
+ * у `data`. JSON-звіт пишеться у системний tmp (поза репо) — дерево не мутується.
5
+ */
6
+ import { mkdtempSync, readFileSync, rmSync } from 'node:fs'
7
+ import { tmpdir } from 'node:os'
8
+ import { join } from 'node:path'
9
+
10
+ import { spawnAsync } from '@7n/rules/scripts/utils/spawn-async.mjs'
11
+
12
+ /**
13
+ * @typedef {{ name: string, start: number, end: number }} JscpdFileRef
14
+ * @typedef {{ firstFile: JscpdFileRef, secondFile: JscpdFileRef, lines: number, format: string }} JscpdClone
15
+ */
16
+
17
+ /**
18
+ * Один клон → LintViolation.
19
+ * @param {JscpdClone} clone дубльований фрагмент
20
+ * @returns {import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintViolation} нормалізоване порушення
21
+ */
22
+ function cloneToViolation(clone) {
23
+ const a = clone.firstFile
24
+ const b = clone.secondFile
25
+ const aLoc = `${a.name}:${a.start}-${a.end}`
26
+ const bLoc = `${b.name}:${b.start}-${b.end}`
27
+ return /** @type {import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintViolation} */ ({
28
+ reason: 'duplicate-clone',
29
+ message: `jscpd: дубльований фрагмент (${clone.lines} рядків, ${clone.format}) ${aLoc} ↔ ${bLoc}`,
30
+ file: a.name,
31
+ data: {
32
+ line: a.start,
33
+ lines: clone.lines,
34
+ format: clone.format,
35
+ first: { file: a.name, start: a.start, end: a.end },
36
+ second: { file: b.name, start: b.start, end: b.end }
37
+ }
38
+ })
39
+ }
40
+
41
+ /**
42
+ * Detector js/jscpd_duplicates: дублікати коду через `jscpd` (read-only). Async (не блокує
43
+ * event loop) — детектор може виконуватись у parallel lane `detectAll()` (ADR 260716-1354).
44
+ * @param {import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст лінту
45
+ * @returns {Promise<import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintResult>} перелік порушень
46
+ */
47
+ export async function lint(ctx) {
48
+ const cwd = ctx.cwd
49
+ const outDir = mkdtempSync(join(tmpdir(), 'jscpd-'))
50
+ try {
51
+ const r = await spawnAsync('bunx', ['jscpd', '.', '--reporters', 'json', '--output', outDir, '--silent'], { cwd })
52
+ let report
53
+ try {
54
+ report = JSON.parse(readFileSync(join(outDir, 'jscpd-report.json'), 'utf8'))
55
+ } catch {
56
+ // jscpd не зміг сформувати звіт (інструмент відсутній / краш) — пропускаємо
57
+ const detail = `${r.stdout ?? ''}${r.stderr ?? ''}`.trim().slice(0, 500)
58
+ const suffix = detail ? `: ${detail}` : ''
59
+ return {
60
+ violations: [],
61
+ diagnostics: [{ level: 'warn', message: `jscpd: не вдалося прочитати JSON-звіт${suffix}` }]
62
+ }
63
+ }
64
+ const clones = Array.isArray(report.duplicates) ? report.duplicates : []
65
+ return { violations: clones.map(clone => cloneToViolation(clone)) }
66
+ } finally {
67
+ rmSync(outDir, { recursive: true, force: true })
68
+ }
69
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
+ "fixability": "structural",
4
+ "lint": {
5
+ "scope": "full"
6
+ }
7
+ }
@@ -0,0 +1,31 @@
1
+ ---
2
+ type: JS Module
3
+ title: main.mjs
4
+ resource: plugins/lang-js/rules/js/knip/main.mjs
5
+ docgen:
6
+ crc: e46cd90b
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
9
+ issues: judge:inaccurate:0.98
10
+ judgeModel: openai-codex/gpt-5.4-mini
11
+ ---
12
+
13
+ ## Огляд
14
+
15
+ Lint-поверхня `js/knip`: read-only detector невикористаних залежностей, експортів і файлів через programmatic API knip. Кожен knip-issue стає одним порушенням (`reason` = тип issue, `file`/`line` де доступні); звіт рендерить runner, сам детектор нічого не друкує і не мутує (без `--fix`).
16
+
17
+ ## Поведінка
18
+
19
+ - `lint(ctx)` будує опції knip (`createOptions` з dist-каталогу пакета — внутрішній util не експортується через `exports`), глушить TTY-прогрес поза `ctx.verbose` і жене `knipMain`.
20
+ - Кожен issue з `results.issues` нормалізується у violation: `message` = `knip: <тип> \`<символ>\` — <файл:рядок>`, `severity` warn/error за knip.
21
+ - Вбудований ігнор пакетів екосистеми n-rules: unused dependency/devDependency-issue на `@7n/rules` та `@7n/rules-*` (lang-/ci-плагіни) відкидається — їх ставить і веде сам `npx @7n/rules`, код споживача їх не імпортує, тож у кожному consumer-репо це було б хибне спрацювання. Інші типи issue (files, exports) на ці пакети НЕ ігноруються.
22
+
23
+ ## Публічний API
24
+
25
+ - lint — детектор js/knip: повертає `{ violations }` з нормалізованими knip-issue.
26
+ - isNRulesPackageIssue — чи є issue хибним спрацюванням на пакет екосистеми n-rules (експортовано для тестів).
27
+
28
+ ## Гарантії поведінки
29
+
30
+ - Read-only: knip запускається без `--fix`, жодних мутацій ФС/БД.
31
+ - Не друкує звіт — рендеринг на боці runner-а.
@@ -0,0 +1,15 @@
1
+ ## `knip.json` — аналіз невикористаних залежностей та експортів
2
+
3
+ Залежнісний аналіз (knip — невикористані залежності/експорти, `knip.json` канон) крос-файловий; запускається як частина `npx @7n/rules lint js`.
4
+
5
+ У корені проєкту має бути **`knip.json`**, який стартує з канонічного baseline з пакета `@7n/rules` — файл [`knip-canonical.json`](../tooling/data/tooling/knip-canonical.json). Канон покриває типові false-positives для наших правил:
6
+
7
+ - `entry` зі CLI-конфігами (eslint, stylelint, oxlint, jscpd, markdownlint-cli2, `commitlint`);
8
+ - `project` для `**/*.{js,mjs,cjs,jsx,ts,tsx,mts,cts}`;
9
+ - `ignore` для `**/__fixtures__/**`;
10
+ - `ignoreDependencies` для пакетів, посилання на які є лише в не-JS-конфігах (`@nitra/cspell-dict`, `/@cspell\/dict-.+/`, `graphql`);
11
+ - `ignoreBinaries` для CLI, які канон вимагає викликати через `npx`/`bunx` і яких заборонено додавати в `devDependencies` (`actionlint`, `cspell`, `eslint`, `git-ai`, `jscpd`, `markdownlint-cli2`, `oxfmt`, `oxlint`, `shellcheck`, `uvx`, `v8r`, `zizmor`).
12
+
13
+ Якщо `knip.json` відсутній — `npx @7n/rules lint js` (концерн `js/check`) копіює канон у корінь проєкту як side effect детектора, ще до фази LLM-фіксів. Після створення модифікуй файл під свій проєкт як завгодно: перевіряємо лише наявність, зміст подальших змін не валідується.
14
+
15
+ Пакет `knip` окремо в `devDependencies` не додавай — `bunx knip` тягне його ad-hoc, як oxlint/eslint/jscpd.
@@ -0,0 +1,89 @@
1
+ /**
2
+ * lint-поверхня js/knip: read-only detector невикористаних залежностей/експортів/файлів.
3
+ * Кожен knip-issue → одне порушення (reason = тип issue, file/line де доступні). Жодних
4
+ * мутацій (knip запускається без `--fix`) і жодного друку звіту — рендерить runner.
5
+ */
6
+ import { createRequire } from 'node:module'
7
+ import { dirname, join, relative } from 'node:path'
8
+ import { pathToFileURL } from 'node:url'
9
+
10
+ import { main as knipMain } from 'knip'
11
+
12
+ // Пакети екосистеми n-rules (ядро і lang-/ci-плагіни): їх ставить і веде сам
13
+ // `npx @7n/rules` як devDependency, код споживача їх не імпортує — knip завжди
14
+ // вважав би їх unused. Вбудований ігнор знімає хибне спрацювання у ВСІХ consumer-репо без
15
+ // правки їхніх knip.json (канон ignoreDependencies покриває лише свіжі копії).
16
+ const N_RULES_PKG_RE = /^@7n\/rules(-.+)?$/u
17
+
18
+ /**
19
+ * Чи є issue хибним спрацюванням на пакет екосистеми n-rules (unused dependency/devDependency).
20
+ * @param {{ type: string, symbol?: string }} issue knip-issue
21
+ * @returns {boolean} true — ігноруємо
22
+ */
23
+ export function isNRulesPackageIssue(issue) {
24
+ return (
25
+ (issue.type === 'dependencies' || issue.type === 'devDependencies') &&
26
+ typeof issue.symbol === 'string' &&
27
+ N_RULES_PKG_RE.test(issue.symbol)
28
+ )
29
+ }
30
+
31
+ /**
32
+ * Один knip-issue → LintViolation.
33
+ * @param {{ type: string, filePath?: string, symbol?: string, symbolType?: string, line?: number, col?: number, severity?: string }} issue knip-issue
34
+ * @param {string} cwd робочий каталог
35
+ * @returns {import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintViolation} нормалізоване порушення
36
+ */
37
+ function issueToViolation(issue, cwd) {
38
+ const abs = issue.filePath
39
+ const file = abs ? relative(cwd, abs).split('\\').join('/') : undefined
40
+ const lineSuffix = issue.line ? `:${issue.line}` : ''
41
+ const where = file ? `${file}${lineSuffix}` : '<unknown>'
42
+ const symbolType = issue.symbolType ? ` (${issue.symbolType})` : ''
43
+ const sym = issue.symbol ? ` \`${issue.symbol}\`${symbolType}` : ''
44
+ /** @type {Partial<import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintViolation>} */
45
+ const v = {
46
+ reason: issue.type || 'knip-issue',
47
+ message: `knip: ${issue.type}${sym} — ${where}`,
48
+ severity: issue.severity === 'warn' ? 'warn' : 'error',
49
+ data: { line: issue.line, col: issue.col, symbol: issue.symbol, type: issue.type }
50
+ }
51
+ if (file) v.file = file
52
+ return v
53
+ }
54
+
55
+ /**
56
+ * Detector js/knip: невикористані deps/exports/files через programmatic API knip (read-only).
57
+ * @param {import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст лінту
58
+ * @returns {Promise<import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintResult>} перелік порушень
59
+ */
60
+ export async function lint(ctx) {
61
+ const cwd = ctx.cwd
62
+
63
+ // knip-package не експортує внутрішні util-и через "exports" — резолвимо dist-каталог
64
+ // від головного entry і імпортуємо `create-options` за абсолютним file:// URL.
65
+ const require = createRequire(import.meta.url)
66
+ const distDir = dirname(require.resolve('knip'))
67
+ // eslint-disable-next-line no-unsanitized/method -- URL from resolved package path, not user input
68
+ const { createOptions } = await import(pathToFileURL(join(distDir, 'util/create-options.js')).href)
69
+
70
+ // knip вмикає власний прогрес-репортер ("Analyzing workspace …") автоматично в TTY,
71
+ // незалежно від наших verbose-прапорців — глушимо його поза `--verbose`, щоб не
72
+ // засмічувати вивід `lint --full`.
73
+ const options = await createOptions({ cwd, isDisableConfigHints: true, isShowProgress: ctx.verbose === true })
74
+ const results = await knipMain(options)
75
+
76
+ /** @type {import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintViolation[]} */
77
+ const violations = []
78
+ // results.issues: { <type>: { <file>: { <key>: Issue } } }
79
+ for (const byFile of Object.values(results.issues)) {
80
+ for (const byKey of Object.values(byFile)) {
81
+ for (const issue of Object.values(byKey)) {
82
+ const typed = /** @type {Parameters<typeof issueToViolation>[0]} */ (issue)
83
+ if (isNRulesPackageIssue(typed)) continue
84
+ violations.push(issueToViolation(typed, cwd))
85
+ }
86
+ }
87
+ }
88
+ return { violations }
89
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json"
3
+ }
@@ -0,0 +1,40 @@
1
+ ---
2
+ type: JS Module
3
+ title: main.mjs
4
+ resource: plugins/lang-js/rules/js/lint-findings/main.mjs
5
+ docgen:
6
+ crc: dc58119b
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
9
+ issues: judge:inaccurate:0.98
10
+ judgeModel: openai-codex/gpt-5.4-mini
11
+ ---
12
+
13
+ ## Огляд
14
+
15
+ Ось оновлений текст секції «overview» відповідно до ваших вимог:
16
+
17
+ Ця система надає можливості для аналізу логіки та виявлення помилок на основі результатів відмінних лінтерів. Доступні функції включають `parseOxlint` та `parseEslint` для парсингу результатів. Далі, `eslintResultsToFindings` перетворює ці результати у стандартизовані знахідки, які потім класифікуються за допомогою `classifyFindings`. Фінальне представлення цих знахідок здійснюється функцією `renderFindings`. Усі операції є read-only та не взаємодіють з файловою системою чи базою даних. Система працює з принципом fail-safe, тобто перехоплює можливі помилки і, замість викидання винятку, повертає порожнє значення, наприклад, `null`.
18
+
19
+ ## Поведінка
20
+
21
+ Поведінка:
22
+ parseOxlint: Парсить вивід `oxlint` у форматі JSON, повертаючи список знайдених порушень або `null` у разі непарсибельного JSON.
23
+ parseEslint: Парсить вивід `eslint` у форматі JSON, повертаючи список знайдених порушень або `null` у разі непарсибельного JSON.
24
+ eslintResultsToFindings: Конвертує результати, отримані з API `eslint.lintFiles`, у нормалізований список знайдених порушень.
25
+ classifyFindings: Класифікує знайдені порушення на "внесено цією зміною" або "борг файлу" на основі інформації про додані рядки у файлі.
26
+ renderFindings: Форматує класифіковані порушення у текстовий звіт, розділяючи внесені та існуючі проблеми.
27
+
28
+ ## Публічний API
29
+
30
+ parseOxlint — отримує результат парсингу Oxlint, повертаючи `null`, якщо вивід інструмента не може бути інтерпретований як JSON.
31
+ parseEslint — отримує результат парсингу Eslint, повертаючи `null`, якщо вивід інструмента не може бути інтерпретований як JSON.
32
+ eslintResultsToFindings — перетворює детальні звіти з ESLint у стандартизований формат знахідок.
33
+ classifyFindings — розрізняє знахідки, які виникли через додані рядки, від тих, що були присутні до змін.
34
+ renderFindings — створює зведений звіт, відображаючи нові знахідки (🆕 introduced) та існуючий технічний борг (🗄 pre-existing).
35
+
36
+ ## Гарантії поведінки
37
+
38
+ - Read-only: не виконує операцій запису (ФС/БД).
39
+ - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
40
+ - За певних помилок повертає порожнє значення (напр. `null`) замість винятку.