@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.
- package/CHANGELOG.md +12 -0
- package/package.json +1 -5
- package/schemas/v8r-catalog.json +10 -1
- package/scripts/auto-rules.mjs +2 -3
- package/scripts/docs/auto-rules.md +1 -1
- package/scripts/lib/docs/glob-to-regex.md +31 -0
- package/scripts/lib/docs/index.md +2 -0
- package/scripts/lib/docs/js-source-signals.md +32 -0
- package/scripts/lib/glob-to-regex.mjs +79 -0
- package/scripts/lib/js-source-signals.mjs +50 -0
- package/scripts/utils/ast-scan-utils.mjs +2 -2
- package/scripts/utils/docs/ast-scan-utils.md +1 -1
- package/rules/bun/bunfig/bunfig.mdc +0 -17
- package/rules/bun/bunfig/bunfig.rego +0 -29
- package/rules/bun/bunfig/concern.json +0 -9
- package/rules/bun/bunfig/template/bunfig.toml.snippet.toml +0 -2
- package/rules/bun/docs/index.md +0 -11
- package/rules/bun/layout/concern.json +0 -16
- package/rules/bun/layout/docs/fix-layout.md +0 -29
- package/rules/bun/layout/docs/main.md +0 -34
- package/rules/bun/layout/fix-layout.mjs +0 -63
- package/rules/bun/layout/layout.mdc +0 -60
- package/rules/bun/layout/main.mjs +0 -53
- package/rules/bun/licensee/concern.json +0 -7
- package/rules/bun/licensee/docs/fix-licensee.md +0 -27
- package/rules/bun/licensee/docs/index.md +0 -12
- package/rules/bun/licensee/docs/main.md +0 -30
- package/rules/bun/licensee/fix-licensee.mjs +0 -31
- package/rules/bun/licensee/main.mjs +0 -68
- package/rules/bun/lint-surface/concern.json +0 -3
- package/rules/bun/lint-surface/lint-surface.mdc +0 -13
- package/rules/bun/main.json +0 -1
- package/rules/bun/main.mdc +0 -11
- package/rules/bun/package_json/concern.json +0 -9
- package/rules/bun/package_json/docs/fix-package_json.md +0 -28
- package/rules/bun/package_json/docs/index.md +0 -9
- package/rules/bun/package_json/fix-package_json.mjs +0 -311
- package/rules/bun/package_json/package_json.mdc +0 -14
- package/rules/bun/package_json/package_json.rego +0 -64
- package/rules/bun/package_json/template/package.json.deny.json +0 -4
- package/rules/js/check/check.mdc +0 -26
- package/rules/js/check/concern.json +0 -19
- package/rules/js/check/docs/eslint-config.md +0 -56
- package/rules/js/check/docs/fix-check.md +0 -48
- package/rules/js/check/docs/index.md +0 -13
- package/rules/js/check/docs/main.md +0 -48
- package/rules/js/check/eslint-config.mjs +0 -262
- package/rules/js/check/fix-check.mjs +0 -82
- package/rules/js/check/main.mjs +0 -310
- package/rules/js/dep-policy/concern.json +0 -4
- package/rules/js/dep-policy/dep-policy.mdc +0 -36
- package/rules/js/dep-policy/docs/main.md +0 -35
- package/rules/js/dep-policy/main.mjs +0 -99
- package/rules/js/docs/index.md +0 -11
- package/rules/js/eslint/concern.json +0 -8
- package/rules/js/eslint/docs/fix-eslint.md +0 -50
- package/rules/js/eslint/docs/fix-worker.md +0 -31
- package/rules/js/eslint/docs/index.md +0 -11
- package/rules/js/eslint/docs/main.md +0 -35
- package/rules/js/eslint/fix-eslint.mjs +0 -160
- package/rules/js/eslint/fix-worker.mjs +0 -139
- package/rules/js/eslint/main.mjs +0 -115
- package/rules/js/file-extensions/concern.json +0 -3
- package/rules/js/file-extensions/file-extensions.mdc +0 -12
- package/rules/js/jscpd_config/concern.json +0 -11
- package/rules/js/jscpd_config/docs/fix-jscpd_config.md +0 -26
- package/rules/js/jscpd_config/docs/index.md +0 -9
- package/rules/js/jscpd_config/fix-jscpd_config.mjs +0 -3
- package/rules/js/jscpd_config/jscpd_config.mdc +0 -42
- package/rules/js/jscpd_config/jscpd_config.rego +0 -44
- package/rules/js/jscpd_config/template/.jscpd.json.snippet.json +0 -7
- package/rules/js/jscpd_duplicates/concern.json +0 -7
- package/rules/js/jscpd_duplicates/docs/main.md +0 -29
- package/rules/js/jscpd_duplicates/main.mjs +0 -69
- package/rules/js/knip/concern.json +0 -7
- package/rules/js/knip/docs/main.md +0 -32
- package/rules/js/knip/knip.mdc +0 -15
- package/rules/js/knip/main.mjs +0 -68
- package/rules/js/lint-findings/concern.json +0 -3
- package/rules/js/lint-findings/docs/main.md +0 -41
- package/rules/js/lint-findings/main.mjs +0 -125
- package/rules/js/main.json +0 -1
- package/rules/js/main.mdc +0 -18
- package/rules/js/package_json/concern.json +0 -9
- package/rules/js/package_json/docs/fix-package_json.md +0 -26
- package/rules/js/package_json/docs/index.md +0 -9
- package/rules/js/package_json/fix-package_json.mjs +0 -3
- package/rules/js/package_json/package_json.mdc +0 -15
- package/rules/js/package_json/package_json.rego +0 -142
- package/rules/js/package_json/template/package.json.snippet.json +0 -6
- package/rules/js/tooling/concern.json +0 -3
- package/rules/js/tooling/data/tooling/knip-canonical.json +0 -30
- package/rules/js/tooling/data/tooling/oxlint-canonical.json +0 -400
- package/rules/js/tooling/docs/main.md +0 -53
- package/rules/js/tooling/main.mjs +0 -183
- package/rules/js/utils_imports/concern.json +0 -4
- package/rules/js/utils_imports/docs/main.md +0 -50
- package/rules/js/utils_imports/main.mjs +0 -185
- package/rules/js/utils_imports/utils_imports.mdc +0 -15
- package/rules/js/vscode_extensions/concern.json +0 -11
- package/rules/js/vscode_extensions/docs/fix-vscode_extensions.md +0 -25
- package/rules/js/vscode_extensions/docs/index.md +0 -11
- package/rules/js/vscode_extensions/fix-vscode_extensions.mjs +0 -1
- package/rules/js/vscode_extensions/template/extensions.json.snippet.json +0 -6
- package/rules/js/vscode_extensions/vscode_extensions.mdc +0 -11
- package/rules/js/vscode_extensions/vscode_extensions.rego +0 -12
- package/rules/js-bun-db/connection/concern.json +0 -3
- package/rules/js-bun-db/connection/connection.mdc +0 -42
- package/rules/js-bun-db/docs/index.md +0 -11
- package/rules/js-bun-db/lib/bun-sql-scan.mjs +0 -1055
- package/rules/js-bun-db/lib/docs/bun-sql-scan.md +0 -63
- package/rules/js-bun-db/lib/docs/index.md +0 -11
- package/rules/js-bun-db/main.json +0 -1
- package/rules/js-bun-db/main.mdc +0 -8
- package/rules/js-bun-db/package_json/concern.json +0 -9
- package/rules/js-bun-db/package_json/package_json.mdc +0 -31
- package/rules/js-bun-db/package_json/package_json.rego +0 -15
- package/rules/js-bun-db/package_json/template/package.json.deny.json +0 -6
- package/rules/js-bun-db/pg_format_identifiers/concern.json +0 -3
- package/rules/js-bun-db/pg_format_identifiers/pg_format_identifiers.mdc +0 -104
- package/rules/js-bun-db/safety/concern.json +0 -4
- package/rules/js-bun-db/safety/docs/main.md +0 -34
- package/rules/js-bun-db/safety/main.mjs +0 -430
- package/rules/js-bun-db/safety/safety.mdc +0 -458
- package/rules/js-bun-redis/docs/index.md +0 -11
- package/rules/js-bun-redis/imports/concern.json +0 -4
- package/rules/js-bun-redis/imports/docs/main.md +0 -36
- package/rules/js-bun-redis/imports/imports.mdc +0 -47
- package/rules/js-bun-redis/imports/main.mjs +0 -88
- package/rules/js-bun-redis/lib/docs/index.md +0 -11
- package/rules/js-bun-redis/lib/docs/redis-imports.md +0 -227
- package/rules/js-bun-redis/lib/redis-imports.mjs +0 -130
- package/rules/js-bun-redis/main.json +0 -1
- package/rules/js-bun-redis/main.mdc +0 -8
- package/rules/js-bun-redis/package_json/concern.json +0 -9
- package/rules/js-bun-redis/package_json/package_json.mdc +0 -11
- package/rules/js-bun-redis/package_json/package_json.rego +0 -15
- package/rules/js-bun-redis/package_json/template/package.json.deny.json +0 -12
- package/rules/js-mssql/deps/concern.json +0 -4
- package/rules/js-mssql/deps/docs/main.md +0 -33
- package/rules/js-mssql/deps/main.mjs +0 -297
- package/rules/js-mssql/docs/index.md +0 -11
- package/rules/js-mssql/lib/docs/index.md +0 -11
- package/rules/js-mssql/lib/docs/mssql-pool-scan.md +0 -380
- package/rules/js-mssql/lib/mssql-pool-scan.mjs +0 -610
- package/rules/js-mssql/main.json +0 -1
- package/rules/js-mssql/main.mdc +0 -144
- package/rules/js-mssql/mssql-tvp/concern.json +0 -3
- package/rules/js-mssql/mssql-tvp/mssql-tvp.mdc +0 -77
- package/rules/js-mssql/package_json/concern.json +0 -9
- package/rules/js-mssql/package_json/package_json.mdc +0 -9
- package/rules/js-mssql/package_json/package_json.rego +0 -57
- package/rules/js-run/configmap/concern.json +0 -9
- package/rules/js-run/configmap/configmap.mdc +0 -37
- package/rules/js-run/configmap/configmap.rego +0 -21
- package/rules/js-run/configmap/template/configmap.yaml.contains.yml +0 -4
- package/rules/js-run/docs/index.md +0 -11
- package/rules/js-run/jsconfig/concern.json +0 -9
- package/rules/js-run/jsconfig/docs/fix-jsconfig.md +0 -29
- package/rules/js-run/jsconfig/docs/index.md +0 -9
- package/rules/js-run/jsconfig/fix-jsconfig.mjs +0 -119
- package/rules/js-run/jsconfig/jsconfig.mdc +0 -48
- package/rules/js-run/jsconfig/jsconfig.rego +0 -59
- package/rules/js-run/jsconfig/template/jsconfig.json.snippet.json +0 -10
- package/rules/js-run/lib/bunyan-imports.mjs +0 -98
- package/rules/js-run/lib/check-env-scan.mjs +0 -338
- package/rules/js-run/lib/conn-file-rules.mjs +0 -214
- package/rules/js-run/lib/conn-imports-scan.mjs +0 -154
- package/rules/js-run/lib/docs/bunyan-imports.md +0 -121
- package/rules/js-run/lib/docs/check-env-scan.md +0 -438
- package/rules/js-run/lib/docs/conn-file-rules.md +0 -304
- package/rules/js-run/lib/docs/conn-imports-scan.md +0 -208
- package/rules/js-run/lib/docs/index.md +0 -16
- package/rules/js-run/lib/docs/promise-settimeout-scan.md +0 -334
- package/rules/js-run/lib/docs/temporal-scan.md +0 -29
- package/rules/js-run/lib/promise-settimeout-scan.mjs +0 -128
- package/rules/js-run/lib/temporal-scan.mjs +0 -52
- package/rules/js-run/main.json +0 -1
- package/rules/js-run/main.mdc +0 -16
- package/rules/js-run/package_json/concern.json +0 -9
- package/rules/js-run/package_json/package_json.mdc +0 -44
- package/rules/js-run/package_json/package_json.rego +0 -37
- package/rules/js-run/package_json/template/package.json.deny.json +0 -22
- package/rules/js-run/project-structure/concern.json +0 -3
- package/rules/js-run/project-structure/project-structure.mdc +0 -11
- package/rules/js-run/runtime/concern.json +0 -12
- package/rules/js-run/runtime/docs/fix-runtime.md +0 -27
- package/rules/js-run/runtime/docs/main.md +0 -35
- package/rules/js-run/runtime/fix-runtime.mjs +0 -46
- package/rules/js-run/runtime/main.mjs +0 -494
- package/rules/js-run/runtime/runtime.mdc +0 -184
- package/rules/js-run/scope/concern.json +0 -3
- package/rules/js-run/scope/scope.mdc +0 -11
- package/rules/npm-module/docs/index.md +0 -11
- package/rules/npm-module/emit_types_config/concern.json +0 -9
- package/rules/npm-module/emit_types_config/docs/fix-emit_types_config.md +0 -25
- package/rules/npm-module/emit_types_config/docs/index.md +0 -9
- package/rules/npm-module/emit_types_config/emit_types_config.mdc +0 -43
- package/rules/npm-module/emit_types_config/emit_types_config.rego +0 -28
- package/rules/npm-module/emit_types_config/fix-emit_types_config.mjs +0 -5
- package/rules/npm-module/emit_types_config/template/tsconfig.emit-types.json.snippet.json +0 -9
- package/rules/npm-module/header_doc_pointer/concern.json +0 -5
- package/rules/npm-module/header_doc_pointer/docs/main.md +0 -40
- package/rules/npm-module/header_doc_pointer/header_doc_pointer.mdc +0 -18
- package/rules/npm-module/header_doc_pointer/main.mjs +0 -131
- package/rules/npm-module/main.json +0 -1
- package/rules/npm-module/main.mdc +0 -36
- package/rules/npm-module/npm_package_json/concern.json +0 -9
- package/rules/npm-module/npm_package_json/docs/fix-npm_package_json.md +0 -27
- package/rules/npm-module/npm_package_json/docs/index.md +0 -9
- package/rules/npm-module/npm_package_json/fix-npm_package_json.mjs +0 -5
- package/rules/npm-module/npm_package_json/npm_package_json.mdc +0 -57
- package/rules/npm-module/npm_package_json/npm_package_json.rego +0 -73
- package/rules/npm-module/npm_package_json/template/package.json.snippet.json +0 -1
- package/rules/npm-module/package_structure/concern.json +0 -5
- package/rules/npm-module/package_structure/docs/main.md +0 -37
- package/rules/npm-module/package_structure/main.mjs +0 -515
- package/rules/npm-module/package_structure/package_structure.mdc +0 -63
- package/rules/npm-module/root_package_json/concern.json +0 -9
- package/rules/npm-module/root_package_json/docs/fix-root_package_json.md +0 -26
- package/rules/npm-module/root_package_json/docs/index.md +0 -9
- package/rules/npm-module/root_package_json/fix-root_package_json.mjs +0 -5
- package/rules/npm-module/root_package_json/root_package_json.mdc +0 -41
- package/rules/npm-module/root_package_json/root_package_json.rego +0 -28
- package/rules/npm-module/root_package_json/template/package.json.snippet.json +0 -1
- package/rules/npm-module/rule_meta/concern.json +0 -8
- package/rules/npm-module/rule_meta/docs/main.md +0 -35
- package/rules/npm-module/rule_meta/main.mjs +0 -119
- package/rules/npm-module/rule_meta/rule_meta.mdc +0 -11
- package/rules/npm-module/skill_meta/concern.json +0 -5
- package/rules/npm-module/skill_meta/docs/main.md +0 -149
- package/rules/npm-module/skill_meta/main.mjs +0 -91
- package/rules/npm-module/skill_meta/skill_meta.mdc +0 -11
- package/rules/tool-surface/docs/index.md +0 -11
- package/rules/tool-surface/main.json +0 -6
- package/rules/tool-surface/main.mdc +0 -72
- package/rules/vue/composition-api/composition-api.mdc +0 -82
- package/rules/vue/composition-api/concern.json +0 -3
- package/rules/vue/docs/index.md +0 -11
- package/rules/vue/lib/docs/index.md +0 -11
- package/rules/vue/lib/docs/vue-forbidden-imports.md +0 -265
- package/rules/vue/lib/vue-forbidden-imports.mjs +0 -264
- package/rules/vue/main.json +0 -1
- package/rules/vue/main.mdc +0 -18
- package/rules/vue/nheader-layout/concern.json +0 -3
- package/rules/vue/nheader-layout/nheader-layout.mdc +0 -171
- package/rules/vue/package_json/concern.json +0 -9
- package/rules/vue/package_json/package_json.mdc +0 -30
- package/rules/vue/package_json/package_json.rego +0 -140
- package/rules/vue/packages/concern.json +0 -6
- package/rules/vue/packages/docs/index.md +0 -11
- package/rules/vue/packages/docs/main.md +0 -35
- package/rules/vue/packages/main.mjs +0 -575
- package/rules/vue/packages/packages.mdc +0 -56
- package/rules/vue/quasar-ui/concern.json +0 -3
- package/rules/vue/quasar-ui/quasar-ui.mdc +0 -32
- package/rules/vue/structure/concern.json +0 -3
- package/rules/vue/structure/structure.mdc +0 -101
- package/rules/vue/testing/concern.json +0 -3
- package/rules/vue/testing/testing.mdc +0 -40
- package/rules/vue/tfm-translations/concern.json +0 -7
- package/rules/vue/tfm-translations/docs/main.md +0 -29
- package/rules/vue/tfm-translations/main.mjs +0 -55
- package/rules/vue/tfm-translations/tfm-translations.mdc +0 -32
- package/rules/vue/vite-config/concern.json +0 -3
- package/rules/vue/vite-config/vite-config.mdc +0 -153
- package/rules/vue/vite-env/concern.json +0 -3
- package/rules/vue/vite-env/vite-env.mdc +0 -61
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/** @see ./docs/fix-eslint.md */
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* T0-autofix для `js/eslint` — детермінований прогін лінтерів у fix-режимі
|
|
5
|
-
* (`oxlint --fix` + `eslint --fix`) на файлах, де детектор знайшов порушення.
|
|
6
|
-
* Виправляє лише авто-fixable правила; решта лишається детектору на re-check
|
|
7
|
-
* (далі — LLM-ладдер). Запис permanent (поза rollback).
|
|
8
|
-
*
|
|
9
|
-
* Межа CI: цей модуль — частина fix-фази (`lint` без `--no-fix`); сам по собі
|
|
10
|
-
* detector-шлях його не вантажить. У CI (`--no-fix`) fix не запускається —
|
|
11
|
-
* узгоджено із забороною `oxlint --fix`/`eslint --fix` у CI (js.mdc / lint_js_yml).
|
|
12
|
-
*/
|
|
13
|
-
import { spawnSync } from 'node:child_process'
|
|
14
|
-
import { readFileSync, writeFileSync } from 'node:fs'
|
|
15
|
-
import { resolve } from 'node:path'
|
|
16
|
-
|
|
17
|
-
import { ESLint } from 'eslint'
|
|
18
|
-
|
|
19
|
-
import { filterJsFiles } from './main.mjs'
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Вміст файлу або null, якщо не читається (видалений/недоступний).
|
|
23
|
-
* @param {string} abs абсолютний шлях
|
|
24
|
-
* @returns {string|null} вміст або null
|
|
25
|
-
*/
|
|
26
|
-
function readOrNull(abs) {
|
|
27
|
-
try {
|
|
28
|
-
return readFileSync(abs, 'utf8')
|
|
29
|
-
} catch {
|
|
30
|
-
return null
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Прогін `oxlint --fix` + `eslint --fix` по списку js-файлів. Повертає абсолютні
|
|
36
|
-
* шляхи фактично змінених файлів (порівняння до/після).
|
|
37
|
-
* @param {string[]} jsFiles posix-relative js-файли
|
|
38
|
-
* @param {string} cwd корінь проєкту
|
|
39
|
-
* @returns {Promise<string[]>} абсолютні шляхи змінених файлів
|
|
40
|
-
*/
|
|
41
|
-
async function runLinterFix(jsFiles, cwd) {
|
|
42
|
-
const abs = jsFiles.map(f => resolve(cwd, f))
|
|
43
|
-
const before = new Map(abs.map(a => [a, readOrNull(a)]))
|
|
44
|
-
|
|
45
|
-
// oxlint --fix (CLI): авто-fixable oxc-правила.
|
|
46
|
-
spawnSync('bunx', ['oxlint', '--fix', ...jsFiles], { cwd, encoding: 'utf8' })
|
|
47
|
-
|
|
48
|
-
// eslint --fix (API): outputFixes пише виправлені файли на диск.
|
|
49
|
-
const eslint = new ESLint({ cwd, fix: true })
|
|
50
|
-
const results = await eslint.lintFiles(abs)
|
|
51
|
-
await ESLint.outputFixes(results)
|
|
52
|
-
|
|
53
|
-
/** @type {string[]} */
|
|
54
|
-
const touched = []
|
|
55
|
-
for (const a of abs) {
|
|
56
|
-
if (readOrNull(a) !== before.get(a)) touched.push(a)
|
|
57
|
-
}
|
|
58
|
-
return touched
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Реєстр "механічних" правил `js/eslint`: `oxlint --fix`/`eslint --fix` (T0
|
|
63
|
-
* `js-eslint-autofix` вище) їх НЕ покриває (suggestion-only у власній реалізації
|
|
64
|
-
* інструментів — перевірено емпірично: лишаються порушеними після `--fix` на
|
|
65
|
-
* реальних прогонах), але текстуальна заміна на позначеному рядку однозначна й
|
|
66
|
-
* безпечна без повного AST-парсингу — проста заміна ідентифікатора/API-виклику.
|
|
67
|
-
* `reasons` — обидва формати `reason` з `main.mjs` (`f.rule`): eslint (`ruleId`,
|
|
68
|
-
* `"plugin/rule"`) і oxlint (`d.code`, `"plugin(rule)"`) — те саме правило різні
|
|
69
|
-
* тули віддають по-різному.
|
|
70
|
-
* @type {Array<{ reasons: string[], replace: (line: string) => string|null }>}
|
|
71
|
-
*/
|
|
72
|
-
const MECHANICAL_TEXT_FIXES = [
|
|
73
|
-
{
|
|
74
|
-
// Number.isInteger(x) не ловить x поза Number.MIN/MAX_SAFE_INTEGER — сама заміна
|
|
75
|
-
// імені методу семантично точна для рядка, що ЛИШЕ так і викликає isInteger.
|
|
76
|
-
reasons: ['unicorn/prefer-number-is-safe-integer', 'unicorn(prefer-number-is-safe-integer)'],
|
|
77
|
-
replace: line =>
|
|
78
|
-
line.includes('Number.isInteger') ? line.replaceAll('Number.isInteger', 'Number.isSafeInteger') : null
|
|
79
|
-
}
|
|
80
|
-
]
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Знаходить механічний фікс для reason-у порушення (обидва формати tool-у).
|
|
84
|
-
* @param {string} reason `violation.reason`
|
|
85
|
-
* @returns {((line: string) => string|null)|null} replace-функція або null
|
|
86
|
-
*/
|
|
87
|
-
function mechanicalFixFor(reason) {
|
|
88
|
-
return MECHANICAL_TEXT_FIXES.find(f => f.reasons.includes(reason))?.replace ?? null
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Застосовує механічні текстові заміни по конкретних рядках (`data.line`, 1-indexed)
|
|
93
|
-
* файлу. Рядок без збігу очікуваного шаблону (файл змінився з моменту detect-у) —
|
|
94
|
-
* пропускається, не гадаємо.
|
|
95
|
-
* @param {string} content вміст файлу
|
|
96
|
-
* @param {Array<{ line: number, replace: (line: string) => string|null }>} targets рядки й replace-функції
|
|
97
|
-
* @returns {string|null} новий вміст або null, якщо жоден рядок не змінився
|
|
98
|
-
*/
|
|
99
|
-
function applyMechanicalLineFixes(content, targets) {
|
|
100
|
-
const lines = content.split('\n')
|
|
101
|
-
let changed = false
|
|
102
|
-
for (const { line, replace } of targets) {
|
|
103
|
-
const idx = line - 1
|
|
104
|
-
if (idx < 0 || idx >= lines.length) continue
|
|
105
|
-
const next = replace(lines[idx])
|
|
106
|
-
if (next !== null && next !== lines[idx]) {
|
|
107
|
-
lines[idx] = next
|
|
108
|
-
changed = true
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return changed ? lines.join('\n') : null
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** @type {import('../../../scripts/lib/lint-surface/types.mjs').T0Pattern[]} */
|
|
115
|
-
export const patterns = [
|
|
116
|
-
{
|
|
117
|
-
id: 'js-eslint-autofix',
|
|
118
|
-
test: violations => violations.some(v => v.file),
|
|
119
|
-
apply: async (violations, ctx) => {
|
|
120
|
-
const files = [...new Set(violations.map(v => v.file).filter(Boolean))]
|
|
121
|
-
const jsFiles = filterJsFiles(files)
|
|
122
|
-
if (jsFiles.length === 0) return { touchedFiles: [] }
|
|
123
|
-
const touchedFiles = await runLinterFix(jsFiles, ctx.cwd)
|
|
124
|
-
for (const a of touchedFiles) ctx.recordWrite?.(a)
|
|
125
|
-
return touchedFiles.length > 0
|
|
126
|
-
? { touchedFiles, message: `oxlint/eslint --fix: ${touchedFiles.length} файл(ів)` }
|
|
127
|
-
: { touchedFiles: [] }
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
id: 'js-eslint-mechanical-text-fix',
|
|
132
|
-
test: violations => violations.some(v => v.file && v.data?.line && mechanicalFixFor(v.reason)),
|
|
133
|
-
apply: (violations, ctx) => {
|
|
134
|
-
/** @type {Map<string, Array<{ line: number, replace: (line: string) => string|null }>>} */
|
|
135
|
-
const byFile = new Map()
|
|
136
|
-
for (const v of violations) {
|
|
137
|
-
const replace = v.file && v.data?.line ? mechanicalFixFor(v.reason) : null
|
|
138
|
-
if (!replace) continue
|
|
139
|
-
const arr = byFile.get(v.file)
|
|
140
|
-
const target = { line: v.data.line, replace }
|
|
141
|
-
if (arr) arr.push(target)
|
|
142
|
-
else byFile.set(v.file, [target])
|
|
143
|
-
}
|
|
144
|
-
const touchedFiles = []
|
|
145
|
-
for (const [rel, targets] of byFile) {
|
|
146
|
-
const abs = resolve(ctx.cwd, rel)
|
|
147
|
-
const content = readOrNull(abs)
|
|
148
|
-
if (content === null) continue
|
|
149
|
-
const next = applyMechanicalLineFixes(content, targets)
|
|
150
|
-
if (next === null) continue
|
|
151
|
-
ctx.recordWrite?.(abs)
|
|
152
|
-
writeFileSync(abs, next)
|
|
153
|
-
touchedFiles.push(abs)
|
|
154
|
-
}
|
|
155
|
-
return touchedFiles.length > 0
|
|
156
|
-
? { touchedFiles, message: `механічні текстові заміни: ${touchedFiles.length} файл(ів)` }
|
|
157
|
-
: { touchedFiles: [] }
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
]
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* fix-worker для `js/eslint`: окрема агентна сесія НА ФАЙЛ замість одного великого промпту
|
|
3
|
-
* на всі файли concern-а одразу. Дефолтний worker (`default-worker.mjs`) шле ВСІ порушення
|
|
4
|
-
* з УСІХ файлів в один `runAgentFix`-виклик — на реальних lint-прогонах (tauri-components)
|
|
5
|
-
* це давало 100% timeout на всіх 4 rung-ах драбини (local-min→cloud-avg), навіть коли
|
|
6
|
-
* порушень було лише 20-90 у 1-3 файлах: одна сесія жонглює кількома файлами одразу і не
|
|
7
|
-
* встигає в rung-таймаут.
|
|
8
|
-
*
|
|
9
|
-
* Файли обробляються ПАРАЛЕЛЬНО (обмежений пул, `MAX_PARALLEL_FILES`), не послідовно —
|
|
10
|
-
* ADR docs/adr/260718-0754-js-eslint-fix-worker-per-session-overhead.md. Профайлінг
|
|
11
|
-
* реального прогону (2026-07-18, trace `runAgentFix`) спростував гіпотезу про фіксовані
|
|
12
|
-
* bootstrap-витрати сесії: `dispatch.test.js` (2 порушення) — 9 раундів моделі, 29.4с;
|
|
13
|
-
* тривіальний фікс так само вимагав багато tool-раундів, а не одноразової плати за bootstrap.
|
|
14
|
-
* Оскільки домінує саме кількість/довжина turn-раундів, а не старт сесії, паралелізм
|
|
15
|
-
* (кілька файлів одночасно замість черги) дає пряме пришвидшення незалежно від причини —
|
|
16
|
-
* секвенційна версія витрачала весь бюджет rung-а на 1 файл, лишаючи іншим 0 шансів
|
|
17
|
-
* навіть стартувати.
|
|
18
|
-
*
|
|
19
|
-
* Rollback-контракт незмінний: `recordWrite` (не durable) на кожен файл — правки наявного
|
|
20
|
-
* стороннього коду. Якщо після worker-а `runRung`-ів canonical re-detect (whole-concern)
|
|
21
|
-
* знайде хоч одне порушення будь-де, увесь rung однаково відкотиться (S1) — той самий
|
|
22
|
-
* контракт, що й у дефолтного worker-а; паралелізм підвищує ймовірність, що ЦЕЙ rung
|
|
23
|
-
* дійде до 0 порушень, а не замінює rollback-семантику. `snapshot.record`/`recordWrite` —
|
|
24
|
-
* синхронні Map-операції без `await` усередині, тож конкурентні виклики безпечні (Node
|
|
25
|
-
* event loop не перериває синхронний блок).
|
|
26
|
-
*
|
|
27
|
-
* Дедлайн (`DEADLINE_FRACTION` від `ctx.timeoutMs`, той самий підхід, що й
|
|
28
|
-
* `doc-files/check/fix-worker.mjs`) гейтить лише СТАРТ нового файлу з черги — не
|
|
29
|
-
* скасовує вже запущені. При files.length ≤ MAX_PARALLEL_FILES усі стартують майже
|
|
30
|
-
* одночасно й отримують практично весь бюджет незалежно один від одного (справжній
|
|
31
|
-
* паралелізм); при більшій кількості — черга природно звужує бюджет пізніших хвиль
|
|
32
|
-
* (`callTimeoutMs` рахується в момент старту, не наперед), не даючи перевищити
|
|
33
|
-
* backstop ×1.25 runner-а.
|
|
34
|
-
* @typedef {import('../../../scripts/lib/lint-surface/types.mjs').FixWorkerFn} FixWorkerFn
|
|
35
|
-
*/
|
|
36
|
-
import { resolve } from 'node:path'
|
|
37
|
-
|
|
38
|
-
import { anchoredEnabled } from '../../../scripts/lib/lint-surface/default-worker.mjs'
|
|
39
|
-
import { renderViolations } from '../../../scripts/lib/lint-surface/render.mjs'
|
|
40
|
-
import { lint } from './main.mjs'
|
|
41
|
-
|
|
42
|
-
/** Частка ctx.timeoutMs, після якої черга не стартує новий файл (запас до backstop ×1.25). */
|
|
43
|
-
const DEADLINE_FRACTION = 0.8
|
|
44
|
-
|
|
45
|
-
/** Максимум файлів, що обробляються одночасно (без необмеженого burst на великих concern-ах). */
|
|
46
|
-
const MAX_PARALLEL_FILES = 4
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Item-scoped (один файл) canonical re-detect для evidence-гейта `runAgentFix` —
|
|
50
|
-
* НЕ те саме, що whole-concern `ctx.verify` з `runRung` (той перевірив би ВСІ файли
|
|
51
|
-
* concern-а й дав би хибний "не готово" навіть коли саме ЦЕЙ файл уже чистий).
|
|
52
|
-
* @param {string} cwd абсолютний корінь проєкту
|
|
53
|
-
* @param {string} ruleId id правила
|
|
54
|
-
* @param {string} concernId id concern-а
|
|
55
|
-
* @param {string} file posix-relative шлях файлу, що перевіряється
|
|
56
|
-
* @returns {Promise<{ ok: boolean, output: string }>} вердикт verify-петлі
|
|
57
|
-
*/
|
|
58
|
-
async function verifyFile(cwd, ruleId, concernId, file) {
|
|
59
|
-
const { violations: after } = await lint({ cwd, ruleId, concernId, files: [file] })
|
|
60
|
-
const stamped = after.map(v => ({ ...v, ruleId, concernId }))
|
|
61
|
-
return { ok: stamped.length === 0, output: renderViolations(stamped) }
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Обробляє `items` обмеженим пулом воркерів (не більше `MAX_PARALLEL_FILES` одночасно) —
|
|
66
|
-
* власна черга замість `Promise.all(items.map(...))`, щоб великий concern не відкрив
|
|
67
|
-
* необмежену кількість конкурентних агентних сесій одразу.
|
|
68
|
-
* @param {string[]} items елементи черги (шляхи файлів)
|
|
69
|
-
* @param {(item: string) => Promise<void>} worker обробник одного елемента; винятки ловить сам
|
|
70
|
-
* @returns {Promise<void>}
|
|
71
|
-
*/
|
|
72
|
-
async function runPooled(items, worker) {
|
|
73
|
-
const queue = [...items]
|
|
74
|
-
const runnerCount = Math.min(MAX_PARALLEL_FILES, items.length)
|
|
75
|
-
await Promise.all(
|
|
76
|
-
Array.from({ length: runnerCount }, async () => {
|
|
77
|
-
let item
|
|
78
|
-
while ((item = queue.shift())) {
|
|
79
|
-
await worker(item)
|
|
80
|
-
}
|
|
81
|
-
})
|
|
82
|
-
)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** @type {FixWorkerFn} */
|
|
86
|
-
export async function fixWorker(violations, ctx) {
|
|
87
|
-
// lazy import — тримає detect-шлях вільним від pi/oxc (read-only --no-fix не вантажить їх).
|
|
88
|
-
const [{ runAgentFix }, { isLocalModel }, { extractContext }] = await Promise.all([
|
|
89
|
-
import('@7n/llm-lib/agent-fix'),
|
|
90
|
-
import('@7n/llm-lib/model-tiers'),
|
|
91
|
-
import('../../../scripts/utils/ast-extract.mjs')
|
|
92
|
-
])
|
|
93
|
-
|
|
94
|
-
/** @type {Map<string, import('../../../scripts/lib/lint-surface/types.mjs').LintViolation[]>} */
|
|
95
|
-
const byFile = new Map()
|
|
96
|
-
for (const v of violations) {
|
|
97
|
-
if (!v.file) continue
|
|
98
|
-
const arr = byFile.get(v.file)
|
|
99
|
-
if (arr) arr.push(v)
|
|
100
|
-
else byFile.set(v.file, [v])
|
|
101
|
-
}
|
|
102
|
-
const files = byFile.keys().toArray()
|
|
103
|
-
if (files.length === 0) return { touchedFiles: [] }
|
|
104
|
-
|
|
105
|
-
const deadlineAt = ctx.timeoutMs ? Date.now() + Math.round(ctx.timeoutMs * DEADLINE_FRACTION) : null
|
|
106
|
-
const anchoredEdits = anchoredEnabled(ctx.model, isLocalModel)
|
|
107
|
-
|
|
108
|
-
const touchedFiles = []
|
|
109
|
-
await runPooled(files, async file => {
|
|
110
|
-
if (deadlineAt && Date.now() >= deadlineAt) return
|
|
111
|
-
const callTimeoutMs = deadlineAt ? Math.max(1000, deadlineAt - Date.now()) : ctx.timeoutMs
|
|
112
|
-
|
|
113
|
-
let res
|
|
114
|
-
try {
|
|
115
|
-
res = await runAgentFix(ctx.ruleId, renderViolations(byFile.get(file)), ctx.cwd, {
|
|
116
|
-
model: ctx.model,
|
|
117
|
-
tier: ctx.tier,
|
|
118
|
-
timeoutMs: callTimeoutMs,
|
|
119
|
-
feedback: ctx.feedback ?? null,
|
|
120
|
-
caller: `fix:${ctx.ruleId}/${ctx.concernId}:${ctx.tier}:${file}`,
|
|
121
|
-
recordWrite: ctx.recordWrite,
|
|
122
|
-
chain: ctx.chain ?? null,
|
|
123
|
-
targetFiles: [file],
|
|
124
|
-
verify: () => verifyFile(ctx.cwd, ctx.ruleId, ctx.concernId, file),
|
|
125
|
-
verifyMax: ctx.verifyMax,
|
|
126
|
-
anchoredEdits,
|
|
127
|
-
deps: { astContext: p => extractContext(resolve(ctx.cwd, p)) }
|
|
128
|
-
})
|
|
129
|
-
} catch {
|
|
130
|
-
// Один файл впав винятком — не валимо решту паралельних воркерів у пулі; whole-concern
|
|
131
|
-
// canonical re-detect runner-а (не цей worker) визначить, чи rung закрито.
|
|
132
|
-
return
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (!res.error) touchedFiles.push(...(res.touchedFiles ?? []))
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
return { touchedFiles }
|
|
139
|
-
}
|
package/rules/js/eslint/main.mjs
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
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 '../../../scripts/lib/diff-added-lines.mjs'
|
|
10
|
-
import { spawnAsync } from '../../../scripts/utils/spawn-async.mjs'
|
|
11
|
-
import { WORKTREE_CHECKOUT_GLOBS } from '../../../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('../../../scripts/lib/lint-surface/types.mjs').LintViolation} нормалізоване порушення
|
|
42
|
-
*/
|
|
43
|
-
function toViolation(f, cwd, severity) {
|
|
44
|
-
return /** @type {import('../../../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('../../../scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст лінту
|
|
91
|
-
* @returns {Promise<import('../../../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
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
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` нікого не фейлить.
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: JS Module
|
|
3
|
-
title: fix-jscpd_config.mjs
|
|
4
|
-
resource: npm/rules/js/jscpd_config/fix-jscpd_config.mjs
|
|
5
|
-
docgen:
|
|
6
|
-
crc: 93258f26
|
|
7
|
-
model: openai-codex/gpt-5.4-mini
|
|
8
|
-
tier: cloud-min
|
|
9
|
-
score: 100
|
|
10
|
-
judgeModel: openai-codex/gpt-5.4-mini
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Огляд
|
|
14
|
-
|
|
15
|
-
`patterns` визначає узгоджені з правилами проєкту зміни для `.jscpd.json` і слугує централізованим орієнтиром для того, що саме має бути виправлено в цьому конфігу.
|
|
16
|
-
|
|
17
|
-
## Поведінка
|
|
18
|
-
|
|
19
|
-
1. `patterns` формує перелік правил для уніфікації шаблонного конфіга проєкту.
|
|
20
|
-
2. Кожне правило орієнтується на конфіг `.jscpd.json`, щоб тримати його в узгодженому, очікуваному форматі.
|
|
21
|
-
3. `patterns` потрібен для централізованого опису того, що саме має бути виправлено в `.jscpd.json`, без ручного втручання в інші файли чи артефакти.
|
|
22
|
-
4. `patterns` не змінює файлову систему або базу даних і працює лише як опис набору правок для цього конфіга.
|
|
23
|
-
|
|
24
|
-
## Гарантії поведінки
|
|
25
|
-
|
|
26
|
-
- Read-only: не виконує операцій запису (ФС/БД).
|
|
@@ -1,42 +0,0 @@
|
|
|
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). Якщо тестів немає або вони не покривають — спочатку створи їх, перевір що вони відпрацьовують коректно, потім роби рефакторинг і ще раз запускай тести.
|
|
@@ -1,44 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: JS Module
|
|
3
|
-
title: main.mjs
|
|
4
|
-
resource: npm/rules/js/jscpd_duplicates/main.mjs
|
|
5
|
-
docgen:
|
|
6
|
-
crc: 8839c5e9
|
|
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: не виконує операцій запису (ФС/БД).
|