@nitra/cursor 1.8.95 → 1.8.97
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/mdc/js-lint.mdc +14 -3
- package/package.json +1 -1
- package/scripts/check-js-lint.mjs +85 -4
- package/skills/lint/SKILL.md +10 -0
package/mdc/js-lint.mdc
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Перевірка JavaScript коду
|
|
3
3
|
alwaysApply: true
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.13'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
**oxlint**, **ESLint**, **jscpd**. У скрипті **`lint-js`** і в CI — **`bunx oxlint`**, **`bunx eslint`**, **`bunx jscpd`** (у CI без **`--fix`** для oxlint/eslint — див. приклад workflow нижче). Без **prettier** і **@nitra/prettier-config**.
|
|
7
|
+
**oxlint**, **ESLint**, **jscpd**. У скрипті **`lint-js`** і в CI — **`bunx oxlint`**, **`bunx eslint`**, **`bunx jscpd`** (у CI без **`--fix`** для oxlint/eslint — див. приклад workflow нижче). Без **prettier** і **@nitra/prettier-config**. У **`devDependencies`** має бути **`@nitra/eslint-config` мінімум `^3.5.0`** (з ним транзитивно йде **`@e18e/eslint-plugin`** для oxlint); пакет **`@e18e/eslint-plugin`** окремо не додавай. Пакети oxlint/eslint/jscpd не додавай без потреби монорепо.
|
|
8
8
|
|
|
9
9
|
```json title=".vscode/extensions.json"
|
|
10
10
|
{
|
|
@@ -22,7 +22,7 @@ version: '1.11'
|
|
|
22
22
|
"lint-js": "bunx oxlint --fix && bunx eslint --fix . && bunx jscpd ."
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@nitra/eslint-config": "^3.
|
|
25
|
+
"@nitra/eslint-config": "^3.5.0"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
28
28
|
"node": ">=24"
|
|
@@ -30,6 +30,17 @@ version: '1.11'
|
|
|
30
30
|
}
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
У корені має бути **`.oxlintrc.json`** з підключенням **`@e18e/eslint-plugin`** через **`jsPlugins`** і правилом **`e18e/prefer-includes`** зі значенням **`error`**. Модуль **`@e18e/eslint-plugin`** не оголошуй окремо в **`package.json`** — він уже в залежностях **`@nitra/eslint-config`** (з **3.5.0**), oxlint резолвить його з **`node_modules`**.
|
|
34
|
+
|
|
35
|
+
```json title=".oxlintrc.json (фрагмент)"
|
|
36
|
+
{
|
|
37
|
+
"jsPlugins": ["@e18e/eslint-plugin"],
|
|
38
|
+
"rules": {
|
|
39
|
+
"e18e/prefer-includes": "error"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
33
44
|
У корені проєкту має бути `.jscpd.json`. Мінімум: увімкнути облік `.gitignore`, ненульовий код виходу при знаходженні клонів, консольний звіт. За потреби додай `ignore` (дзеркальні каталоги, шаблони) та `minLines`, щоб відсікти дрібні збіги:
|
|
34
45
|
|
|
35
46
|
```json title=".jscpd.json"
|
package/package.json
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Перевіряє лінт JavaScript за правилом js-lint.mdc.
|
|
3
3
|
*
|
|
4
4
|
* Канонічний `lint-js`, flat ESLint з getConfig і ignore для auto-imports, рекомендації VSCode,
|
|
5
|
-
* `.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* `.oxlintrc.json` з `jsPlugins` (`@e18e/eslint-plugin`) і правилом `e18e/prefer-includes: error`,
|
|
6
|
+
* `@nitra/eslint-config` у devDependencies мінімум **3.5.0** (транзитивний `@e18e/eslint-plugin` для oxlint), `.jscpd.json`
|
|
7
|
+
* (gitignore, exitCode, reporters, minLines), workflow `lint-js.yml` (checkout@v6, setup-bun-deps,
|
|
8
|
+
* bunx без --fix), без prettier, `engines.node` >= 24. Дубль перевірки JS у `lint.yml` — заборонено.
|
|
8
9
|
*/
|
|
9
10
|
import { existsSync } from 'node:fs'
|
|
10
11
|
import { readFile } from 'node:fs/promises'
|
|
@@ -36,6 +37,55 @@ export function isCanonicalLintJs(script) {
|
|
|
36
37
|
return normalizeLintJsScript(script) === CANONICAL_LINT_JS
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Чи діапазон `@nitra/eslint-config` у `package.json` передбачає версію з транзитивним `@e18e/eslint-plugin` (>=3.5.0).
|
|
42
|
+
*
|
|
43
|
+
* @param {unknown} versionSpec значення `devDependencies['@nitra/eslint-config']`
|
|
44
|
+
* @returns {boolean} true для `workspace:*` або першої semver у рядку >= 3.5.0
|
|
45
|
+
*/
|
|
46
|
+
export function nitraEslintConfigDeclaresE18eTransitive(versionSpec) {
|
|
47
|
+
const s = String(versionSpec).trim()
|
|
48
|
+
if (s.startsWith('workspace:')) {
|
|
49
|
+
return true
|
|
50
|
+
}
|
|
51
|
+
const m = s.match(/(\d+)\.(\d+)\.(\d+)/u)
|
|
52
|
+
if (!m) {
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
const major = Number(m[1])
|
|
56
|
+
const minor = Number(m[2])
|
|
57
|
+
const patch = Number(m[3])
|
|
58
|
+
return major > 3 || (major === 3 && minor > 5) || (major === 3 && minor === 5 && patch >= 0)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Перевіряє обов’язкові поля `.oxlintrc.json` для плагіна e18e (js-lint.mdc).
|
|
63
|
+
*
|
|
64
|
+
* @param {unknown} cfg корінь JSON-конфігурації oxlint
|
|
65
|
+
* @returns {{ ok: boolean, failures: string[] }} `ok` і перелік повідомлень для `fail`
|
|
66
|
+
*/
|
|
67
|
+
export function verifyOxlintRcE18e(cfg) {
|
|
68
|
+
const failures = []
|
|
69
|
+
if (!cfg || typeof cfg !== 'object' || Array.isArray(cfg)) {
|
|
70
|
+
return { ok: false, failures: ['.oxlintrc.json: корінь має бути об’єктом'] }
|
|
71
|
+
}
|
|
72
|
+
const o = /** @type {Record<string, unknown>} */ (cfg)
|
|
73
|
+
const jsPlugins = o.jsPlugins
|
|
74
|
+
if (!Array.isArray(jsPlugins) || !jsPlugins.includes('@e18e/eslint-plugin')) {
|
|
75
|
+
failures.push('.oxlintrc.json: jsPlugins має містити "@e18e/eslint-plugin"')
|
|
76
|
+
}
|
|
77
|
+
const rules = o.rules
|
|
78
|
+
if (!rules || typeof rules !== 'object' || Array.isArray(rules)) {
|
|
79
|
+
failures.push('.oxlintrc.json: поле rules має бути об’єктом')
|
|
80
|
+
} else {
|
|
81
|
+
const r = /** @type {Record<string, unknown>} */ (rules)
|
|
82
|
+
if (r['e18e/prefer-includes'] !== 'error') {
|
|
83
|
+
failures.push('.oxlintrc.json: у rules має бути "e18e/prefer-includes": "error"')
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { ok: failures.length === 0, failures }
|
|
87
|
+
}
|
|
88
|
+
|
|
39
89
|
/**
|
|
40
90
|
* Перевіряє відповідність проєкту правилам js-lint.mdc
|
|
41
91
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
@@ -103,8 +153,16 @@ export async function check() {
|
|
|
103
153
|
pass('package.json не містить @nitra/prettier-config')
|
|
104
154
|
}
|
|
105
155
|
|
|
106
|
-
|
|
156
|
+
const nitraEslint = pkg.devDependencies?.['@nitra/eslint-config']
|
|
157
|
+
if (nitraEslint) {
|
|
107
158
|
pass('@nitra/eslint-config є в devDependencies')
|
|
159
|
+
if (nitraEslintConfigDeclaresE18eTransitive(nitraEslint)) {
|
|
160
|
+
pass('@nitra/eslint-config: мінімум 3.5.0 (транзитивний @e18e/eslint-plugin для oxlint jsPlugins, js-lint.mdc)')
|
|
161
|
+
} else {
|
|
162
|
+
fail(
|
|
163
|
+
'@nitra/eslint-config: онови до мінімум "^3.5.0" — з цієї версії постачається @e18e/eslint-plugin для .oxlintrc.json (js-lint.mdc)'
|
|
164
|
+
)
|
|
165
|
+
}
|
|
108
166
|
} else {
|
|
109
167
|
fail('@nitra/eslint-config відсутній в devDependencies — додай: bun add -d @nitra/eslint-config')
|
|
110
168
|
}
|
|
@@ -122,6 +180,29 @@ export async function check() {
|
|
|
122
180
|
}
|
|
123
181
|
}
|
|
124
182
|
|
|
183
|
+
if (existsSync('.oxlintrc.json')) {
|
|
184
|
+
let oxCfg
|
|
185
|
+
try {
|
|
186
|
+
oxCfg = JSON.parse(await readFile('.oxlintrc.json', 'utf8'))
|
|
187
|
+
} catch {
|
|
188
|
+
fail('.oxlintrc.json не є валідним JSON')
|
|
189
|
+
oxCfg = null
|
|
190
|
+
}
|
|
191
|
+
if (oxCfg !== null) {
|
|
192
|
+
pass('.oxlintrc.json існує')
|
|
193
|
+
const oxV = verifyOxlintRcE18e(oxCfg)
|
|
194
|
+
if (oxV.ok) {
|
|
195
|
+
pass('.oxlintrc.json: jsPlugins з @e18e/eslint-plugin і e18e/prefer-includes: error')
|
|
196
|
+
} else {
|
|
197
|
+
for (const msg of oxV.failures) {
|
|
198
|
+
fail(msg)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
fail('.oxlintrc.json не існує — додай конфіг oxlint (js-lint.mdc)')
|
|
204
|
+
}
|
|
205
|
+
|
|
125
206
|
if (existsSync('.vscode/extensions.json')) {
|
|
126
207
|
let ext
|
|
127
208
|
try {
|
package/skills/lint/SKILL.md
CHANGED
|
@@ -26,6 +26,7 @@ bun run lint
|
|
|
26
26
|
2. **Якщо exit code не 0** — проаналізуй вивід (останній упавший крок у ланцюжку **`lint`** часто видно з stderr / логів):
|
|
27
27
|
- Де скрипт уже робить **auto-fix** (**`--fix`**, **`markdownlint-cli2 --fix`**, **`oxfmt`** тощо) — перезапусти **`bun run lint`** після змін файлів.
|
|
28
28
|
- Де auto-fix **немає** (наприклад, **jscpd**, **cspell**, **zizmor**, перевірки без прапорця fix) — виправ код, конфіги або винятки **узгоджено з** `.cursor/rules/` (не розширюй ignore лише щоб приховати проблему без причини).
|
|
29
|
+
- Якщо спрацьовує **`sonarjs/cognitive-complexity`** — див. окремий блок нижче.
|
|
29
30
|
|
|
30
31
|
3. **Цикл** — повторюй кроки 1–2, доки **`bun run lint`** не завершиться успішно. Після суттєвих правок за потреби ще раз **`bun run lint`**, щоб переконатися, що не зламав наступний крок у скрипті **`lint`**.
|
|
31
32
|
|
|
@@ -37,6 +38,15 @@ bun run lint
|
|
|
37
38
|
|
|
38
39
|
5. **Результат** — коротко опиши, що саме виправлено; якщо щось блокує нульовий exit code — залиш чітке пояснення й наступні кроки для людини.
|
|
39
40
|
|
|
41
|
+
## sonarjs/cognitive-complexity
|
|
42
|
+
|
|
43
|
+
- **Не** додавай **`eslint-disable`** (у т.ч. на **`sonarjs/cognitive-complexity`**) чи інші коментарі-винятки лише щоб приховати порушення — потрібен саме **рефакторинг коду**, щоб зменшити **cognitive complexity**.
|
|
44
|
+
- **Перед будь-яким рефакторингом** перевір, чи є **тести**, які покривають змінювану поведінку:
|
|
45
|
+
- **unit** — **`bun test`** (або скрипт тестів у відповідному пакеті репозиторію);
|
|
46
|
+
- **e2e** — **Playwright**, якщо в проєкті він використовується для UI/потоків.
|
|
47
|
+
- Якщо тестів **немає** або вони **не покривають** блок, який змінюєш — **спочатку** додай/розшир тести, переконайся, що вони стабільно проходять, **потім** роби рефакторинг, **потім** знову прогони тести й **`bun run lint`**, щоб підтвердити, що функціональність коректна й лінт чистий.
|
|
48
|
+
- Якщо після рефакторингу тести або лінт падають — **не** залишай «половинчастий» рефакторинг: відкотись або доведи зміни до зеленого стану.
|
|
49
|
+
|
|
40
50
|
## Примітка
|
|
41
51
|
|
|
42
52
|
Цей скіл **не** замінює **`npx @nitra/cursor check`**: **`lint`** перевіряє лінтери/формат у **`package.json`**, а **`check`** — програмні правила пакета **`@nitra/cursor`**. За потреби запускай обидва.
|