@nitra/cursor 12.6.1 → 12.8.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/.pi-template/extensions/n-cursor-adr/docs/index.md +2 -2
- package/CHANGELOG.md +25 -5
- package/bin/docs/n-cursor.md +4 -20
- package/bin/n-cursor.js +8 -54
- package/docs/index.md +3 -3
- package/docs/stryker.config.md +20 -28
- package/lib/docs/index.md +5 -5
- package/lib/docs/llm.md +4 -4
- package/package.json +2 -2
- package/rules/abie/docs/fix.md +8 -8
- package/rules/abie/docs/index.md +4 -3
- package/rules/abie/docs/main.md +29 -0
- package/rules/abie/js/docs/index.md +6 -6
- package/rules/abie/lib/docs/index.md +9 -9
- 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/fix.md +5 -5
- package/rules/bun/docs/index.md +4 -3
- package/rules/bun/docs/main.md +30 -0
- package/rules/bun/js/docs/index.md +2 -2
- package/rules/bun/js/docs/layout.md +11 -36
- package/rules/bun/{fix.mjs → main.mjs} +5 -3
- package/rules/capacitor/docs/fix.md +10 -10
- package/rules/capacitor/docs/index.md +4 -3
- package/rules/capacitor/docs/main.md +29 -0
- package/rules/capacitor/js/docs/index.md +2 -2
- package/rules/capacitor/{fix.mjs → main.mjs} +5 -3
- package/rules/changelog/docs/fix.md +11 -11
- package/rules/changelog/docs/index.md +4 -3
- package/rules/changelog/docs/main.md +27 -0
- package/rules/changelog/js/docs/consistency.md +12 -12
- package/rules/changelog/js/docs/index.md +2 -2
- package/rules/changelog/lib/docs/index.md +2 -2
- package/rules/changelog/main.mjs +20 -0
- package/rules/ci4/docs/fix.md +4 -4
- package/rules/ci4/docs/index.md +4 -3
- package/rules/ci4/docs/main.md +30 -0
- package/rules/ci4/js/docs/index.md +2 -2
- package/rules/ci4/main.mjs +20 -0
- package/rules/doc-files/docs/index.md +4 -3
- package/rules/doc-files/docs/main.md +31 -0
- package/rules/doc-files/js/docgen-crc.mjs +2 -8
- package/rules/doc-files/js/docgen-extract.mjs +5 -3
- package/rules/doc-files/js/docgen-files-batch.mjs +63 -4
- package/rules/doc-files/js/docgen-gen.mjs +11 -3
- package/rules/doc-files/js/docgen-judge-measure.mjs +67 -18
- package/rules/doc-files/js/docgen-judge.mjs +8 -1
- package/rules/doc-files/js/docgen-scan.mjs +99 -11
- package/rules/doc-files/js/docs/docgen-crc.md +25 -14
- package/rules/doc-files/js/docs/docgen-extract.md +15 -13
- package/rules/doc-files/js/docs/docgen-files-batch.md +15 -15
- package/rules/doc-files/js/docs/docgen-gen.md +15 -26
- package/rules/doc-files/js/docs/docgen-judge-measure.md +14 -12
- package/rules/doc-files/js/docs/docgen-scan.md +34 -34
- package/rules/doc-files/js/docs/index.md +16 -15
- 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/fix.md +6 -6
- package/rules/docker/docs/index.md +4 -3
- package/rules/docker/docs/main.md +28 -0
- package/rules/docker/js/docs/index.md +2 -2
- 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/lib/docs/index.md +5 -5
- package/rules/docker/main.mjs +20 -0
- package/rules/efes/docs/fix.md +8 -8
- package/rules/efes/docs/index.md +4 -3
- package/rules/efes/docs/main.md +29 -0
- package/rules/efes/main.mjs +20 -0
- package/rules/feedback/docs/fix.md +5 -5
- package/rules/feedback/docs/index.md +4 -3
- package/rules/feedback/docs/main.md +30 -0
- package/rules/feedback/main.mjs +20 -0
- package/rules/ga/docs/fix.md +5 -5
- package/rules/ga/docs/index.md +4 -3
- package/rules/ga/docs/main.md +29 -0
- package/rules/ga/js/docs/index.md +3 -3
- package/rules/ga/{lint/lint.mjs → main.mjs} +36 -10
- package/rules/graphql/docs/fix.md +8 -8
- package/rules/graphql/docs/index.md +4 -3
- package/rules/graphql/docs/main.md +36 -0
- package/rules/graphql/js/docs/index.md +2 -2
- package/rules/graphql/lib/docs/index.md +2 -2
- package/rules/graphql/main.mjs +20 -0
- package/rules/hasura/docs/fix.md +11 -11
- package/rules/hasura/docs/index.md +4 -3
- package/rules/hasura/docs/main.md +30 -0
- package/rules/hasura/js/docs/index.md +2 -2
- package/rules/hasura/main.mjs +20 -0
- package/rules/image-avif/docs/fix.md +3 -3
- package/rules/image-avif/docs/index.md +4 -3
- package/rules/image-avif/docs/main.md +30 -0
- package/rules/image-avif/js/docs/avif_generation.md +20 -233
- package/rules/image-avif/js/docs/index.md +2 -2
- package/rules/image-avif/main.mjs +20 -0
- package/rules/image-compress/docs/fix.md +2 -2
- package/rules/image-compress/docs/index.md +4 -3
- package/rules/image-compress/docs/main.md +29 -0
- package/rules/image-compress/js/docs/index.md +3 -3
- 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/fix.md +5 -5
- package/rules/js-bun-db/docs/index.md +4 -3
- package/rules/js-bun-db/docs/main.md +30 -0
- package/rules/js-bun-db/js/docs/index.md +2 -2
- package/rules/js-bun-db/lib/docs/index.md +2 -2
- package/rules/js-bun-db/main.mjs +20 -0
- package/rules/js-bun-redis/docs/fix.md +6 -6
- package/rules/js-bun-redis/docs/index.md +4 -3
- package/rules/js-bun-redis/docs/main.md +29 -0
- package/rules/js-bun-redis/js/docs/index.md +2 -2
- package/rules/js-bun-redis/lib/docs/index.md +2 -2
- package/rules/js-bun-redis/main.mjs +20 -0
- package/rules/js-lint/docs/fix.md +9 -9
- package/rules/js-lint/docs/index.md +4 -3
- 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 +4 -4
- 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/fix.md +3 -3
- package/rules/js-lint-ci/docs/index.md +4 -3
- package/rules/js-lint-ci/docs/main.md +27 -0
- package/rules/js-lint-ci/js/docs/index.md +2 -2
- package/rules/js-lint-ci/main.mjs +33 -0
- package/rules/js-mssql/docs/fix.md +5 -5
- package/rules/js-mssql/docs/index.md +4 -3
- package/rules/js-mssql/docs/main.md +30 -0
- package/rules/js-mssql/js/docs/index.md +2 -2
- package/rules/js-mssql/lib/docs/index.md +2 -2
- package/rules/js-mssql/main.mjs +20 -0
- package/rules/js-run/docs/fix.md +8 -8
- package/rules/js-run/docs/index.md +4 -3
- package/rules/js-run/docs/main.md +30 -0
- package/rules/js-run/js/docs/index.md +2 -2
- package/rules/js-run/lib/docs/index.md +7 -7
- package/rules/js-run/main.mjs +20 -0
- package/rules/k8s/docs/fix.md +4 -4
- package/rules/k8s/docs/index.md +4 -3
- 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/fix.md +7 -7
- package/rules/nginx-default-tpl/docs/index.md +4 -3
- package/rules/nginx-default-tpl/docs/main.md +30 -0
- package/rules/nginx-default-tpl/js/docs/index.md +2 -2
- package/rules/nginx-default-tpl/js/docs/template.md +2 -2
- package/rules/nginx-default-tpl/main.mjs +20 -0
- package/rules/npm-module/docs/fix.md +8 -8
- package/rules/npm-module/docs/index.md +4 -3
- package/rules/npm-module/docs/main.md +29 -0
- package/rules/npm-module/js/docs/index.md +5 -5
- package/rules/npm-module/js/docs/rule_meta.md +17 -16
- package/rules/npm-module/js/header_doc_pointer.mjs +1 -3
- package/rules/npm-module/js/rule_meta.mjs +13 -3
- package/rules/npm-module/main.mjs +20 -0
- package/rules/php/docs/fix.md +6 -6
- package/rules/php/docs/index.md +4 -3
- package/rules/php/docs/main.md +33 -0
- package/rules/php/js/docs/index.md +3 -3
- package/rules/php/js/docs/tooling.md +10 -10
- package/rules/php/{lint/lint.mjs → main.mjs} +32 -6
- package/rules/python/docs/fix.md +11 -11
- package/rules/python/docs/index.md +4 -3
- package/rules/python/docs/main.md +31 -0
- package/rules/python/js/docs/index.md +3 -3
- package/rules/python/js/docs/tooling.md +17 -17
- package/rules/python/{lint/lint.mjs → main.mjs} +31 -6
- package/rules/rego/docs/fix.md +5 -5
- package/rules/rego/docs/index.md +4 -3
- package/rules/rego/docs/main.md +37 -0
- package/rules/rego/js/docs/index.md +3 -3
- package/rules/rego/{lint/lint.mjs → main.mjs} +27 -5
- package/rules/release/docs/index.md +5 -4
- package/rules/release/docs/main.md +29 -0
- package/rules/release/docs/release.md +0 -3
- package/rules/release/lib/docs/index.md +4 -4
- package/rules/release/release.mdc +10 -0
- package/rules/rust/docs/fix.md +4 -4
- package/rules/rust/docs/index.md +4 -3
- package/rules/rust/docs/main.md +27 -0
- package/rules/rust/js/docs/index.md +3 -3
- package/rules/rust/lib/docs/index.md +2 -2
- package/rules/rust/{js/lint.mjs → main.mjs} +27 -4
- package/rules/security/docs/fix.md +6 -6
- package/rules/security/docs/index.md +4 -3
- package/rules/security/docs/main.md +28 -0
- package/rules/security/js/docs/index.md +4 -4
- package/rules/security/main.mjs +45 -0
- package/rules/style-lint/docs/fix.md +3 -3
- package/rules/style-lint/docs/index.md +4 -3
- package/rules/style-lint/docs/main.md +29 -0
- package/rules/style-lint/js/docs/index.md +3 -3
- package/rules/style-lint/{js/lint.mjs → main.mjs} +19 -1
- package/rules/tauri/docs/fix.md +11 -11
- package/rules/tauri/docs/index.md +4 -3
- package/rules/tauri/docs/main.md +29 -0
- package/rules/tauri/js/docs/index.md +3 -3
- package/rules/tauri/main.mjs +20 -0
- package/rules/test/docs/fix.md +5 -5
- package/rules/test/docs/index.md +4 -3
- package/rules/test/docs/main.md +30 -0
- package/rules/test/js/data/stryker_config/docs/index.md +4 -4
- package/rules/test/js/data/vitest_config/docs/index.md +2 -2
- package/rules/test/js/docs/index.md +7 -7
- package/rules/test/main.mjs +20 -0
- package/rules/text/docs/fix.md +11 -11
- package/rules/text/docs/index.md +4 -3
- package/rules/text/docs/main.md +29 -0
- package/rules/text/{lint → js}/cspell-fix.mjs +7 -2
- 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 +8 -4
- 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} +41 -10
- package/rules/tool-surface/docs/index.md +4 -3
- package/rules/tool-surface/docs/main.md +29 -0
- package/rules/tool-surface/main.mjs +20 -0
- package/rules/tool-surface/meta.json +6 -1
- package/rules/vue/docs/fix.md +6 -6
- package/rules/vue/docs/index.md +4 -3
- package/rules/vue/docs/main.md +29 -0
- package/rules/vue/js/docs/index.md +2 -2
- package/rules/vue/lib/docs/index.md +2 -2
- package/rules/vue/main.mjs +20 -0
- package/rules/worktree/docs/fix.md +11 -11
- package/rules/worktree/docs/index.md +4 -3
- package/rules/worktree/docs/main.md +28 -0
- package/rules/worktree/main.mjs +20 -0
- package/scripts/coverage-classify/docs/index.md +6 -6
- package/scripts/dispatcher/docs/index.md +2 -2
- package/scripts/docs/index.md +16 -15
- 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 +36 -35
- 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 +29 -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/analyze-escalation.mjs +4 -1
- package/scripts/lib/fix/docs/index.md +11 -10
- package/scripts/lib/fix/docs/orchestrator.md +23 -18
- package/scripts/lib/fix/docs/run-conformance-check.md +33 -0
- package/scripts/lib/fix/docs/run-fix-check.md +3 -3
- package/scripts/lib/fix/docs/t0.md +10 -9
- package/scripts/lib/fix/orchestrator.mjs +31 -8
- package/scripts/lib/fix/{run-fix-check.mjs → run-conformance-check.mjs} +13 -13
- package/scripts/lib/fix/t0.mjs +6 -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 -22
- 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/scripts/utils/docs/index.md +14 -14
- package/skills/doc-aggregate/js/docs/index.md +3 -3
- package/skills/doc-files/.changes/260612-0002.md +1 -0
- package/skills/doc-files/.changes/260612-0006.md +1 -0
- package/skills/doc-files/.changes/260612-0008.md +1 -0
- package/skills/doc-files/.changes/260612-0012.md +1 -0
- package/skills/doc-files/.changes/260612-0031.md +1 -0
- package/skills/doc-files/.changes/260612-0036.md +1 -0
- package/skills/doc-files/.changes/260612-0114.md +1 -0
- package/skills/start-check/js/docs/index.md +2 -2
- package/skills/taze/js/docs/index.md +2 -2
- package/types/bin/n-cursor.d.ts +1 -1
- 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}/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
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/** @see ./docs/tooling.md */
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { copyFile, readFile } from 'node:fs/promises'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
|
|
6
|
+
import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
|
|
7
|
+
|
|
8
|
+
import { KNIP_CANONICAL_JSON_PATH, OXLINT_CANONICAL_JSON_PATH, verifyOxlintRcAgainstCanonical } from './tooling.mjs'
|
|
9
|
+
|
|
10
|
+
const NON_DIGITS_RE = /\D+/u
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Перевіряє ESLint flat config файл.
|
|
14
|
+
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
15
|
+
* @param {(msg: string) => void} failFn callback при помилці
|
|
16
|
+
* @param {string} cwd корінь репозиторію
|
|
17
|
+
*/
|
|
18
|
+
async function checkEslintConfig(passFn, failFn, cwd) {
|
|
19
|
+
let eslintPath
|
|
20
|
+
if (existsSync(join(cwd, 'eslint.config.js'))) {
|
|
21
|
+
eslintPath = 'eslint.config.js'
|
|
22
|
+
passFn('eslint.config.js існує')
|
|
23
|
+
} else if (existsSync(join(cwd, 'eslint.config.mjs'))) {
|
|
24
|
+
eslintPath = 'eslint.config.mjs'
|
|
25
|
+
passFn('eslint.config.mjs існує')
|
|
26
|
+
} else {
|
|
27
|
+
failFn('Відсутній eslint.config.js або eslint.config.mjs — flat config з getConfig (js-lint.mdc)')
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
const eslintRaw = await readFile(join(cwd, eslintPath), 'utf8')
|
|
31
|
+
const checks = [
|
|
32
|
+
{
|
|
33
|
+
needle: 'getConfig',
|
|
34
|
+
ok: `${eslintPath}: містить getConfig`,
|
|
35
|
+
err: `${eslintPath}: потрібен виклик getConfig (js-lint.mdc)`
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
needle: '@nitra/eslint-config',
|
|
39
|
+
ok: `${eslintPath}: імпорт @nitra/eslint-config`,
|
|
40
|
+
err: `${eslintPath}: імпортуй getConfig з @nitra/eslint-config`
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
needle: '**/auto-imports.d.ts',
|
|
44
|
+
ok: `${eslintPath}: ignores містить **/auto-imports.d.ts`,
|
|
45
|
+
err: `${eslintPath}: додай у ignores запис **/auto-imports.d.ts (js-lint.mdc)`
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
for (const { needle, ok, err } of checks) {
|
|
49
|
+
if (eslintRaw.includes(needle)) {
|
|
50
|
+
passFn(ok)
|
|
51
|
+
} else {
|
|
52
|
+
failFn(err)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Перевірки `prettier` / `@nitra/prettier-config` у залежностях (text.mdc) і
|
|
58
|
+
// `@nitra/eslint-config ≥ 3.10.0` тепер у Rego: відповідно
|
|
59
|
+
// `npm/policy/text/package_json/` і `npm/policy/js_lint/package_json/`. Тут
|
|
60
|
+
// лишилася лише workspace-ітерація для `type: "module"` і engines, бо js_lint
|
|
61
|
+
// Rego запускається лише на кореневому `package.json`.
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Перевіряє, що package.json має `"type": "module"`.
|
|
65
|
+
* @param {string} label шлях або назва пакета для повідомлень
|
|
66
|
+
* @param {{ type?: string }} pkg parsed package.json
|
|
67
|
+
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
68
|
+
* @param {(msg: string) => void} failFn callback при помилці
|
|
69
|
+
*/
|
|
70
|
+
function checkPackageJsonTypeModule(label, pkg, passFn, failFn) {
|
|
71
|
+
if (pkg.type === 'module') {
|
|
72
|
+
passFn(`${label}: "type": "module"`)
|
|
73
|
+
} else {
|
|
74
|
+
failFn(`${label}: має містити "type": "module" (js-lint.mdc)`)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* `"type": "module"`, `engines.node >= 24` і `engines.bun >= 1.3` у кожному workspace `package.json`.
|
|
80
|
+
* @param {unknown[]} workspaces поле workspaces з package.json
|
|
81
|
+
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
82
|
+
* @param {(msg: string) => void} failFn callback при помилці
|
|
83
|
+
* @param {string} cwd корінь репозиторію
|
|
84
|
+
*/
|
|
85
|
+
async function checkWorkspacePackages(workspaces, passFn, failFn, cwd) {
|
|
86
|
+
for (const ws of workspaces) {
|
|
87
|
+
const wsPkgRel = `${ws}/package.json`
|
|
88
|
+
const wsPkgAbs = join(cwd, wsPkgRel)
|
|
89
|
+
if (existsSync(wsPkgAbs)) {
|
|
90
|
+
const wsPkg = JSON.parse(await readFile(wsPkgAbs, 'utf8'))
|
|
91
|
+
checkPackageJsonTypeModule(wsPkgRel, wsPkg, passFn, failFn)
|
|
92
|
+
checkEnginesNode(wsPkgRel, wsPkg, passFn, failFn)
|
|
93
|
+
checkEnginesBun(wsPkgRel, wsPkg, passFn, failFn)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* engines.node >= 24.
|
|
100
|
+
* @param {string} label шлях або назва пакета для повідомлень
|
|
101
|
+
* @param {{ engines?: { node?: string } }} pkg розпарсений package.json
|
|
102
|
+
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
103
|
+
* @param {(msg: string) => void} failFn callback при помилці
|
|
104
|
+
*/
|
|
105
|
+
function checkEnginesNode(label, pkg, passFn, failFn) {
|
|
106
|
+
const nodeEngine = pkg.engines?.node
|
|
107
|
+
if (nodeEngine) {
|
|
108
|
+
const firstNumeric = String(nodeEngine).split(NON_DIGITS_RE).find(Boolean)
|
|
109
|
+
if (firstNumeric && Number(firstNumeric) >= 24) {
|
|
110
|
+
passFn(`${label}: engines.node "${nodeEngine}"`)
|
|
111
|
+
} else {
|
|
112
|
+
failFn(`${label}: engines.node "${nodeEngine}" — має бути >=24`)
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
failFn(`${label} не містить engines.node — додай: "engines": { "node": ">=24" }`)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* engines.bun >= 1.3.
|
|
121
|
+
* @param {string} label шлях або назва пакета для повідомлень
|
|
122
|
+
* @param {{ engines?: { bun?: string } }} pkg розпарсений package.json
|
|
123
|
+
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
124
|
+
* @param {(msg: string) => void} failFn callback при помилці
|
|
125
|
+
*/
|
|
126
|
+
function checkEnginesBun(label, pkg, passFn, failFn) {
|
|
127
|
+
const bunEngine = pkg.engines?.bun
|
|
128
|
+
if (bunEngine) {
|
|
129
|
+
const [major, minor] = String(bunEngine).split(NON_DIGITS_RE).filter(Boolean).map(Number)
|
|
130
|
+
if (Number.isFinite(major) && Number.isFinite(minor) && (major > 1 || (major === 1 && minor >= 3))) {
|
|
131
|
+
passFn(`${label}: engines.bun "${bunEngine}"`)
|
|
132
|
+
} else {
|
|
133
|
+
failFn(`${label}: engines.bun "${bunEngine}" — має бути >=1.3`)
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
failFn(`${label} не містить engines.bun — додай: "engines": { "bun": ">=1.3" }`)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Workspace-ітерація: для кожного workspace `package.json` перевіряємо
|
|
142
|
+
* `type: "module"` і `engines.{node,bun}`. Кореневий `package.json` ці поля
|
|
143
|
+
* валідує `npm/policy/js_lint/package_json/`; lint-js скрипт і `@nitra/eslint-config`
|
|
144
|
+
* — теж у Rego.
|
|
145
|
+
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
146
|
+
* @param {(msg: string) => void} failFn callback при помилці
|
|
147
|
+
* @param {string} cwd корінь репозиторію
|
|
148
|
+
*/
|
|
149
|
+
async function checkPackageJsonJsLint(passFn, failFn, cwd) {
|
|
150
|
+
const pkgPath = join(cwd, 'package.json')
|
|
151
|
+
if (!existsSync(pkgPath)) return
|
|
152
|
+
const pkg = JSON.parse(await readFile(pkgPath, 'utf8'))
|
|
153
|
+
const workspaces = Array.isArray(pkg.workspaces) ? pkg.workspaces : []
|
|
154
|
+
await checkWorkspacePackages(workspaces, passFn, failFn, cwd)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Перевіряє .oxlintrc.json.
|
|
159
|
+
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
160
|
+
* @param {(msg: string) => void} failFn callback при помилці
|
|
161
|
+
* @param {string} cwd корінь репозиторію
|
|
162
|
+
*/
|
|
163
|
+
async function checkOxlintRc(passFn, failFn, cwd) {
|
|
164
|
+
const oxPath = join(cwd, '.oxlintrc.json')
|
|
165
|
+
if (!existsSync(oxPath)) {
|
|
166
|
+
failFn('.oxlintrc.json не існує — додай конфіг oxlint (js-lint.mdc)')
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
let oxCfg
|
|
170
|
+
try {
|
|
171
|
+
oxCfg = JSON.parse(await readFile(oxPath, 'utf8'))
|
|
172
|
+
} catch {
|
|
173
|
+
failFn('.oxlintrc.json не є валідним JSON')
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
passFn('.oxlintrc.json існує')
|
|
177
|
+
let canonical
|
|
178
|
+
try {
|
|
179
|
+
canonical = JSON.parse(await readFile(OXLINT_CANONICAL_JSON_PATH, 'utf8'))
|
|
180
|
+
} catch {
|
|
181
|
+
failFn('внутрішня помилка: не вдалося прочитати канон oxlint з пакета @nitra/cursor')
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
const oxV = verifyOxlintRcAgainstCanonical(oxCfg, canonical)
|
|
185
|
+
if (oxV.ok) {
|
|
186
|
+
passFn('.oxlintrc.json збігається з каноном oxlint (@nitra/cursor)')
|
|
187
|
+
} else {
|
|
188
|
+
for (const msg of oxV.failures) {
|
|
189
|
+
failFn(msg)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* FS-existence для `lint-js.yml` + cross-file перевірка, що `lint.yml` (якщо існує)
|
|
196
|
+
* не дублює лінт JS-кроки. Структуру `lint-js.yml` (`actions/checkout@v6`,
|
|
197
|
+
* `persist-credentials: false`, `setup-bun-deps`, `bunx oxlint/eslint/jscpd .`,
|
|
198
|
+
* заборона `--fix` у CI) валідує `npm/policy/js_lint/lint_js_yml/`.
|
|
199
|
+
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
200
|
+
* @param {(msg: string) => void} failFn callback при помилці
|
|
201
|
+
* @param {string} cwd корінь репозиторію
|
|
202
|
+
*/
|
|
203
|
+
async function checkLintJsWorkflows(passFn, failFn, cwd) {
|
|
204
|
+
if (existsSync(join(cwd, '.github/workflows/lint-js.yml'))) {
|
|
205
|
+
passFn('.github/workflows/lint-js.yml є (структуру перевіряє npx @nitra/cursor fix → js_lint.lint_js_yml)')
|
|
206
|
+
} else {
|
|
207
|
+
failFn('.github/workflows/lint-js.yml не існує — створи його (js-lint.mdc)')
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const lintYmlPath = join(cwd, '.github/workflows/lint.yml')
|
|
211
|
+
if (existsSync(lintYmlPath)) {
|
|
212
|
+
const lintYml = await readFile(lintYmlPath, 'utf8')
|
|
213
|
+
if (lintYml.includes('bunx oxlint') && lintYml.includes('bunx eslint') && lintYml.includes('jscpd')) {
|
|
214
|
+
failFn('.github/workflows/lint.yml дублює кроки lint-js.yml — залиш один workflow на лінт JS (js-lint.mdc)')
|
|
215
|
+
} else {
|
|
216
|
+
passFn('.github/workflows/lint.yml не дублює oxlint/eslint/jscpd з lint-js.yml')
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Перевіряє наявність `knip.json` у корені проєкту. Якщо файл відсутній —
|
|
223
|
+
* копіює канонічний `knip-canonical.json` з пакета `@nitra/cursor` як стартовий
|
|
224
|
+
* baseline; зміст подальших модифікацій локально не валідується (`entry` /
|
|
225
|
+
* `project` / `ignore` / `ignoreDependencies` / `ignoreBinaries` дозволені
|
|
226
|
+
* будь-які; це side effect — описано у js-lint.mdc).
|
|
227
|
+
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
228
|
+
* @param {(msg: string) => void} failFn callback при помилці
|
|
229
|
+
* @param {string} cwd корінь репозиторію
|
|
230
|
+
*/
|
|
231
|
+
async function checkKnipConfig(passFn, failFn, cwd) {
|
|
232
|
+
const knipPath = join(cwd, 'knip.json')
|
|
233
|
+
if (existsSync(knipPath)) {
|
|
234
|
+
passFn('knip.json існує')
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
if (!existsSync(KNIP_CANONICAL_JSON_PATH)) {
|
|
238
|
+
failFn(
|
|
239
|
+
`knip.json відсутній, і канонічний шаблон у пакеті не знайдено (${KNIP_CANONICAL_JSON_PATH}) — ` +
|
|
240
|
+
'перевстанови @nitra/cursor'
|
|
241
|
+
)
|
|
242
|
+
return
|
|
243
|
+
}
|
|
244
|
+
await copyFile(KNIP_CANONICAL_JSON_PATH, knipPath)
|
|
245
|
+
passFn('knip.json створено з канонічного npm/rules/js-lint/js/data/tooling/knip-canonical.json (js-lint.mdc)')
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Перевіряє відповідність проєкту правилам js-lint.mdc
|
|
250
|
+
* @param {string} [cwd] корінь репозиторію
|
|
251
|
+
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
252
|
+
*/
|
|
253
|
+
export async function check(cwd = process.cwd()) {
|
|
254
|
+
const reporter = createCheckReporter()
|
|
255
|
+
const { pass, fail } = reporter
|
|
256
|
+
|
|
257
|
+
await checkEslintConfig(pass, fail, cwd)
|
|
258
|
+
await checkPackageJsonJsLint(pass, fail, cwd)
|
|
259
|
+
await checkOxlintRc(pass, fail, cwd)
|
|
260
|
+
await checkLintJsWorkflows(pass, fail, cwd)
|
|
261
|
+
await checkKnipConfig(pass, fail, cwd)
|
|
262
|
+
|
|
263
|
+
for (const dup of ['.eslintrc', '.eslintrc.js', '.eslintrc.json', '.eslintrc.yml']) {
|
|
264
|
+
if (existsSync(join(cwd, dup))) fail(`Знайдено застарілий конфіг ESLint: ${dup} — видали, використовуй flat config`)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return reporter.getExitCode()
|
|
268
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: check.mjs
|
|
4
|
+
resource: npm/rules/js-lint/js/check.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: f61768f2
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Огляд
|
|
12
|
+
|
|
13
|
+
Модуль виконує перевірку конфігураційних файлів проєкту, забезпечуючи їхню відповідність встановленим стандартам. Він читає та аналізує конфігураційні файли, включаючи `package.json`, `.oxlintrc.json`, `knip.json`, `knip-canonical.json` та `.eslintrc.json`. Перевірка ґрунтується на логіці, визначеній у (js-lint.mdc) та (text.mdc). Функціонал реалізований у режимі лише читання, що означає відсутність змін у файловій системі чи базах даних. При виявленні проблем, система перехоплює помилки, працюючи у режимі fail-safe. Свідомо ігноруються шляхи `.github` та `.git`.
|
|
14
|
+
|
|
15
|
+
## Поведінка
|
|
16
|
+
|
|
17
|
+
1. Викликає `check` для початку перевірки.
|
|
18
|
+
2. Перевіряє наявність та відповідність конфігурації ESLint.
|
|
19
|
+
3. Перевіряє конфігурацію `package.json` для всіх робочих просторів:
|
|
20
|
+
а. Перевіряє, чи має `package.json` `"type": "module"`.
|
|
21
|
+
б. Перевіряє, чи містить `package.json` `engines.node` з версією не менше 24.
|
|
22
|
+
в. Перевіряє, чи містить `package.json` `engines.bun` з версією не менше 1.3.
|
|
23
|
+
4. Перевіряє конфігураційний файл `.oxlintrc.json` на відповідність канонічному шаблону.
|
|
24
|
+
5. Перевіряє файли робочих процесів у `.github/workflows/`:
|
|
25
|
+
а. Перевіряє наявність `lint-js.yml`.
|
|
26
|
+
б. Перевіряє, чи не дублює `lint.yml` кроки лінтінгу JS.
|
|
27
|
+
6. Перевіряє наявність файлу `knip.json` у корені проєкту. Якщо він відсутній, копіює канонічний шаблон.
|
|
28
|
+
7. Перевіряє наявність застарілих конфігурацій ESLint (`.eslintrc`, `.eslintrc.js`, `.eslintrc.json`, `.eslintrc.yml`) та повідомляє про їхнє існування.
|
|
29
|
+
8. Повертає код виходу, що відображає загальний статус перевірки.
|
|
30
|
+
|
|
31
|
+
## Публічний API
|
|
32
|
+
|
|
33
|
+
check — забезпечує відповідність проєкту вимогам, описаним у js-lint.mdc.
|
|
34
|
+
|
|
35
|
+
## Гарантії поведінки
|
|
36
|
+
|
|
37
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
|
38
|
+
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
39
|
+
- Свідомо пропускає шляхи: `.github`, `.git`.
|
|
@@ -6,9 +6,9 @@ resource: npm/rules/js-lint/js/
|
|
|
6
6
|
|
|
7
7
|
# npm/rules/js-lint/js
|
|
8
8
|
|
|
9
|
-
| Файл
|
|
10
|
-
|
|
9
|
+
| Файл | Тип |
|
|
10
|
+
| ------------------------------------- | --------- |
|
|
11
|
+
| [check.mjs](check.md) | JS Module |
|
|
11
12
|
| [lint-findings.mjs](lint-findings.md) | JS Module |
|
|
12
|
-
| [
|
|
13
|
-
| [tooling.mjs](tooling.md) | JS Module |
|
|
13
|
+
| [tooling.mjs](tooling.md) | JS Module |
|
|
14
14
|
| [utils_imports.mjs](utils_imports.md) | JS Module |
|
|
@@ -3,47 +3,27 @@ type: JS Module
|
|
|
3
3
|
title: tooling.mjs
|
|
4
4
|
resource: npm/rules/js-lint/js/tooling.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
7
|
-
|
|
6
|
+
crc: 7ead48ee
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 95
|
|
8
9
|
---
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
Повертає шлях до канонічного JSON-файлу oxlint у пакету
|
|
11
|
+
## Огляд
|
|
12
12
|
|
|
13
|
-
KNIP_CANONICAL_JSON_PATH
|
|
14
|
-
Повертає шлях до канонічного JSON-файлу knip у пакету
|
|
15
|
-
|
|
16
|
-
verifyOxlintRcAgainstCanonical
|
|
17
|
-
Звіряє блок rules з `.oxlintrc.json` проти канону пакета @nitra/cursor
|
|
18
|
-
|
|
19
|
-
check
|
|
20
|
-
Перевіряє конфігурацію проєкту відповідно до правил js-lint.mdc
|
|
13
|
+
Визначає шляхи до канонічних конфігураційних файлів для oxlint та knip за допомогою OXLINT_CANONICAL_JSON_PATH та KNIP_CANONICAL_JSON_PATH. Дозволяє перевіряти відповідність конфігураційного файлу .oxlintrc.json до канонічного файлу oxlint-canonical.json за допомогою verifyOxlintRcAgainstCanonical.
|
|
21
14
|
|
|
22
15
|
## Поведінка
|
|
23
16
|
|
|
24
|
-
OXLINT_CANONICAL_JSON_PATH
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
KNIP_CANONICAL_JSON_PATH
|
|
28
|
-
Шлях до канонічного knip JSON у цьому пакеті
|
|
29
|
-
|
|
30
|
-
verifyOxlintRcAgainstCanonical
|
|
31
|
-
Звіряє блок rules з `.oxlintrc.json` проти канону пакета @nitra/cursor
|
|
32
|
-
|
|
33
|
-
check
|
|
34
|
-
Перевіряє конфігурацію проєкту відповідно до правил js-lint.mdc
|
|
17
|
+
OXLINT_CANONICAL_JSON_PATH — Вказує шлях до канонічного JSON-файлу oxlint у цьому пакеті.
|
|
18
|
+
KNIP_CANONICAL_JSON_PATH — Вказує шлях до канонічного JSON-файлу knip у цьому пакеті.
|
|
19
|
+
verifyOxlintRcAgainstCanonical — Перевіряє конфігураційний файл `.oxlintrc.json` на відповідність канонічному файлу oxlint-canonical.json, виявляючи відхилення у правилах та інших полях.
|
|
35
20
|
|
|
36
21
|
## Публічний API
|
|
37
22
|
|
|
38
|
-
OXLINT_CANONICAL_JSON_PATH —
|
|
39
|
-
KNIP_CANONICAL_JSON_PATH —
|
|
40
|
-
verifyOxlintRcAgainstCanonical — Порівнює `.oxlintrc.json` з
|
|
41
|
-
check — Перевіряє відповідність проєкту правилам js-lint.mdc
|
|
23
|
+
OXLINT_CANONICAL_JSON_PATH — Вказує розташування стандартного конфігураційного файлу oxlint для валідації.
|
|
24
|
+
KNIP_CANONICAL_JSON_PATH — Вказує розташування стандартного конфігураційного файлу knip, який копіюється у кореневий каталог проєкту, якщо його там немає.
|
|
25
|
+
verifyOxlintRcAgainstCanonical — Порівнює конфігураційний файл `.oxlintrc.json` з канонічним файлом пакета, вимагаючи збігу всіх полів, крім додаткових ключів у секції `rules`.
|
|
42
26
|
|
|
43
27
|
## Гарантії поведінки
|
|
44
28
|
|
|
45
|
-
- Read-only:
|
|
46
|
-
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
47
|
-
- За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
|
|
48
|
-
- Свідомо пропускає шляхи: `.github`, `.git`.
|
|
49
|
-
- Не звертається до мережі.
|
|
29
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
|
@@ -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
|
-
}
|