@nitra/cursor 12.6.1 → 12.7.0
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/.claude-template/settings.template.json +1 -1
- package/CHANGELOG.md +10 -0
- package/bin/docs/n-cursor.md +4 -20
- package/bin/n-cursor.js +7 -53
- package/docs/stryker.config.md +20 -28
- package/package.json +1 -1
- package/rules/abie/docs/index.md +1 -0
- package/rules/abie/docs/main.md +29 -0
- package/rules/abie/{fix.mjs → main.mjs} +5 -3
- package/rules/adr/docs/index.md +1 -0
- package/rules/adr/docs/main.md +29 -0
- package/rules/adr/{fix.mjs → main.mjs} +5 -3
- package/rules/bun/docs/index.md +1 -0
- package/rules/bun/docs/main.md +30 -0
- package/rules/bun/js/docs/layout.md +11 -36
- package/rules/bun/{fix.mjs → main.mjs} +5 -3
- package/rules/capacitor/docs/index.md +1 -0
- package/rules/capacitor/docs/main.md +29 -0
- package/rules/capacitor/{fix.mjs → main.mjs} +5 -3
- package/rules/changelog/docs/index.md +1 -0
- package/rules/changelog/docs/main.md +27 -0
- package/rules/changelog/main.mjs +20 -0
- package/rules/ci4/docs/index.md +1 -0
- package/rules/ci4/docs/main.md +30 -0
- package/rules/ci4/main.mjs +20 -0
- package/rules/doc-files/docs/index.md +1 -0
- package/rules/doc-files/docs/main.md +31 -0
- package/rules/doc-files/js/docgen-files-batch.mjs +47 -3
- package/rules/doc-files/js/docgen-scan.mjs +89 -9
- package/rules/doc-files/js/docs/docgen-files-batch.md +15 -15
- package/rules/doc-files/js/docs/docgen-scan.md +34 -34
- package/rules/doc-files/js/docs/index.md +1 -0
- package/rules/doc-files/js/docs/run-lint.md +27 -0
- package/rules/doc-files/{lint/lint.mjs → js/run-lint.mjs} +23 -9
- package/rules/doc-files/{js/lint.mjs → main.mjs} +60 -10
- package/rules/docker/docs/index.md +1 -0
- package/rules/docker/docs/main.md +28 -0
- package/rules/docker/js/docs/lint.md +26 -54
- package/rules/docker/js/lint.mjs +11 -0
- package/rules/docker/lib/docker-hadolint.mjs +1 -1
- package/rules/docker/lib/docs/docker-hadolint.md +16 -173
- package/rules/docker/main.mjs +20 -0
- package/rules/efes/docs/index.md +1 -0
- package/rules/efes/docs/main.md +29 -0
- package/rules/efes/main.mjs +20 -0
- package/rules/feedback/docs/index.md +1 -0
- package/rules/feedback/docs/main.md +30 -0
- package/rules/feedback/main.mjs +20 -0
- package/rules/ga/docs/index.md +1 -0
- package/rules/ga/docs/main.md +29 -0
- package/rules/ga/{lint/lint.mjs → main.mjs} +36 -10
- package/rules/graphql/docs/index.md +1 -0
- package/rules/graphql/docs/main.md +36 -0
- package/rules/graphql/main.mjs +20 -0
- package/rules/hasura/docs/index.md +1 -0
- package/rules/hasura/docs/main.md +30 -0
- package/rules/hasura/main.mjs +20 -0
- package/rules/image-avif/docs/index.md +1 -0
- package/rules/image-avif/docs/main.md +30 -0
- package/rules/image-avif/js/docs/avif_generation.md +20 -233
- package/rules/image-avif/main.mjs +20 -0
- package/rules/image-compress/docs/index.md +1 -0
- package/rules/image-compress/docs/main.md +29 -0
- package/rules/image-compress/js/docs/package_setup.md +12 -11
- package/rules/image-compress/{js/lint.mjs → main.mjs} +21 -5
- package/rules/js-bun-db/docs/index.md +1 -0
- package/rules/js-bun-db/docs/main.md +30 -0
- package/rules/js-bun-db/main.mjs +20 -0
- package/rules/js-bun-redis/docs/index.md +1 -0
- package/rules/js-bun-redis/docs/main.md +29 -0
- package/rules/js-bun-redis/main.mjs +20 -0
- package/rules/js-lint/docs/index.md +1 -0
- package/rules/js-lint/docs/main.md +29 -0
- package/rules/js-lint/js/check.mjs +268 -0
- package/rules/js-lint/js/docs/check.md +39 -0
- package/rules/js-lint/js/docs/index.md +1 -1
- package/rules/js-lint/js/docs/tooling.md +12 -32
- package/rules/js-lint/js/tooling.mjs +1 -265
- package/rules/js-lint/{js/lint.mjs → main.mjs} +19 -2
- package/rules/js-lint-ci/docs/index.md +1 -0
- package/rules/js-lint-ci/docs/main.md +27 -0
- package/rules/js-lint-ci/main.mjs +33 -0
- package/rules/js-mssql/docs/index.md +1 -0
- package/rules/js-mssql/docs/main.md +30 -0
- package/rules/js-mssql/main.mjs +20 -0
- package/rules/js-run/docs/index.md +1 -0
- package/rules/js-run/docs/main.md +30 -0
- package/rules/js-run/main.mjs +20 -0
- package/rules/k8s/docs/index.md +1 -0
- package/rules/k8s/docs/main.md +40 -0
- package/rules/k8s/js/docs/index.md +12 -0
- package/rules/k8s/{lint/lint.mjs → main.mjs} +32 -10
- package/rules/nginx-default-tpl/docs/index.md +1 -0
- package/rules/nginx-default-tpl/docs/main.md +30 -0
- package/rules/nginx-default-tpl/main.mjs +20 -0
- package/rules/npm-module/docs/index.md +1 -0
- package/rules/npm-module/docs/main.md +29 -0
- package/rules/npm-module/js/docs/rule_meta.md +17 -16
- package/rules/npm-module/js/rule_meta.mjs +13 -3
- package/rules/npm-module/main.mjs +20 -0
- package/rules/php/docs/index.md +1 -0
- package/rules/php/docs/main.md +33 -0
- package/rules/php/js/docs/tooling.md +10 -10
- package/rules/php/{lint/lint.mjs → main.mjs} +32 -6
- package/rules/python/docs/index.md +1 -0
- package/rules/python/docs/main.md +31 -0
- package/rules/python/js/docs/tooling.md +17 -17
- package/rules/python/{lint/lint.mjs → main.mjs} +29 -5
- package/rules/rego/docs/index.md +1 -0
- package/rules/rego/docs/main.md +37 -0
- package/rules/rego/{lint/lint.mjs → main.mjs} +27 -5
- package/rules/release/docs/index.md +1 -0
- package/rules/release/docs/main.md +29 -0
- package/rules/release/docs/release.md +0 -3
- package/rules/release/release.mdc +10 -0
- package/rules/rust/docs/index.md +1 -0
- package/rules/rust/docs/main.md +27 -0
- package/rules/rust/{js/lint.mjs → main.mjs} +20 -3
- package/rules/security/docs/index.md +1 -0
- package/rules/security/docs/main.md +28 -0
- package/rules/security/main.mjs +45 -0
- package/rules/style-lint/docs/index.md +1 -0
- package/rules/style-lint/docs/main.md +29 -0
- package/rules/style-lint/{js/lint.mjs → main.mjs} +19 -1
- package/rules/tauri/docs/index.md +1 -0
- package/rules/tauri/docs/main.md +29 -0
- package/rules/tauri/main.mjs +20 -0
- package/rules/test/docs/index.md +1 -0
- package/rules/test/docs/main.md +30 -0
- package/rules/test/main.mjs +20 -0
- package/rules/text/docs/index.md +1 -0
- package/rules/text/docs/main.md +29 -0
- package/rules/text/js/docs/cspell-fix.md +30 -0
- package/rules/text/js/docs/formatting.md +12 -45
- package/rules/text/js/docs/index.md +4 -0
- package/rules/text/js/docs/run-dotenv-linter.md +31 -0
- package/rules/text/js/docs/run-shellcheck.md +28 -0
- package/rules/text/js/docs/run-v8r.md +29 -0
- package/rules/text/{lint/lint.mjs → main.mjs} +38 -9
- package/rules/tool-surface/docs/index.md +1 -0
- package/rules/tool-surface/docs/main.md +29 -0
- package/rules/tool-surface/main.mjs +20 -0
- package/rules/vue/docs/index.md +1 -0
- package/rules/vue/docs/main.md +29 -0
- package/rules/vue/main.mjs +20 -0
- package/rules/worktree/docs/index.md +1 -0
- package/rules/worktree/docs/main.md +28 -0
- package/rules/worktree/main.mjs +20 -0
- package/scripts/docs/index.md +1 -0
- package/scripts/docs/post-tool-use-check.md +29 -0
- package/scripts/docs/sync-claude-config.md +64 -92
- package/scripts/lib/adr/docs/normalize-cli.md +0 -3
- package/scripts/lib/adr/docs/normalize-pipeline.md +0 -3
- package/scripts/lib/docs/gha-workflow.md +25 -317
- package/scripts/lib/docs/index.md +1 -0
- package/scripts/lib/docs/list-project-rules-mdc.md +5 -4
- package/scripts/lib/docs/list-rule-ids.md +15 -148
- package/scripts/lib/docs/read-n-cursor-config-lite.md +12 -16
- package/scripts/lib/docs/run-lint-step.md +13 -13
- package/scripts/lib/docs/run-lint.md +30 -0
- package/scripts/lib/docs/run-rule-cli.md +14 -10
- package/scripts/lib/docs/run-standard-lint.md +27 -10
- package/scripts/lib/docs/run-standard-rule.md +12 -11
- package/scripts/lib/docs/timing-summary.md +11 -12
- package/scripts/lib/docs/worktree-notice.md +0 -3
- package/scripts/lib/fix/docs/index.md +1 -0
- package/scripts/lib/fix/docs/orchestrator.md +23 -18
- package/scripts/lib/fix/docs/run-conformance-check.md +32 -0
- package/scripts/lib/fix/docs/t0.md +10 -9
- package/scripts/lib/fix/orchestrator.mjs +5 -5
- package/scripts/lib/fix/{run-fix-check.mjs → run-conformance-check.mjs} +13 -13
- package/scripts/lib/fix/t0.mjs +3 -3
- package/scripts/lib/list-project-rules-mdc.mjs +1 -1
- package/scripts/lib/list-rule-ids.mjs +12 -3
- package/scripts/lib/read-n-cursor-config-lite.mjs +2 -2
- package/{rules/lint/js/orchestrate.mjs → scripts/lib/run-lint.mjs} +42 -20
- package/scripts/lib/run-rule-cli.mjs +4 -4
- package/scripts/lib/run-standard-lint.mjs +19 -6
- package/scripts/lib/run-standard-rule.mjs +4 -4
- package/scripts/lib/timing-summary.mjs +1 -1
- package/scripts/{post-tool-use-fix.mjs → post-tool-use-check.mjs} +9 -9
- package/scripts/sync-claude-config.mjs +2 -2
- package/rules/changelog/fix.mjs +0 -18
- package/rules/ci4/fix.mjs +0 -18
- package/rules/doc-files/fix.mjs +0 -19
- package/rules/doc-files/js/docs/lint.md +0 -34
- package/rules/doc-files/lint/docs/index.md +0 -11
- package/rules/doc-files/lint/docs/lint.md +0 -35
- package/rules/docker/fix.mjs +0 -18
- package/rules/docker/lint/docs/index.md +0 -11
- package/rules/docker/lint/docs/lint.md +0 -200
- package/rules/docker/lint/lint.mjs +0 -95
- package/rules/efes/fix.mjs +0 -18
- package/rules/feedback/fix.mjs +0 -18
- package/rules/ga/fix.mjs +0 -18
- package/rules/ga/js/docs/lint.md +0 -20
- package/rules/ga/js/lint.mjs +0 -12
- package/rules/ga/lint/docs/index.md +0 -11
- package/rules/ga/lint/docs/lint.md +0 -31
- package/rules/graphql/fix.mjs +0 -18
- package/rules/hasura/fix.mjs +0 -18
- package/rules/image-avif/fix.mjs +0 -18
- package/rules/image-compress/fix.mjs +0 -18
- package/rules/image-compress/js/docs/lint.md +0 -24
- package/rules/js-bun-db/fix.mjs +0 -18
- package/rules/js-bun-redis/fix.mjs +0 -18
- package/rules/js-lint/fix.mjs +0 -18
- package/rules/js-lint/js/docs/lint.md +0 -32
- package/rules/js-lint-ci/fix.mjs +0 -18
- package/rules/js-lint-ci/js/docs/lint.md +0 -22
- package/rules/js-lint-ci/js/lint.mjs +0 -15
- package/rules/js-mssql/fix.mjs +0 -18
- package/rules/js-run/fix.mjs +0 -18
- package/rules/k8s/fix.mjs +0 -18
- package/rules/k8s/js/lint.mjs +0 -14
- package/rules/k8s/lint/docs/index.md +0 -11
- package/rules/k8s/lint/docs/lint.md +0 -413
- package/rules/lint/docs/fix.md +0 -25
- package/rules/lint/docs/index.md +0 -11
- package/rules/lint/fix.mjs +0 -18
- package/rules/lint/js/docs/index.md +0 -11
- package/rules/lint/js/docs/orchestrate.md +0 -31
- package/rules/lint/meta.json +0 -1
- package/rules/nginx-default-tpl/fix.mjs +0 -18
- package/rules/npm-module/fix.mjs +0 -18
- package/rules/php/fix.mjs +0 -18
- package/rules/php/js/docs/lint.md +0 -20
- package/rules/php/js/lint.mjs +0 -15
- package/rules/php/lint/docs/index.md +0 -11
- package/rules/php/lint/docs/lint.md +0 -219
- package/rules/python/fix.mjs +0 -18
- package/rules/python/js/docs/lint.md +0 -21
- package/rules/python/js/lint.mjs +0 -14
- package/rules/python/lint/docs/index.md +0 -11
- package/rules/python/lint/docs/lint.md +0 -29
- package/rules/rego/fix.mjs +0 -18
- package/rules/rego/js/docs/lint.md +0 -21
- package/rules/rego/js/lint.mjs +0 -12
- package/rules/rego/lint/docs/index.md +0 -11
- package/rules/rego/lint/docs/lint.md +0 -208
- package/rules/rust/fix.mjs +0 -18
- package/rules/rust/js/docs/lint.md +0 -21
- package/rules/security/fix.mjs +0 -18
- package/rules/security/js/docs/lint.md +0 -175
- package/rules/security/js/lint.mjs +0 -26
- package/rules/style-lint/fix.mjs +0 -18
- package/rules/style-lint/js/docs/lint.md +0 -31
- package/rules/tauri/fix.mjs +0 -18
- package/rules/test/fix.mjs +0 -18
- package/rules/text/fix.mjs +0 -18
- package/rules/text/js/docs/lint.md +0 -23
- package/rules/text/js/lint.mjs +0 -15
- package/rules/text/lint/docs/cspell-fix.md +0 -32
- package/rules/text/lint/docs/index.md +0 -15
- package/rules/text/lint/docs/lint.md +0 -36
- package/rules/text/lint/docs/run-dotenv-linter.md +0 -161
- package/rules/text/lint/docs/run-shellcheck.md +0 -216
- package/rules/text/lint/docs/run-v8r.md +0 -201
- package/rules/tool-surface/fix.mjs +0 -18
- package/rules/vue/fix.mjs +0 -18
- package/rules/worktree/fix.mjs +0 -18
- /package/rules/release/{fix.mjs → main.mjs} +0 -0
- /package/rules/text/{lint → js}/cspell-fix.mjs +0 -0
- /package/rules/text/{lint → js}/run-dotenv-linter.mjs +0 -0
- /package/rules/text/{lint → js}/run-shellcheck.mjs +0 -0
- /package/rules/text/{lint → js}/run-v8r.mjs +0 -0
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
/** @see ./docs/tooling.md */
|
|
2
|
-
import { existsSync } from 'node:fs'
|
|
3
|
-
import { copyFile, readFile } from 'node:fs/promises'
|
|
4
2
|
import { dirname, join } from 'node:path'
|
|
5
3
|
import { fileURLToPath } from 'node:url'
|
|
6
4
|
|
|
7
|
-
import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
|
|
8
|
-
|
|
9
5
|
/** Шлях до канонічного oxlint JSON у цьому пакеті (для перевірки та тестів). */
|
|
10
6
|
export const OXLINT_CANONICAL_JSON_PATH = join(
|
|
11
7
|
dirname(fileURLToPath(import.meta.url)),
|
|
@@ -22,15 +18,13 @@ export const KNIP_CANONICAL_JSON_PATH = join(
|
|
|
22
18
|
'knip-canonical.json'
|
|
23
19
|
)
|
|
24
20
|
|
|
25
|
-
const NON_DIGITS_RE = /\D+/u
|
|
26
|
-
|
|
27
21
|
// Канонічний рядок `lint-js`-скрипта і мінімальна версія `@nitra/eslint-config` —
|
|
28
22
|
// у rego (`npm/policy/js_lint/package_json/`). JS-копії (`CANONICAL_LINT_JS`,
|
|
29
23
|
// `isCanonicalLintJs`, `nitraEslintConfigMeetsMinVersion`) видалено, щоб не
|
|
30
24
|
// було двох джерел істини й ризику дрифту.
|
|
31
25
|
|
|
32
26
|
/**
|
|
33
|
-
* Рекурсивне порівняння фрагментів канону oxlint (масиви — порядок як у каноні;
|
|
27
|
+
* Рекурсивне порівняння фрагментів канону oxlint (масиви — порядок як у каноні; об'єкти — той самий набір ключів і вкладеність).
|
|
34
28
|
* @param {unknown} actual значення з `.oxlintrc.json`
|
|
35
29
|
* @param {unknown} expected значення з канону
|
|
36
30
|
* @returns {boolean} true, якщо значення збігаються за правилами канону
|
|
@@ -154,261 +148,3 @@ export function verifyOxlintRcAgainstCanonical(cfg, canonical) {
|
|
|
154
148
|
|
|
155
149
|
return { ok: failures.length === 0, failures }
|
|
156
150
|
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Перевіряє ESLint flat config файл.
|
|
160
|
-
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
161
|
-
* @param {(msg: string) => void} failFn callback при помилці
|
|
162
|
-
* @param {string} cwd корінь репозиторію
|
|
163
|
-
*/
|
|
164
|
-
async function checkEslintConfig(passFn, failFn, cwd) {
|
|
165
|
-
let eslintPath
|
|
166
|
-
if (existsSync(join(cwd, 'eslint.config.js'))) {
|
|
167
|
-
eslintPath = 'eslint.config.js'
|
|
168
|
-
passFn('eslint.config.js існує')
|
|
169
|
-
} else if (existsSync(join(cwd, 'eslint.config.mjs'))) {
|
|
170
|
-
eslintPath = 'eslint.config.mjs'
|
|
171
|
-
passFn('eslint.config.mjs існує')
|
|
172
|
-
} else {
|
|
173
|
-
failFn('Відсутній eslint.config.js або eslint.config.mjs — flat config з getConfig (js-lint.mdc)')
|
|
174
|
-
return
|
|
175
|
-
}
|
|
176
|
-
const eslintRaw = await readFile(join(cwd, eslintPath), 'utf8')
|
|
177
|
-
const checks = [
|
|
178
|
-
{
|
|
179
|
-
needle: 'getConfig',
|
|
180
|
-
ok: `${eslintPath}: містить getConfig`,
|
|
181
|
-
err: `${eslintPath}: потрібен виклик getConfig (js-lint.mdc)`
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
needle: '@nitra/eslint-config',
|
|
185
|
-
ok: `${eslintPath}: імпорт @nitra/eslint-config`,
|
|
186
|
-
err: `${eslintPath}: імпортуй getConfig з @nitra/eslint-config`
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
needle: '**/auto-imports.d.ts',
|
|
190
|
-
ok: `${eslintPath}: ignores містить **/auto-imports.d.ts`,
|
|
191
|
-
err: `${eslintPath}: додай у ignores запис **/auto-imports.d.ts (js-lint.mdc)`
|
|
192
|
-
}
|
|
193
|
-
]
|
|
194
|
-
for (const { needle, ok, err } of checks) {
|
|
195
|
-
if (eslintRaw.includes(needle)) {
|
|
196
|
-
passFn(ok)
|
|
197
|
-
} else {
|
|
198
|
-
failFn(err)
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Перевірки `prettier` / `@nitra/prettier-config` у залежностях (text.mdc) і
|
|
204
|
-
// `@nitra/eslint-config ≥ 3.10.0` тепер у Rego: відповідно
|
|
205
|
-
// `npm/policy/text/package_json/` і `npm/policy/js_lint/package_json/`. Тут
|
|
206
|
-
// лишилася лише workspace-ітерація для `type: "module"` і engines, бо js_lint
|
|
207
|
-
// Rego запускається лише на кореневому `package.json`.
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Перевіряє, що package.json має `"type": "module"`.
|
|
211
|
-
* @param {string} label шлях або назва пакета для повідомлень
|
|
212
|
-
* @param {{ type?: string }} pkg parsed package.json
|
|
213
|
-
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
214
|
-
* @param {(msg: string) => void} failFn callback при помилці
|
|
215
|
-
*/
|
|
216
|
-
function checkPackageJsonTypeModule(label, pkg, passFn, failFn) {
|
|
217
|
-
if (pkg.type === 'module') {
|
|
218
|
-
passFn(`${label}: "type": "module"`)
|
|
219
|
-
} else {
|
|
220
|
-
failFn(`${label}: має містити "type": "module" (js-lint.mdc)`)
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* `"type": "module"`, `engines.node >= 24` і `engines.bun >= 1.3` у кожному workspace `package.json`.
|
|
226
|
-
* @param {unknown[]} workspaces поле workspaces з package.json
|
|
227
|
-
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
228
|
-
* @param {(msg: string) => void} failFn callback при помилці
|
|
229
|
-
* @param {string} cwd корінь репозиторію
|
|
230
|
-
*/
|
|
231
|
-
async function checkWorkspacePackages(workspaces, passFn, failFn, cwd) {
|
|
232
|
-
for (const ws of workspaces) {
|
|
233
|
-
const wsPkgRel = `${ws}/package.json`
|
|
234
|
-
const wsPkgAbs = join(cwd, wsPkgRel)
|
|
235
|
-
if (existsSync(wsPkgAbs)) {
|
|
236
|
-
const wsPkg = JSON.parse(await readFile(wsPkgAbs, 'utf8'))
|
|
237
|
-
checkPackageJsonTypeModule(wsPkgRel, wsPkg, passFn, failFn)
|
|
238
|
-
checkEnginesNode(wsPkgRel, wsPkg, passFn, failFn)
|
|
239
|
-
checkEnginesBun(wsPkgRel, wsPkg, passFn, failFn)
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* engines.node >= 24.
|
|
246
|
-
* @param {string} label шлях або назва пакета для повідомлень
|
|
247
|
-
* @param {{ engines?: { node?: string } }} pkg розпарсений package.json
|
|
248
|
-
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
249
|
-
* @param {(msg: string) => void} failFn callback при помилці
|
|
250
|
-
*/
|
|
251
|
-
function checkEnginesNode(label, pkg, passFn, failFn) {
|
|
252
|
-
const nodeEngine = pkg.engines?.node
|
|
253
|
-
if (nodeEngine) {
|
|
254
|
-
const firstNumeric = String(nodeEngine).split(NON_DIGITS_RE).find(Boolean)
|
|
255
|
-
if (firstNumeric && Number(firstNumeric) >= 24) {
|
|
256
|
-
passFn(`${label}: engines.node "${nodeEngine}"`)
|
|
257
|
-
} else {
|
|
258
|
-
failFn(`${label}: engines.node "${nodeEngine}" — має бути >=24`)
|
|
259
|
-
}
|
|
260
|
-
} else {
|
|
261
|
-
failFn(`${label} не містить engines.node — додай: "engines": { "node": ">=24" }`)
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* engines.bun >= 1.3.
|
|
267
|
-
* @param {string} label шлях або назва пакета для повідомлень
|
|
268
|
-
* @param {{ engines?: { bun?: string } }} pkg розпарсений package.json
|
|
269
|
-
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
270
|
-
* @param {(msg: string) => void} failFn callback при помилці
|
|
271
|
-
*/
|
|
272
|
-
function checkEnginesBun(label, pkg, passFn, failFn) {
|
|
273
|
-
const bunEngine = pkg.engines?.bun
|
|
274
|
-
if (bunEngine) {
|
|
275
|
-
const [major, minor] = String(bunEngine).split(NON_DIGITS_RE).filter(Boolean).map(Number)
|
|
276
|
-
if (Number.isFinite(major) && Number.isFinite(minor) && (major > 1 || (major === 1 && minor >= 3))) {
|
|
277
|
-
passFn(`${label}: engines.bun "${bunEngine}"`)
|
|
278
|
-
} else {
|
|
279
|
-
failFn(`${label}: engines.bun "${bunEngine}" — має бути >=1.3`)
|
|
280
|
-
}
|
|
281
|
-
} else {
|
|
282
|
-
failFn(`${label} не містить engines.bun — додай: "engines": { "bun": ">=1.3" }`)
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Workspace-ітерація: для кожного workspace `package.json` перевіряємо
|
|
288
|
-
* `type: "module"` і `engines.{node,bun}`. Кореневий `package.json` ці поля
|
|
289
|
-
* валідує `npm/policy/js_lint/package_json/`; lint-js скрипт і `@nitra/eslint-config`
|
|
290
|
-
* — теж у Rego.
|
|
291
|
-
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
292
|
-
* @param {(msg: string) => void} failFn callback при помилці
|
|
293
|
-
* @param {string} cwd корінь репозиторію
|
|
294
|
-
*/
|
|
295
|
-
async function checkPackageJsonJsLint(passFn, failFn, cwd) {
|
|
296
|
-
const pkgPath = join(cwd, 'package.json')
|
|
297
|
-
if (!existsSync(pkgPath)) return
|
|
298
|
-
const pkg = JSON.parse(await readFile(pkgPath, 'utf8'))
|
|
299
|
-
const workspaces = Array.isArray(pkg.workspaces) ? pkg.workspaces : []
|
|
300
|
-
await checkWorkspacePackages(workspaces, passFn, failFn, cwd)
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Перевіряє .oxlintrc.json.
|
|
305
|
-
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
306
|
-
* @param {(msg: string) => void} failFn callback при помилці
|
|
307
|
-
* @param {string} cwd корінь репозиторію
|
|
308
|
-
*/
|
|
309
|
-
async function checkOxlintRc(passFn, failFn, cwd) {
|
|
310
|
-
const oxPath = join(cwd, '.oxlintrc.json')
|
|
311
|
-
if (!existsSync(oxPath)) {
|
|
312
|
-
failFn('.oxlintrc.json не існує — додай конфіг oxlint (js-lint.mdc)')
|
|
313
|
-
return
|
|
314
|
-
}
|
|
315
|
-
let oxCfg
|
|
316
|
-
try {
|
|
317
|
-
oxCfg = JSON.parse(await readFile(oxPath, 'utf8'))
|
|
318
|
-
} catch {
|
|
319
|
-
failFn('.oxlintrc.json не є валідним JSON')
|
|
320
|
-
return
|
|
321
|
-
}
|
|
322
|
-
passFn('.oxlintrc.json існує')
|
|
323
|
-
let canonical
|
|
324
|
-
try {
|
|
325
|
-
canonical = JSON.parse(await readFile(OXLINT_CANONICAL_JSON_PATH, 'utf8'))
|
|
326
|
-
} catch {
|
|
327
|
-
failFn('внутрішня помилка: не вдалося прочитати канон oxlint з пакета @nitra/cursor')
|
|
328
|
-
return
|
|
329
|
-
}
|
|
330
|
-
const oxV = verifyOxlintRcAgainstCanonical(oxCfg, canonical)
|
|
331
|
-
if (oxV.ok) {
|
|
332
|
-
passFn('.oxlintrc.json збігається з каноном oxlint (@nitra/cursor)')
|
|
333
|
-
} else {
|
|
334
|
-
for (const msg of oxV.failures) {
|
|
335
|
-
failFn(msg)
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* FS-existence для `lint-js.yml` + cross-file перевірка, що `lint.yml` (якщо існує)
|
|
342
|
-
* не дублює лінт JS-кроки. Структуру `lint-js.yml` (`actions/checkout@v6`,
|
|
343
|
-
* `persist-credentials: false`, `setup-bun-deps`, `bunx oxlint/eslint/jscpd .`,
|
|
344
|
-
* заборона `--fix` у CI) валідує `npm/policy/js_lint/lint_js_yml/`.
|
|
345
|
-
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
346
|
-
* @param {(msg: string) => void} failFn callback при помилці
|
|
347
|
-
* @param {string} cwd корінь репозиторію
|
|
348
|
-
*/
|
|
349
|
-
async function checkLintJsWorkflows(passFn, failFn, cwd) {
|
|
350
|
-
if (existsSync(join(cwd, '.github/workflows/lint-js.yml'))) {
|
|
351
|
-
passFn('.github/workflows/lint-js.yml є (структуру перевіряє npx @nitra/cursor fix → js_lint.lint_js_yml)')
|
|
352
|
-
} else {
|
|
353
|
-
failFn('.github/workflows/lint-js.yml не існує — створи його (див. rules/js-lint/fix.mjs / js-lint.mdc)')
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
const lintYmlPath = join(cwd, '.github/workflows/lint.yml')
|
|
357
|
-
if (existsSync(lintYmlPath)) {
|
|
358
|
-
const lintYml = await readFile(lintYmlPath, 'utf8')
|
|
359
|
-
if (lintYml.includes('bunx oxlint') && lintYml.includes('bunx eslint') && lintYml.includes('jscpd')) {
|
|
360
|
-
failFn('.github/workflows/lint.yml дублює кроки lint-js.yml — залиш один workflow на лінт JS (js-lint.mdc)')
|
|
361
|
-
} else {
|
|
362
|
-
passFn('.github/workflows/lint.yml не дублює oxlint/eslint/jscpd з lint-js.yml')
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Перевіряє наявність `knip.json` у корені проєкту. Якщо файл відсутній —
|
|
369
|
-
* копіює канонічний `knip-canonical.json` з пакета `@nitra/cursor` як стартовий
|
|
370
|
-
* baseline; зміст подальших модифікацій локально не валідується (`entry` /
|
|
371
|
-
* `project` / `ignore` / `ignoreDependencies` / `ignoreBinaries` дозволені
|
|
372
|
-
* будь-які; це side effect — описано у js-lint.mdc).
|
|
373
|
-
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
374
|
-
* @param {(msg: string) => void} failFn callback при помилці
|
|
375
|
-
* @param {string} cwd корінь репозиторію
|
|
376
|
-
*/
|
|
377
|
-
async function checkKnipConfig(passFn, failFn, cwd) {
|
|
378
|
-
const knipPath = join(cwd, 'knip.json')
|
|
379
|
-
if (existsSync(knipPath)) {
|
|
380
|
-
passFn('knip.json існує')
|
|
381
|
-
return
|
|
382
|
-
}
|
|
383
|
-
if (!existsSync(KNIP_CANONICAL_JSON_PATH)) {
|
|
384
|
-
failFn(
|
|
385
|
-
`knip.json відсутній, і канонічний шаблон у пакеті не знайдено (${KNIP_CANONICAL_JSON_PATH}) — ` +
|
|
386
|
-
'перевстанови @nitra/cursor'
|
|
387
|
-
)
|
|
388
|
-
return
|
|
389
|
-
}
|
|
390
|
-
await copyFile(KNIP_CANONICAL_JSON_PATH, knipPath)
|
|
391
|
-
passFn('knip.json створено з канонічного npm/rules/js-lint/js/data/tooling/knip-canonical.json (js-lint.mdc)')
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Перевіряє відповідність проєкту правилам js-lint.mdc
|
|
396
|
-
* @param {string} [cwd] корінь репозиторію
|
|
397
|
-
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
398
|
-
*/
|
|
399
|
-
export async function check(cwd = process.cwd()) {
|
|
400
|
-
const reporter = createCheckReporter()
|
|
401
|
-
const { pass, fail } = reporter
|
|
402
|
-
|
|
403
|
-
await checkEslintConfig(pass, fail, cwd)
|
|
404
|
-
await checkPackageJsonJsLint(pass, fail, cwd)
|
|
405
|
-
await checkOxlintRc(pass, fail, cwd)
|
|
406
|
-
await checkLintJsWorkflows(pass, fail, cwd)
|
|
407
|
-
await checkKnipConfig(pass, fail, cwd)
|
|
408
|
-
|
|
409
|
-
for (const dup of ['.eslintrc', '.eslintrc.js', '.eslintrc.json', '.eslintrc.yml']) {
|
|
410
|
-
if (existsSync(join(cwd, dup))) fail(`Знайдено застарілий конфіг ESLint: ${dup} — видали, використовуй flat config`)
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
return reporter.getExitCode()
|
|
414
|
-
}
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
/** @see ./docs/lint.md */
|
|
2
2
|
import { spawnSync } from 'node:child_process'
|
|
3
3
|
|
|
4
|
-
import { addedLinesByFile } from '
|
|
5
|
-
import { classifyFindings, parseEslint, parseOxlint, renderFindings } from './lint-findings.mjs'
|
|
4
|
+
import { addedLinesByFile } from '../../scripts/lib/diff-added-lines.mjs'
|
|
5
|
+
import { classifyFindings, parseEslint, parseOxlint, renderFindings } from './js/lint-findings.mjs'
|
|
6
|
+
import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
|
|
7
|
+
import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Єдиний entrypoint правила (ADR 2026-06-21). `run()` — check-поверхня (applies → JS-concerns
|
|
11
|
+
* → policy → mdc-refs); `lint()` нижче — lint-поверхня (oxlint+eslint), імпл інлайн тут.
|
|
12
|
+
* @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону
|
|
13
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
14
|
+
*/
|
|
15
|
+
export function run(ctx) {
|
|
16
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
17
|
+
}
|
|
6
18
|
|
|
7
19
|
const JS_EXT_RE = /\.(?:mjs|cjs|js|jsx|ts|tsx|vue)$/u
|
|
8
20
|
|
|
@@ -119,3 +131,8 @@ export function lint(files, cwd = process.cwd(), opts = {}) {
|
|
|
119
131
|
if (js.length === 0) return Promise.resolve(0)
|
|
120
132
|
return Promise.resolve(lintChangedClassified(js, cwd, readOnly))
|
|
121
133
|
}
|
|
134
|
+
|
|
135
|
+
if (isRunAsCli(import.meta.url)) {
|
|
136
|
+
// Standalone: bun rules/js-lint/main.mjs — повний еквівалент `npx @nitra/cursor check js-lint`.
|
|
137
|
+
process.exitCode = await runRuleCli(import.meta.dirname)
|
|
138
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
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: не виконує операцій запису (ФС/БД).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process'
|
|
2
|
+
|
|
3
|
+
import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
|
|
4
|
+
import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Єдиний entrypoint правила (ADR 2026-06-21). `run()` — check-поверхня (applies → JS-concerns
|
|
8
|
+
* → policy → mdc-refs); `lint()` — lint-поверхня (jscpd + knip, крос-файловий аналіз).
|
|
9
|
+
* @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону
|
|
10
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
11
|
+
*/
|
|
12
|
+
export function run(ctx) {
|
|
13
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* lint-поверхня: jscpd (дублікати) + knip (мертвий код) по всьому репо.
|
|
18
|
+
* @param {string[] | undefined} _files ігнорується (крос-файловий аналіз)
|
|
19
|
+
* @param {string} [cwd] корінь репо
|
|
20
|
+
* @returns {Promise<number>} 0 — OK, ≠0 — порушення
|
|
21
|
+
*/
|
|
22
|
+
export function lint(_files, cwd = process.cwd()) {
|
|
23
|
+
const jscpd = spawnSync('bunx', ['jscpd', '.'], { cwd, stdio: 'inherit' })
|
|
24
|
+
const jc = typeof jscpd.status === 'number' ? jscpd.status : 1
|
|
25
|
+
if (jc !== 0) return Promise.resolve(jc)
|
|
26
|
+
const knip = spawnSync('bunx', ['knip', '--no-config-hints'], { cwd, stdio: 'inherit' })
|
|
27
|
+
return Promise.resolve(typeof knip.status === 'number' ? knip.status : 1)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (isRunAsCli(import.meta.url)) {
|
|
31
|
+
// Standalone: bun rules/<id>/main.mjs — повний еквівалент `npx @nitra/cursor check <id>`.
|
|
32
|
+
process.exitCode = await runRuleCli(import.meta.dirname)
|
|
33
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: main.mjs
|
|
4
|
+
resource: npm/rules/js-mssql/main.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: 762b6875
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Огляд
|
|
12
|
+
|
|
13
|
+
Модуль валідує відповідність даних, використовуючи логіку, визначену у meta.json. Він перевіряє відповідність конфігурацій та білих списків. Публічна функція run ініціює процес. Модуль є Read-only, тобто не виконує запис у файлову систему чи базу даних.
|
|
14
|
+
|
|
15
|
+
## Поведінка
|
|
16
|
+
|
|
17
|
+
1. Викликається функція `run` для виконання перевірки.
|
|
18
|
+
2. Виконання `run` застосовує логіку, визначену в `meta.json`, включаючи конфігурації.
|
|
19
|
+
3. Виконання `run` виконує перевірку, яка включає логіку, пов'язану з JS-зацікавленостями та політиками.
|
|
20
|
+
4. Якщо скрипт запускається як окрема програма (CLI), виконується повний цикл виконання правила.
|
|
21
|
+
5. Цей цикл включає завантаження конфігурацій, застосування білих списків та підбиття підсумків.
|
|
22
|
+
|
|
23
|
+
## Публічний API
|
|
24
|
+
|
|
25
|
+
run — виконує основну логіку правила: застосовує перевірки щодо JS-зацікавленостей, політики та посилань MDC.
|
|
26
|
+
|
|
27
|
+
## Гарантії поведінки
|
|
28
|
+
|
|
29
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
|
30
|
+
- Кешує результати в межах одного прогону.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
|
|
2
|
+
import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Єдиний entrypoint правила (ADR 2026-06-21). `run()` — check-поверхня: applies →
|
|
6
|
+
* JS-concerns → policy → mdc-refs (через runStandardRule). Lint-поверхні правило не має
|
|
7
|
+
* (`meta.json` без `lint`), тож експорту `lint` тут немає.
|
|
8
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
9
|
+
* @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
10
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
11
|
+
*/
|
|
12
|
+
export function run(ctx) {
|
|
13
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (isRunAsCli(import.meta.url)) {
|
|
17
|
+
// Standalone: bun rules/<id>/main.mjs — повний еквівалент `npx @nitra/cursor check <id>`
|
|
18
|
+
// (config-loading + whitelist + summary): library-роль (run) + standalone-роль (CLI-блок).
|
|
19
|
+
process.exitCode = await runRuleCli(import.meta.dirname)
|
|
20
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: main.mjs
|
|
4
|
+
resource: npm/rules/js-run/main.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: 762b6875
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Огляд
|
|
12
|
+
|
|
13
|
+
Модуль виконує визначене правило. Він ініціює стандартний запуск правила, використовуючи директорію правила та контекст прогону. При запуску як окрема програма, він завантажує конфігурації, на які спирається код, зокрема `meta.json`. Функціональність є read-only, і дані кешуються у межах прогону. Фінальний результат визначається викликом публічної функції `run`.
|
|
14
|
+
|
|
15
|
+
## Поведінка
|
|
16
|
+
|
|
17
|
+
1. Викликається функція `run` для виконання перевірки.
|
|
18
|
+
2. Функція `run` ініціює стандартне правило, використовуючи директорію цього правила та наданий контекст прогону.
|
|
19
|
+
3. Якщо код виконується як окрема програма (CLI), ініціюється повний запуск правила.
|
|
20
|
+
4. Повний запуск включає завантаження конфігурацій (наприклад, `meta.json`), застосування білих списків та підсумок.
|
|
21
|
+
5. Результат виконання визначає код виходу процесу.
|
|
22
|
+
|
|
23
|
+
## Публічний API
|
|
24
|
+
|
|
25
|
+
run — виконує основну перевірку правила, застосовуючи вимоги JS-контексту, політики та посилання MDC.
|
|
26
|
+
|
|
27
|
+
## Гарантії поведінки
|
|
28
|
+
|
|
29
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
|
30
|
+
- Кешує результати в межах одного прогону.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
|
|
2
|
+
import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Єдиний entrypoint правила (ADR 2026-06-21). `run()` — check-поверхня: applies →
|
|
6
|
+
* JS-concerns → policy → mdc-refs (через runStandardRule). Lint-поверхні правило не має
|
|
7
|
+
* (`meta.json` без `lint`), тож експорту `lint` тут немає.
|
|
8
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
9
|
+
* @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
10
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
11
|
+
*/
|
|
12
|
+
export function run(ctx) {
|
|
13
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (isRunAsCli(import.meta.url)) {
|
|
17
|
+
// Standalone: bun rules/<id>/main.mjs — повний еквівалент `npx @nitra/cursor check <id>`
|
|
18
|
+
// (config-loading + whitelist + summary): library-роль (run) + standalone-роль (CLI-блок).
|
|
19
|
+
process.exitCode = await runRuleCli(import.meta.dirname)
|
|
20
|
+
}
|
package/rules/k8s/docs/index.md
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: main.mjs
|
|
4
|
+
resource: npm/rules/k8s/main.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: 1caca447
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 85
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Огляд
|
|
12
|
+
|
|
13
|
+
Модуль знаходить унікальні корені каталогів із іменем `k8s` за шляхами файлів `*.yaml` у репозиторії. Якщо таких файлів немає, виконання завершується з кодом 0 без виклику зовнішніх CLI. Для знайдених коренів виконується перевірка YAML-маніфестів. `kubeconform` перевіряє маніфести проти OpenAPI-схем Kubernetes (https://github.com/yannh/kubeconform#readme), використовуючи версію, узгоджену з лінією релізу. `kubescape` сканує маніфести на misconfiguration та відповідність стандартам (NSA, MITRE, CIS), використовуючи конфігураційний файл `.kubescape-exceptions.json`. Орієнтир цільового кластера для `kubescape` визначається за тією ж лінією релізу, що й для `kubeconform`. Обидві утиліти (`kubeconform` та `kubescape`) повинні бути доступні в системному PATH.
|
|
14
|
+
|
|
15
|
+
## Поведінка
|
|
16
|
+
|
|
17
|
+
run виконує стандартну перевірку правила.
|
|
18
|
+
pathHasK8sSegment визначає, чи містить шлях сегмент директорії `k8s` відносно кореня репозиторію.
|
|
19
|
+
k8sRootFromFile знаходить абсолютний шлях до каталогу `…/k8s`, що містить маніфест, виходячи з абсолютного шляху до YAML-файлу.
|
|
20
|
+
findK8sRoots знаходить унікальні абсолютні шляхи до каталогів `k8s` за наявності файлів `*.yaml`, ігноруючи шляхи, що починаються з `.github/`.
|
|
21
|
+
buildKubescapeExceptionsArgs будує аргументи для `kubescape`, якщо існує файл `.kubescape-exceptions.json` у корені репозиторію.
|
|
22
|
+
findKustomizationDirs знаходить абсолютні шляхи до каталогів, що містять білдабельний `kustomization.yaml` у межах каталогу `…/k8s`, виключаючи компоненти (`kind: Component`).
|
|
23
|
+
runLintK8s виконує повний цикл перевірки Kubernetes-маніфестів за допомогою `kubeconform` та `kubescape`.
|
|
24
|
+
lint делегує виконання повного циклу перевірки Kubernetes-маніфестів функції `runLintK8s`.
|
|
25
|
+
|
|
26
|
+
## Публічний API
|
|
27
|
+
|
|
28
|
+
run — виконує перевірку конфігурацій (applies → JS-concerns → policy → mdc-refs) та лінтинг (kubeconform/kubescape).
|
|
29
|
+
pathHasK8sSegment — визначає, чи містить шлях сегмент директорії `k8s`.
|
|
30
|
+
k8sRootFromFile — знаходить каталог `…/k8s`, що містить маніфест, рухаючись від файлу вгору.
|
|
31
|
+
findK8sRoots — збирає список унікальних коренів `k8s`, якщо в поточному каталозі є файли `*.yaml`.
|
|
32
|
+
buildKubescapeExceptionsArgs — створює аргументи `--exceptions <file>` для kubescape, якщо існує `.kubescape-exceptions.json` у корені проєкту.
|
|
33
|
+
findKustomizationDirs — знаходить каталоги, які є точками входу Kustomize (містять `kustomization.yaml` з `kind: Kustomization`).
|
|
34
|
+
runLintK8s — виконує публічну CLI-операцію лінтингу конфігурацій, використовуючи механізм блокування та дедуплікації за станом git-дерева.
|
|
35
|
+
lint — слугує оркестратором, який делегує виконання лінтингу конфігурацій (`n-cursor lint k8s`) функції `runLintK8s`.
|
|
36
|
+
|
|
37
|
+
## Гарантії поведінки
|
|
38
|
+
|
|
39
|
+
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
40
|
+
- Свідомо пропускає шляхи: `.github`, `.git`.
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Запуск kubeconform та kubescape для каталогів `…/k8s`, де є YAML-маніфести (див. k8s.mdc).
|
|
3
3
|
*
|
|
4
4
|
* Знаходить унікальні корені каталогів із іменем `k8s` за шляхами файлів **`*.yaml`**
|
|
5
|
-
* (той самий принцип сегмента `k8s`, що й у rules/k8s/
|
|
5
|
+
* (той самий принцип сегмента `k8s`, що й у rules/k8s/check.mjs; розширення **`.yml`** під `k8s` не використовується). Якщо таких файлів немає — вихід 0
|
|
6
6
|
* без виклику зовнішніх CLI.
|
|
7
7
|
*
|
|
8
8
|
* kubeconform перевіряє маніфести проти OpenAPI-схем Kubernetes; kubescape — сканування на
|
|
9
9
|
* misconfiguration / compliance (NSA, MITRE, CIS тощо). Обидві утиліти очікуються в PATH
|
|
10
10
|
* (локально: Homebrew, релізи GitHub; у CI — крок установки з k8s.mdc).
|
|
11
11
|
*
|
|
12
|
-
* Версія `-kubernetes-version` для kubeconform узгоджена з PIN yannh у rules/k8s/
|
|
12
|
+
* Версія `-kubernetes-version` для kubeconform узгоджена з PIN yannh у rules/k8s/check.mjs / k8s.mdc.
|
|
13
13
|
* Kubescape не має аналога цього прапорця; орієнтир цільового кластера — та сама лінія релізу (див. k8s.mdc).
|
|
14
14
|
*
|
|
15
15
|
* Канон патерну `lint-*` (серіалізація через `runStandardLint`, без прямого `withLock`) —
|
|
@@ -23,12 +23,24 @@ import { basename, dirname, join, relative } from 'node:path'
|
|
|
23
23
|
|
|
24
24
|
import { parse } from 'yaml'
|
|
25
25
|
|
|
26
|
-
import { isRunAsCli } from '
|
|
27
|
-
import { ensureTool } from '
|
|
28
|
-
import { loadCursorIgnorePaths } from '
|
|
29
|
-
import { resolveCmd } from '
|
|
30
|
-
import { walkDir } from '
|
|
31
|
-
import { runStandardLint } from '
|
|
26
|
+
import { isRunAsCli } from '../../scripts/cli-entry.mjs'
|
|
27
|
+
import { ensureTool } from '../../scripts/lib/ensure-tool.mjs'
|
|
28
|
+
import { loadCursorIgnorePaths } from '../../scripts/lib/load-cursor-config.mjs'
|
|
29
|
+
import { resolveCmd } from '../../scripts/utils/resolve-cmd.mjs'
|
|
30
|
+
import { walkDir } from '../../scripts/utils/walkDir.mjs'
|
|
31
|
+
import { runStandardLint } from '../../scripts/lib/run-standard-lint.mjs'
|
|
32
|
+
import { runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
|
|
33
|
+
import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Єдиний entrypoint правила (ADR 2026-06-21). `run()` — check-поверхня (applies → JS-concerns
|
|
37
|
+
* → policy → mdc-refs); `lint()` нижче — lint-поверхня (kubeconform/kubescape), імпл інлайн тут.
|
|
38
|
+
* @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону
|
|
39
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
40
|
+
*/
|
|
41
|
+
export function run(ctx) {
|
|
42
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
43
|
+
}
|
|
32
44
|
|
|
33
45
|
/** Per-project kubescape exceptions file; підмішується через --exceptions, якщо існує в корені. */
|
|
34
46
|
const KUBESCAPE_EXCEPTIONS_FILE = '.kubescape-exceptions.json'
|
|
@@ -342,11 +354,21 @@ async function runLintK8sSteps() {
|
|
|
342
354
|
|
|
343
355
|
/**
|
|
344
356
|
* Публічна CLI-форма: серіалізує через `withLock('lint-k8s')` + дедуп за станом git-дерева.
|
|
345
|
-
* Експортовано як `runLintK8s` —
|
|
357
|
+
* Експортовано як `runLintK8s` — викликається через `n-cursor lint k8s` (оркестраторний адаптер `lint()` делегує сюди); окремої bin-підкоманди `lint-k8s` немає.
|
|
346
358
|
* @returns {Promise<number>} код виходу
|
|
347
359
|
*/
|
|
348
360
|
export const runLintK8s = () => runStandardLint(import.meta.dirname, runLintK8sSteps)
|
|
349
361
|
|
|
362
|
+
/**
|
|
363
|
+
* Оркестраторний адаптер `n-cursor lint k8s`: делегує у `runLintK8s`.
|
|
364
|
+
* @param {string[] | undefined} _files ігнорується (whole-repo обхід `.../k8s`)
|
|
365
|
+
* @returns {Promise<number>} exit code
|
|
366
|
+
*/
|
|
367
|
+
export function lint(_files) {
|
|
368
|
+
return runLintK8s()
|
|
369
|
+
}
|
|
370
|
+
|
|
350
371
|
if (isRunAsCli(import.meta.url)) {
|
|
351
|
-
|
|
372
|
+
// Standalone: bun rules/k8s/main.mjs — повний еквівалент `npx @nitra/cursor check k8s`.
|
|
373
|
+
process.exitCode = await runRuleCli(import.meta.dirname)
|
|
352
374
|
}
|