@nitra/cursor 12.8.1 → 12.8.3
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/bin/n-cursor.js +1 -1
- package/package.json +1 -1
- package/rules/feedback/feedback.mdc +3 -3
- package/rules/ga/ga.mdc +1 -1
- package/rules/ga/policy/workflow_common/workflow_common.rego +3 -3
- package/rules/js/coverage/coverage.mjs +7 -7
- package/rules/js/docs/fix.md +1 -1
- package/rules/js/docs/index.md +3 -3
- package/rules/js/docs/main.md +1 -1
- package/rules/js/js/check.mjs +10 -10
- package/rules/js/js/docs/check.md +3 -3
- package/rules/js/js/docs/index.md +3 -3
- package/rules/js/js/docs/lint-findings.md +1 -1
- package/rules/js/js/docs/tooling.md +1 -1
- package/rules/js/js/docs/utils_imports.md +8 -8
- package/rules/js/js/tooling.mjs +1 -1
- package/rules/js/js/utils_imports.mjs +3 -3
- package/rules/js/js.mdc +30 -6
- package/rules/js/main.mjs +22 -5
- package/rules/js/policy/jscpd/jscpd.rego +4 -4
- package/rules/js/policy/jscpd/target.json +1 -1
- package/rules/js/policy/lint_js_yml/lint_js_yml.rego +6 -6
- package/rules/js/policy/lint_js_yml/template/lint-js.yml.snippet.yml +1 -1
- package/rules/js/policy/package_json/package_json.rego +7 -7
- package/rules/js/policy/vscode_extensions/target.json +1 -1
- package/rules/js/policy/vscode_extensions/vscode_extensions.rego +2 -2
- package/rules/js-run/lib/docs/conn-file-rules.md +1 -1
- package/rules/{js-lint-ci → style}/docs/fix.md +1 -1
- package/rules/{js-lint-ci → style}/docs/index.md +3 -3
- package/rules/{style-lint → style}/docs/main.md +1 -1
- package/rules/{style-lint → style}/js/docs/index.md +3 -3
- package/rules/{style-lint → style}/js/docs/tooling.md +3 -3
- package/rules/{style-lint → style}/js/tooling.mjs +2 -2
- package/rules/{style-lint → style}/policy/lint_style_yml/lint_style_yml.rego +2 -2
- package/rules/{style-lint → style}/policy/lint_style_yml/template/lint-style.yml.snippet.yml +1 -1
- package/rules/{style-lint → style}/policy/package_json/package_json.rego +3 -3
- package/rules/{style-lint → style}/policy/vscode_extensions/vscode_extensions.rego +2 -2
- package/rules/{style-lint → style}/policy/vscode_settings/vscode_settings.rego +2 -2
- package/rules/{style-lint/style-lint.mdc → style/style.mdc} +3 -3
- package/rules/test/js/docs/stryker_config.md +1 -1
- package/rules/test/js/stryker_config.mjs +4 -4
- package/rules/test/js/vitest-config-pool-forks.mjs +1 -1
- package/rules/test/test.mdc +4 -4
- package/scripts/lib/docs/discover-checkable-rules.md +2 -2
- package/scripts/lib/docs/gha-workflow.md +1 -1
- package/scripts/lib/gha-workflow.mjs +1 -1
- package/scripts/lib/timing-summary.mjs +1 -1
- package/scripts/sync-setup-bun-deps-action.mjs +1 -1
- package/scripts/utils/resolve-js-root.mjs +1 -1
- package/skills/coverage-fix/meta.json +1 -1
- package/skills/lint/SKILL.md +2 -2
- package/skills/llm-patch/SKILL.md +1 -1
- package/rules/js-lint-ci/docs/main.md +0 -27
- package/rules/js-lint-ci/js/docs/index.md +0 -11
- package/rules/js-lint-ci/js-lint-ci.mdc +0 -45
- package/rules/js-lint-ci/main.mjs +0 -33
- package/rules/js-lint-ci/meta.json +0 -1
- package/rules/style-lint/docs/fix.md +0 -28
- package/rules/style-lint/docs/index.md +0 -12
- /package/rules/{style-lint → style}/main.mjs +0 -0
- /package/rules/{style-lint → style}/meta.json +0 -0
- /package/rules/{style-lint → style}/policy/lint_style_yml/target.json +0 -0
- /package/rules/{style-lint → style}/policy/package_json/target.json +0 -0
- /package/rules/{style-lint → style}/policy/package_json/template/package.json.snippet.json +0 -0
- /package/rules/{style-lint → style}/policy/vscode_extensions/target.json +0 -0
- /package/rules/{style-lint → style}/policy/vscode_extensions/template/extensions.json.snippet.json +0 -0
- /package/rules/{style-lint → style}/policy/vscode_settings/target.json +0 -0
- /package/rules/{style-lint → style}/policy/vscode_settings/template/settings.json.snippet.json +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Перевірка `.github/workflows/lint-js.yml` (js
|
|
1
|
+
# Перевірка `.github/workflows/lint-js.yml` (js.mdc).
|
|
2
2
|
#
|
|
3
3
|
# Канон надходить через --data: { "template": { "snippet": ... } }
|
|
4
4
|
# Структура --data сформована з template/lint-js.yml.snippet.yml.
|
|
@@ -50,7 +50,7 @@ all_run_blob := concat("\n", [r |
|
|
|
50
50
|
deny contains msg if {
|
|
51
51
|
some required_use in expected_uses_set
|
|
52
52
|
not contains(all_uses_blob, required_use)
|
|
53
|
-
msg := sprintf("lint-js.yml: відсутній крок uses: %s (js
|
|
53
|
+
msg := sprintf("lint-js.yml: відсутній крок uses: %s (js.mdc)", [required_use])
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
# ── deny: required run substrings ───────────────────────────────────────
|
|
@@ -58,26 +58,26 @@ deny contains msg if {
|
|
|
58
58
|
deny contains msg if {
|
|
59
59
|
some required_run in expected_run_substrings
|
|
60
60
|
not contains(all_run_blob, required_run)
|
|
61
|
-
msg := sprintf("lint-js.yml: у run немає %q (js
|
|
61
|
+
msg := sprintf("lint-js.yml: у run немає %q (js.mdc)", [required_run])
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
# ── deny: actions/checkout@v6 has persist-credentials: false (inverse) ──
|
|
65
65
|
|
|
66
66
|
deny contains msg if {
|
|
67
67
|
not has_checkout_persist_credentials_false
|
|
68
|
-
msg := "lint-js.yml: actions/checkout@v6 має бути з with.persist-credentials: false (js
|
|
68
|
+
msg := "lint-js.yml: actions/checkout@v6 має бути з with.persist-credentials: false (js.mdc)"
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
# ── deny: --fix у CI заборонено (inverse) ───────────────────────────────
|
|
72
72
|
|
|
73
73
|
deny contains msg if {
|
|
74
74
|
regex.match(`bunx\s+oxlint[^\n]*--fix`, all_run_blob)
|
|
75
|
-
msg := "lint-js.yml: у run є oxlint з `--fix` (у CI заборонено) (js
|
|
75
|
+
msg := "lint-js.yml: у run є oxlint з `--fix` (у CI заборонено) (js.mdc)"
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
deny contains msg if {
|
|
79
79
|
contains(all_run_blob, "eslint --fix")
|
|
80
|
-
msg := "lint-js.yml: у run є `eslint --fix` (у CI заборонено) (js
|
|
80
|
+
msg := "lint-js.yml: у run є `eslint --fix` (у CI заборонено) (js.mdc)"
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
# ── helpers ─────────────────────────────────────────────────────────────
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Перевірка `package.json` (js
|
|
1
|
+
# Перевірка `package.json` (js.mdc).
|
|
2
2
|
#
|
|
3
3
|
# Канон надходить через --data: { "template": { "snippet": ... } }
|
|
4
4
|
# Структура --data сформована з template/package.json.snippet.json
|
|
@@ -20,7 +20,7 @@ deny contains msg if {
|
|
|
20
20
|
not is_object(expected_value)
|
|
21
21
|
actual := object.get(input, key, null)
|
|
22
22
|
actual != expected_value
|
|
23
|
-
msg := sprintf("package.json: \"%s\" має бути %q (js
|
|
23
|
+
msg := sprintf("package.json: \"%s\" має бути %q (js.mdc)", [key, expected_value])
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
# ── deny: scripts (nested) — exact match із normalize ──────────────────
|
|
@@ -29,7 +29,7 @@ deny contains msg if {
|
|
|
29
29
|
some script_name, expected in data.template.snippet.scripts
|
|
30
30
|
actual := object.get(object.get(input, "scripts", {}), script_name, "")
|
|
31
31
|
normalize_script(actual) != expected
|
|
32
|
-
msg := sprintf("package.json: scripts.%s має бути %q (js
|
|
32
|
+
msg := sprintf("package.json: scripts.%s має бути %q (js.mdc)", [script_name, expected])
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
# ── deny: engines.node >= 24 (inverse, у rego) ──────────────────────────
|
|
@@ -37,13 +37,13 @@ deny contains msg if {
|
|
|
37
37
|
deny contains msg if {
|
|
38
38
|
engines := object.get(input, "engines", {})
|
|
39
39
|
not engines_node_meets(object.get(engines, "node", ""))
|
|
40
|
-
msg := "package.json: engines.node має бути >= 24 (js
|
|
40
|
+
msg := "package.json: engines.node має бути >= 24 (js.mdc)"
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
deny contains msg if {
|
|
44
44
|
engines := object.get(input, "engines", {})
|
|
45
45
|
not engines_bun_meets(object.get(engines, "bun", ""))
|
|
46
|
-
msg := "package.json: engines.bun має бути >= 1.3 (js
|
|
46
|
+
msg := "package.json: engines.bun має бути >= 1.3 (js.mdc)"
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
# ── deny: @nitra/eslint-config >= snippet-поріг ─────────────────────────
|
|
@@ -51,14 +51,14 @@ deny contains msg if {
|
|
|
51
51
|
deny contains msg if {
|
|
52
52
|
dev := object.get(input, "devDependencies", {})
|
|
53
53
|
not "@nitra/eslint-config" in object.keys(dev)
|
|
54
|
-
msg := "package.json: відсутній @nitra/eslint-config у devDependencies (js
|
|
54
|
+
msg := "package.json: відсутній @nitra/eslint-config у devDependencies (js.mdc)"
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
deny contains msg if {
|
|
58
58
|
range := object.get(object.get(input, "devDependencies", {}), "@nitra/eslint-config", "")
|
|
59
59
|
range != ""
|
|
60
60
|
not eslint_config_meets_min(range)
|
|
61
|
-
msg := sprintf("package.json: @nitra/eslint-config має бути >= %s (зараз %q) (js
|
|
61
|
+
msg := sprintf("package.json: @nitra/eslint-config має бути >= %s (зараз %q) (js.mdc)", [eslint_min_display, range])
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
# ── helpers ──────────────────────────────────────────────────────────────
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Перевірка `.vscode/extensions.json` для js
|
|
1
|
+
# Перевірка `.vscode/extensions.json` для js (js.mdc).
|
|
2
2
|
#
|
|
3
3
|
# Канон надходить через --data: { "template": { "snippet": ... } }
|
|
4
4
|
package js_lint.vscode_extensions
|
|
@@ -8,5 +8,5 @@ import rego.v1
|
|
|
8
8
|
deny contains msg if {
|
|
9
9
|
some rec in data.template.snippet.recommendations
|
|
10
10
|
not rec in {r | some r in object.get(input, "recommendations", [])}
|
|
11
|
-
msg := sprintf(".vscode/extensions.json: recommendations має містити %q (js
|
|
11
|
+
msg := sprintf(".vscode/extensions.json: recommendations має містити %q (js.mdc)", [rec])
|
|
12
12
|
}
|
|
@@ -29,7 +29,7 @@ docgen:
|
|
|
29
29
|
|
|
30
30
|
## Експорти / API
|
|
31
31
|
|
|
32
|
-
Модуль експортує тільки named-exports (узгоджено з `n-js
|
|
32
|
+
Модуль експортує тільки named-exports (узгоджено з `n-js` / `js-run.mdc`):
|
|
33
33
|
|
|
34
34
|
| Експорт | Тип | Призначення |
|
|
35
35
|
| -------------------------------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
type: Directory Index
|
|
3
|
-
title: npm/rules/
|
|
4
|
-
resource: npm/rules/
|
|
3
|
+
title: npm/rules/style
|
|
4
|
+
resource: npm/rules/style/
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# npm/rules/
|
|
7
|
+
# npm/rules/style
|
|
8
8
|
|
|
9
9
|
| Файл | Тип |
|
|
10
10
|
| ------------------- | --------- |
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
type: Directory Index
|
|
3
|
-
title: npm/rules/style
|
|
4
|
-
resource: npm/rules/style
|
|
3
|
+
title: npm/rules/style/js
|
|
4
|
+
resource: npm/rules/style/js/
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# npm/rules/style
|
|
7
|
+
# npm/rules/style/js
|
|
8
8
|
|
|
9
9
|
| Файл | Тип |
|
|
10
10
|
| ------------------------- | --------- |
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
type: JS Module
|
|
3
3
|
title: tooling.mjs
|
|
4
|
-
resource: npm/rules/style
|
|
4
|
+
resource: npm/rules/style/js/tooling.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: bc2b0934
|
|
7
7
|
score: 80
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -18,7 +18,7 @@ docgen:
|
|
|
18
18
|
|
|
19
19
|
## Публічний API
|
|
20
20
|
|
|
21
|
-
check — Перевіряє відповідність проєкту правилам style
|
|
21
|
+
check — Перевіряє відповідність проєкту правилам style.mdc
|
|
22
22
|
|
|
23
23
|
## Гарантії поведінки
|
|
24
24
|
|
|
@@ -6,7 +6,7 @@ import { join } from 'node:path'
|
|
|
6
6
|
import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
|
|
7
7
|
|
|
8
8
|
// Зовнішні файли конфігу stylelint, які підхоплює cosmiconfig. Канон нових
|
|
9
|
-
// JS-конфігів — `.mjs`/`.cjs` (js
|
|
9
|
+
// JS-конфігів — `.mjs`/`.cjs` (js.mdc), legacy `.js` лишається валідним.
|
|
10
10
|
const STYLELINT_CONFIG_FILES = [
|
|
11
11
|
'.stylelintrc.json',
|
|
12
12
|
'.stylelintrc.js',
|
|
@@ -44,7 +44,7 @@ async function checkStylelintConfigPresence(reporter, cwd) {
|
|
|
44
44
|
// `npx @nitra/cursor fix`. JS-копії видалено, щоб не було двох джерел істини.
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
* Перевіряє відповідність проєкту правилам style
|
|
47
|
+
* Перевіряє відповідність проєкту правилам style.mdc
|
|
48
48
|
* @param {string} [cwd] корінь репозиторію
|
|
49
49
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
50
50
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Перевірка `lint-style.yml` (style
|
|
1
|
+
# Перевірка `lint-style.yml` (style.mdc).
|
|
2
2
|
#
|
|
3
3
|
# Канон надходить через --data: { "template": { "snippet": ... } }
|
|
4
4
|
# Структура --data сформована з template/lint-style.yml.snippet.yml.
|
|
@@ -23,7 +23,7 @@ all_run_text := concat("\n", [run_text |
|
|
|
23
23
|
deny contains msg if {
|
|
24
24
|
expected_run_blob != ""
|
|
25
25
|
not contains(all_run_text, expected_run_blob)
|
|
26
|
-
msg := sprintf("lint-style.yml: жоден крок run не містить %q (style
|
|
26
|
+
msg := sprintf("lint-style.yml: жоден крок run не містить %q (style.mdc)", [expected_run_blob])
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
step_run_to_text(step) := step.run if is_string(step.run)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Порт перевірок `package.json` (style
|
|
1
|
+
# Порт перевірок `package.json` (style.mdc).
|
|
2
2
|
#
|
|
3
3
|
# Канон надходить через --data: { "template": { "contains": ..., "snippet": ... } }
|
|
4
4
|
# Структура --data сформована з template/package.json.{contains,snippet}.json.
|
|
@@ -19,7 +19,7 @@ deny contains msg if {
|
|
|
19
19
|
some leaf_key, expected_value in expected_inner
|
|
20
20
|
actual := object.get(cfg, leaf_key, null)
|
|
21
21
|
actual != expected_value
|
|
22
|
-
msg := sprintf("package.json: %s.%s має бути %q (style
|
|
22
|
+
msg := sprintf("package.json: %s.%s має бути %q (style.mdc)", [section, leaf_key, expected_value])
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
# ── deny: @nitra/stylelint-config у devDependencies (inverse) ────────────
|
|
@@ -27,5 +27,5 @@ deny contains msg if {
|
|
|
27
27
|
deny contains msg if {
|
|
28
28
|
dev := object.get(input, "devDependencies", {})
|
|
29
29
|
not "@nitra/stylelint-config" in object.keys(dev)
|
|
30
|
-
msg := "@nitra/stylelint-config відсутній — bun add -d @nitra/stylelint-config (style
|
|
30
|
+
msg := "@nitra/stylelint-config відсутній — bun add -d @nitra/stylelint-config (style.mdc)"
|
|
31
31
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Перевірка `.vscode/extensions.json` для style
|
|
1
|
+
# Перевірка `.vscode/extensions.json` для style (style.mdc).
|
|
2
2
|
#
|
|
3
3
|
# Канон надходить через --data: { "template": { "snippet": ... } }
|
|
4
4
|
# Структура --data сформована з template/extensions.json.snippet.json.
|
|
@@ -9,5 +9,5 @@ import rego.v1
|
|
|
9
9
|
deny contains msg if {
|
|
10
10
|
some rec in data.template.snippet.recommendations
|
|
11
11
|
not rec in {r | some r in object.get(input, "recommendations", [])}
|
|
12
|
-
msg := sprintf(".vscode/extensions.json: recommendations має містити %q (style
|
|
12
|
+
msg := sprintf(".vscode/extensions.json: recommendations має містити %q (style.mdc)", [rec])
|
|
13
13
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Перевірка `.vscode/settings.json` для style
|
|
1
|
+
# Перевірка `.vscode/settings.json` для style (style.mdc).
|
|
2
2
|
#
|
|
3
3
|
# Канон надходить через --data: { "template": { "snippet": ... } }
|
|
4
4
|
# Структура --data сформована з template/settings.json.snippet.json.
|
|
@@ -11,5 +11,5 @@ deny contains msg if {
|
|
|
11
11
|
some key, expected_value in data.template.snippet
|
|
12
12
|
actual := object.get(input, key, null)
|
|
13
13
|
actual != expected_value
|
|
14
|
-
msg := sprintf(".vscode/settings.json: \"%s\" має бути %v (style
|
|
14
|
+
msg := sprintf(".vscode/settings.json: \"%s\" має бути %v (style.mdc)", [key, expected_value])
|
|
15
15
|
}
|
|
@@ -95,7 +95,7 @@ $white-a1: color.adjust(white, $alpha: -0.85);
|
|
|
95
95
|
|
|
96
96
|
- **Джерело правил:** перед тим як писати або суттєво змінювати **`.css`**, **`.scss`** або стилі в **`.vue`**, переглянь у корені проєкту (і в релевантних пакетах монорепо, якщо є) поле **`stylelint`** у **`package.json`** (зокрема `extends`), наявні **`.stylelintrc.*`**, **`stylelint.config.*`** та **`.stylelintignore`**. Не покладайся на «типові» правила stylelint з пам’яті — дотримуйся **проєктного** **`@nitra/stylelint-config`** і будь-яких локальних доповнень у репозиторії.
|
|
97
97
|
- **Форматування** узгоджуй з **`n-text.mdc`** (oxfmt / `.oxfmtrc.json` для css, scss тощо), щоб форматер і stylelint не суперечили один одному.
|
|
98
|
-
- **Запуск stylelint:** лише через **`n-cursor lint style
|
|
98
|
+
- **Запуск stylelint:** лише через **`n-cursor lint style`** (локально — з auto-fix; у CI — `--read-only`, нуль мутацій). Під капотом — `npx stylelint`; **не** використовуй **`bunx stylelint`**. Після змін запускай **`n-cursor lint style`** і виправляй усе, що лишилось після auto-fix; за потреби — повний прогін `n-cursor lint --full`.
|
|
99
99
|
- **Не розширюй винятки:** не додавай зайві **`stylelint-disable`** без потреби; краще підлаштувати стилі під правила проєкту.
|
|
100
100
|
|
|
101
101
|
## Канон
|
|
@@ -104,7 +104,7 @@ $white-a1: color.adjust(white, $alpha: -0.85);
|
|
|
104
104
|
|
|
105
105
|
- `stylelint.extends`: [package.json.snippet.json](./policy/package_json/template/package.json.snippet.json)
|
|
106
106
|
|
|
107
|
-
Окремого `lint-style` скрипта немає — запуск через **`n-cursor lint style
|
|
107
|
+
Окремого `lint-style` скрипта немає — запуск через **`n-cursor lint style`** (CI — `--read-only`).
|
|
108
108
|
|
|
109
109
|
### `.vscode/extensions.json`
|
|
110
110
|
|
|
@@ -129,7 +129,7 @@ $white-a1: color.adjust(white, $alpha: -0.85);
|
|
|
129
129
|
},
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
Додай **`.github/workflows/lint-style.yml`** (лише **`.yml`**, **`ga.mdc`**): після **`checkout`** — локальний composite **`setup-bun-deps`**, далі `n-cursor lint style
|
|
132
|
+
Додай **`.github/workflows/lint-style.yml`** (лише **`.yml`**, **`ga.mdc`**): після **`checkout`** — локальний composite **`setup-bun-deps`**, далі `n-cursor lint style --read-only` у кроці **`run`**. **Не** дублюй окремі кроки **`setup-node`** / **`oven-sh/setup-bun`** / кеш / **`npm install`**.
|
|
133
133
|
|
|
134
134
|
```yaml title=".github/workflows/lint-style.yml"
|
|
135
135
|
name: StyleLint
|
|
@@ -18,7 +18,7 @@ const STRYKER_VUE_PLUGIN_PATH = join(HERE, 'data', 'stryker_config', 'stryker-vu
|
|
|
18
18
|
const STRYKER_VUE_PLUGIN_FILENAME = 'stryker-vue-macros-ignorer.mjs'
|
|
19
19
|
const VITEST_BASELINE_PATH = join(HERE, 'data', 'vitest_config', 'vitest.config.baseline.js')
|
|
20
20
|
|
|
21
|
-
// Канонічна назва vitest-конфіга — `.mjs` (нові файли, js
|
|
21
|
+
// Канонічна назва vitest-конфіга — `.mjs` (нові файли, js.mdc); legacy
|
|
22
22
|
// `.js` лишається валідним. Перший знайдений виграє (.mjs пріоритетніший).
|
|
23
23
|
const VITEST_CONFIG_NAMES = ['vitest.config.mjs', 'vitest.config.js']
|
|
24
24
|
// Заміна literal `configFile` у скопійованому stryker-baseline на фактичне
|
|
@@ -332,14 +332,14 @@ export async function check(cwd = process.cwd()) {
|
|
|
332
332
|
const reporter = createCheckReporter()
|
|
333
333
|
const config = await readNCursorConfigLite(cwd)
|
|
334
334
|
|
|
335
|
-
// Self-gate: js
|
|
336
|
-
if (!config.rules.includes('js
|
|
335
|
+
// Self-gate: js має бути enabled
|
|
336
|
+
if (!config.rules.includes('js') || config.disableRules.includes('js')) {
|
|
337
337
|
return reporter.getExitCode()
|
|
338
338
|
}
|
|
339
339
|
|
|
340
340
|
const jsRoots = await resolveAllJsRoots(cwd)
|
|
341
341
|
if (jsRoots.length === 0) {
|
|
342
|
-
reporter.fail('test: js
|
|
342
|
+
reporter.fail('test: js enabled, але кореневий package.json не знайдено (test.mdc)')
|
|
343
343
|
return reporter.getExitCode()
|
|
344
344
|
}
|
|
345
345
|
|
|
@@ -8,7 +8,7 @@ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
|
|
|
8
8
|
/** Subтring-pattern: `pool: 'forks'` або `pool: "forks"` (з опційним whitespace). */
|
|
9
9
|
const POOL_FORKS_RE = /pool\s*:\s*['"]forks['"]/u
|
|
10
10
|
|
|
11
|
-
// Канонічна назва — `.mjs` (нові файли, js
|
|
11
|
+
// Канонічна назва — `.mjs` (нові файли, js.mdc), але legacy `.js` лишається
|
|
12
12
|
// валідним. Перший знайдений виграє: `.mjs` пріоритетніший.
|
|
13
13
|
const VITEST_CONFIG_NAMES = ['vitest.config.mjs', 'vitest.config.js']
|
|
14
14
|
|
package/rules/test/test.mdc
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: JS-тести (*.test.mjs) живуть у tests/. Правило `test` керує stryker.config.mjs + vitest.config.mjs (якщо js
|
|
2
|
+
description: JS-тести (*.test.mjs) живуть у tests/. Правило `test` керує stryker.config.mjs + vitest.config.mjs (якщо js enabled) і .cargo/mutants.toml (якщо rust enabled).
|
|
3
3
|
version: '2.8'
|
|
4
4
|
globs: "**/{.n-cursor.json,package.json,Cargo.toml,stryker.config.mjs,vitest.config.mjs,vitest.config.js,.cargo/mutants.toml},**/*.test.mjs"
|
|
5
5
|
alwaysApply: false
|
|
@@ -132,7 +132,7 @@ test.skipIf(env.STRYKER_MUTATOR_WORKER)('узгоджені з поточним
|
|
|
132
132
|
|
|
133
133
|
**Scoped-режим `--changed`:** `n-cursor coverage --changed` звужує scope до файлів, змінених від git merge-base поточної гілки з `main` або `origin/main`; якщо обидва refs відсутні — до робочого дерева vs HEAD. `git diff <base>` проти робочого дерева ловить committed і uncommitted однаково, тож результат не залежить від того, чи крок уже закомічено. Недосяжний `base` (rebase/force-update) — fail-closed (помилка, не тихий pass). JS-провайдер ганяє `vitest --changed <base>` (лише зачеплені тести) і Stryker `--mutate` по змінених production-файлах (тест-файли відкидаються); roots без змінених JS пропускаються. Rust-провайдер пропускається, якщо не змінено `.rs`/`Cargo.*` (інакше — повний crate-прогін; per-file scoping cargo-mutants — окремий крок). Порожній scope (нема релевантних змін) — pass. У changed-режимі `COVERAGE.md` **не** перезаписується (рішення гейту — лише exit-код) і LLM-класифікація не запускається. Швидкі gate-и викликають саме `coverage --changed`; повний coverage (увесь проєкт, запис `COVERAGE.md`) лишається для `bun run coverage` / `/n-coverage-fix`.
|
|
134
134
|
|
|
135
|
-
Провайдери живуть у `npm/rules/<rule>/coverage/coverage.mjs` (постачаються правилами мови/рантайму: `js
|
|
135
|
+
Провайдери живуть у `npm/rules/<rule>/coverage/coverage.mjs` (постачаються правилами мови/рантайму: `js`, `rust`, у майбутньому `python` тощо). Оркестратор — у `npm/rules/test/coverage/coverage.mjs`.
|
|
136
136
|
|
|
137
137
|
У `package.json` (корінь) має бути `scripts.coverage` із викликом `n-cursor coverage`:
|
|
138
138
|
|
|
@@ -144,7 +144,7 @@ test.skipIf(env.STRYKER_MUTATOR_WORKER)('узгоджені з поточним
|
|
|
144
144
|
|
|
145
145
|
## Налаштування mutation-testing
|
|
146
146
|
|
|
147
|
-
Якщо у `.n-cursor.json#rules` присутнє правило `js
|
|
147
|
+
Якщо у `.n-cursor.json#rules` присутнє правило `js` — правило `test` створює canonical baseline `stryker.config.mjs` + `vitest.config.mjs` у **кожному** JS-root проєкту: у кожному workspace з власним `package.json` (або в корені для single-package). У monorepo з `workspaces: ['app', 'scripts']` отримаєте `app/stryker.config.mjs` + `app/vitest.config.mjs` і `scripts/stryker.config.mjs` + `scripts/vitest.config.mjs`. Якщо у JS-root уже лежить legacy `vitest.config.js` — він лишається валідним, новий `.mjs` поряд не створюється, а `vitest.configFile` у скопійованому `stryker.config.mjs` приводиться до фактичного імені.
|
|
148
148
|
|
|
149
149
|
Канон Stryker config (Vitest runner + perTest): [stryker.config.baseline.mjs](./js/data/stryker_config/stryker.config.baseline.mjs)
|
|
150
150
|
|
|
@@ -202,7 +202,7 @@ Customization (mutate patterns, exclude rules, timeout) — відповідал
|
|
|
202
202
|
|
|
203
203
|
Якщо інше правило спеціалізує mutation-behavior — воно зобов'язане **доповнювати** існуючий `.cargo/mutants.toml` без дублювання (додавати лише відсутні ключі) і **не перетирати** ручні налаштування. Послідовний запуск `npx @nitra/cursor fix test` після `fix tauri` не має скидати tauri-tuning, і навпаки — повторний `fix tauri` не дублює секції.
|
|
204
204
|
|
|
205
|
-
Додатково: коли `js
|
|
205
|
+
Додатково: коли `js` enabled, концерн `stryker_config` без дублювання додає у кореневий `.gitignore` тест-патерни:
|
|
206
206
|
|
|
207
207
|
- `**/reports/stryker/` — увесь каталог Stryker-output-у (backup'и `tempDirName`, `mutation.json`, HTML/dashboard-репорти якщо додасте інші reporter-и).
|
|
208
208
|
- `**/coverage/` — весь output vitest v8 coverage (`lcov.info` + HTML `lcov-report/`). Ефемерний: регенерується кожним прогоном, фінальні метрики живуть у `COVERAGE.md`. Gitignore не заважає `n-cursor coverage` читати `lcov.info` у тому ж прогоні.
|
|
@@ -145,8 +145,8 @@ for (const rule of rules) {
|
|
|
145
145
|
```javascript
|
|
146
146
|
import { discoverOneRule } from './discover-checkable-rules.mjs'
|
|
147
147
|
|
|
148
|
-
const rule = await discoverOneRule('/abs/path/to/npm/rules/n-js
|
|
149
|
-
// rule.id === 'n-js
|
|
148
|
+
const rule = await discoverOneRule('/abs/path/to/npm/rules/n-js', 'n-js')
|
|
149
|
+
// rule.id === 'n-js'
|
|
150
150
|
// rule.jsConcerns — список JS-концернів у js/
|
|
151
151
|
// rule.policyConcerns — список policy-концернів у policy/
|
|
152
152
|
```
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Допоміжні функції для аналізу GitHub Actions workflow (`.yml`) після структурного розбору YAML.
|
|
3
3
|
*
|
|
4
|
-
* Використовується в check-ga, check-js
|
|
4
|
+
* Використовується в check-ga, check-js, check-text, check-style, check-npm-module замість
|
|
5
5
|
* пошуку підрядків у сирому тексті там, де важливі лише значення `uses:` та `run:` кроків.
|
|
6
6
|
*
|
|
7
7
|
* Для `run:` також виявляється shell-продовження рядка через `\\` перед переносом (антипатерн у ga.mdc).
|
|
@@ -41,7 +41,7 @@ export function formatDurationMs(ms) {
|
|
|
41
41
|
* ```
|
|
42
42
|
*
|
|
43
43
|
* Ширина колонки id вирівнюється під найдовший id у списку. Мінімальна ширина risk — 14
|
|
44
|
-
* (узгоджено з типовою довжиною заголовків `fix-js
|
|
44
|
+
* (узгоджено з типовою довжиною заголовків `fix-js` / `lint-security`).
|
|
45
45
|
* @param {string} title заголовок таблиці (наприклад, `Fix timing` або `Lint timing`)
|
|
46
46
|
* @param {TimingEntry[]} timings записи в порядку запуску — друкуються як є, не сортуються
|
|
47
47
|
* @returns {string} готовий до stdout текст з кінцевим `\n`
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Копіює composite GitHub Action `setup-bun-deps` з установленого пакету `@nitra/cursor`
|
|
3
3
|
* у цільовий репозиторій (`.github/actions/setup-bun-deps/action.yml`).
|
|
4
4
|
*
|
|
5
|
-
* Використовується CLI `npx \@nitra/cursor`, щоб workflows з правил `ga` / `js
|
|
5
|
+
* Використовується CLI `npx \@nitra/cursor`, щоб workflows з правил `ga` / `js` / `text`
|
|
6
6
|
* могли одразу викликати `uses: ./.github/actions/setup-bun-deps` після кроку `actions/checkout@v6` (без checkout runner не знайде action.yml).
|
|
7
7
|
*
|
|
8
8
|
* Джерело: каталог `github-actions/setup-bun-deps/` у корені tarball пакету (поруч із `mdc/`, `bin/`).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Резолвить корінь JS-коду в проєкті: для workspace-projects — перший workspace
|
|
3
3
|
* (з підтримкою glob-патернів типу `cf/*`), для single-package — корінь cwd.
|
|
4
|
-
* Спільна утиліта для coverage-провайдера js
|
|
4
|
+
* Спільна утиліта для coverage-провайдера js і test-концерну stryker_config (DRY).
|
|
5
5
|
*/
|
|
6
6
|
import { existsSync } from 'node:fs'
|
|
7
7
|
import { glob, readFile } from 'node:fs/promises'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{ "auto": ["js
|
|
1
|
+
{ "auto": ["js"], "worktree": true }
|
package/skills/lint/SKILL.md
CHANGED
|
@@ -52,7 +52,7 @@ bun run lint
|
|
|
52
52
|
| **oxlint / ESLint** | `.oxlintrc.json` → `ignorePatterns`; `eslint.config.js` → `ignores`; `eslint-disable` / `oxlint-disable` у коді |
|
|
53
53
|
| **інше** | `.v8rignore`, `.stylelintignore`, `.trufflehog-exclude`, розширення `ignores` у workflow-конфігах |
|
|
54
54
|
|
|
55
|
-
Політика узгоджена з **`.cursor/rules/`** (зокрема **n-js
|
|
55
|
+
Політика узгоджена з **`.cursor/rules/`** (зокрема **n-js**, **n-text**): виняток допустимий лише з **обґрунтованою** причиною, не як заміна рефакторингу для справжніх клонів / дублікатів.
|
|
56
56
|
|
|
57
57
|
### Коли обовʼязково питати користувача
|
|
58
58
|
|
|
@@ -109,7 +109,7 @@ bun run lint
|
|
|
109
109
|
- **oxlint**: **`--threads=1`**, якщо потрібно зменшити навантаження на CPU.
|
|
110
110
|
- **ESLint cache**: **`--cache`** / **`--cache-location .eslintcache`** — менше повторного читання з диска.
|
|
111
111
|
|
|
112
|
-
Канонічний рядок **`lint-js`** у репозиторіях з **`check js
|
|
112
|
+
Канонічний рядок **`lint-js`** у репозиторіях з **`check js`** фіксований; додаткові прапорці — з узгодженням канону або в споживацькому проєкті окремо.
|
|
113
113
|
|
|
114
114
|
## Примітка
|
|
115
115
|
|
|
@@ -242,7 +242,7 @@ description: >-
|
|
|
242
242
|
|
|
243
243
|
# Обмеження
|
|
244
244
|
|
|
245
|
-
- Дотриматись `.cursor/rules/n-js
|
|
245
|
+
- Дотриматись `.cursor/rules/n-js.mdc` і `.cursor/rules/n-changelog.mdc`
|
|
246
246
|
(зміни у workspace = change-файл, не ручний CHANGELOG/version bump).
|
|
247
247
|
- Якщо `eslint ^9` офіційно не підтримує Node 25 — підняти peer range.
|
|
248
248
|
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: JS Module
|
|
3
|
-
title: main.mjs
|
|
4
|
-
resource: npm/rules/js-lint-ci/main.mjs
|
|
5
|
-
docgen:
|
|
6
|
-
crc: 59cd2fd3
|
|
7
|
-
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score: 100
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Огляд
|
|
12
|
-
|
|
13
|
-
Модуль надає інструменти для глибокого аналізу коду. Функція `run` виконує перевірку коду відповідно до заданого контексту прогону. Функція `lint` порівнює файли репозиторію для виявлення дублікатів та неіспользумого коду.
|
|
14
|
-
|
|
15
|
-
## Поведінка
|
|
16
|
-
|
|
17
|
-
run виконує перевірку на основі контексту прогону.
|
|
18
|
-
lint виконує крос-файловий аналіз репозиторію на дублікати та мертвий код.
|
|
19
|
-
|
|
20
|
-
## Публічний API
|
|
21
|
-
|
|
22
|
-
run — точка входу для виконання правила, що включає перевірку логіки застосування (JS-занепокложення $\to$ політика $\to$ посилання MDC) та аналіз коду (jscpd + knip) по всьому репозиторію.
|
|
23
|
-
lint — аналіз коду по всьому репозиторію на наявність дублікатів (jscpd) та мертвого коду (knip).
|
|
24
|
-
|
|
25
|
-
## Гарантії поведінки
|
|
26
|
-
|
|
27
|
-
- Read-only: не виконує операцій запису (ФС/БД).
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Крос-файловий ci-етап js-lint — jscpd (детектор клонів) і knip (невикористані експорти). Лише у `lint --full`, по всьому репо.
|
|
3
|
-
globs: "**/{.oxlintrc.json,eslint.config.js,.jscpd.json,knip.json,package.json},**/*.{js,mjs,cjs,jsx,ts,tsx}"
|
|
4
|
-
alwaysApply: false
|
|
5
|
-
version: '1.0'
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# js-lint-ci — крос-файловий ci-етап
|
|
9
|
-
|
|
10
|
-
`jscpd` і `knip` аналізують увесь граф проєкту, тож мають сенс лише у повному прогоні
|
|
11
|
-
`npx @nitra/cursor lint --full` (CI: `lint --read-only --full`) — не у швидкому `lint` по змінених файлах. Per-file режиму нема.
|
|
12
|
-
|
|
13
|
-
Швидкий етап js-lint (oxlint/eslint) — у правилі `js-lint` (`lint: per-file`).
|
|
14
|
-
|
|
15
|
-
## Залежнісна політика (що не додавати)
|
|
16
|
-
|
|
17
|
-
Залежнісний аналіз — крос-файлова зона цього ci-етапу, тож і політика «що не додавати в залежності» живе тут.
|
|
18
|
-
|
|
19
|
-
`@e18e/eslint-plugin` окремо не додавай — він уже в залежностях `@nitra/eslint-config` (з **3.8.0**), oxlint підвантажує його з `node_modules`. Пакети oxlint/eslint/jscpd/knip теж не додавай у `devDependencies` без потреби монорепо — `bunx` тягне їх ad-hoc.
|
|
20
|
-
|
|
21
|
-
## knip
|
|
22
|
-
|
|
23
|
-
Перевірку невикористаних залежностей і експортів виконує **knip** (заміна `depcheck`). Викликається у скрипті `lint-js` і в CI разом з oxlint/eslint/jscpd — окремий крок у CI не потрібен.
|
|
24
|
-
|
|
25
|
-
У корені проєкту має бути **`knip.json`**, який стартує з канонічного baseline з пакета `@nitra/cursor` — файл [`npm/rules/js-lint/js/tooling/knip-canonical.json`](../js-lint/js/tooling/knip-canonical.json). Він покриває типові false-positives для наших правил: `entry` зі CLI-конфігами (eslint, stylelint, oxlint, jscpd, markdownlint-cli2, `commitlint`), `project` для `**/*.{js,mjs,cjs,jsx,ts,tsx,mts,cts}`, `ignore` для `**/__fixtures__/**`, `ignoreDependencies` для пакетів, посилання на які є лише в не-JS-конфігах (`@nitra/cspell-dict`, `/@cspell\/dict-.+/`, `graphql`), і `ignoreBinaries` для CLI, які канон вимагає викликати через `npx`/`bunx` і яких заборонено додавати в `devDependencies` (`actionlint`, `cspell`, `eslint`, `git-ai`, `jscpd`, `markdownlint-cli2`, `oxfmt`, `oxlint`, `shellcheck`, `uvx`, `v8r`, `zizmor`).
|
|
26
|
-
|
|
27
|
-
Якщо `knip.json` відсутній — `npx @nitra/cursor fix js-lint` копіює канон у корінь проєкту (side effect). Після створення модифікуй файл під свій проєкт як завгодно: перевіряємо лише наявність, зміст подальших змін не валідується.
|
|
28
|
-
|
|
29
|
-
Пакет `knip` окремо в `devDependencies` не додавай — `bunx knip` тягне його ad-hoc, як oxlint/eslint/jscpd.
|
|
30
|
-
|
|
31
|
-
## Заборона `@nitra/as-integrations-fastify`
|
|
32
|
-
|
|
33
|
-
Пакет **`@nitra/as-integrations-fastify`** заборонений у **`dependencies`**, **`peerDependencies`** та в import-specifier-ах. Це чистий републіш upstream, застряглий на peer `@apollo/server: "^4.0.0"`, тож на Apollo 5 `bun install` дає `warn: incorrect peer dependency`. Заміна — upstream **`@as-integrations/fastify`** (**`^3.1.0`**): peer `@apollo/server: "^4.0.0 || ^5.0.0"` + `fastify: "^5.3.0"`.
|
|
34
|
-
|
|
35
|
-
Як і knip, це крос-файлова dependency-політика ci-етапу, а не per-file перевірка — інваріант «per-file режиму нема» зберігається.
|
|
36
|
-
|
|
37
|
-
Міграція — лише specifier у трьох місцях: залежність у `package.json`, `import`, та `vi.mock(...)` / `await import(...)` у тестах. **Код не міняється**, експорти ті самі: `default` → `fastifyApollo`, named → `fastifyApolloDrainPlugin`.
|
|
38
|
-
|
|
39
|
-
```javascript title="❌ до"
|
|
40
|
-
import fastifyApollo, { fastifyApolloDrainPlugin } from '@nitra/as-integrations-fastify'
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
```javascript title="✅ після"
|
|
44
|
-
import fastifyApollo, { fastifyApolloDrainPlugin } from '@as-integrations/fastify'
|
|
45
|
-
```
|