@nitra/cursor 3.22.0 → 3.23.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/.pi-template/extensions/n-cursor-adr/docs/index.md +181 -0
- package/CHANGELOG.md +31 -3
- package/bin/docs/n-cursor.md +636 -0
- package/bin/docs/rename-yaml-extensions.md +207 -0
- package/bin/n-cursor.js +30 -3
- package/package.json +1 -1
- package/rules/abie/docs/fix.md +18 -0
- package/rules/abie/js/docs/applies.md +26 -0
- package/rules/abie/js/docs/env_dns.md +32 -0
- package/rules/abie/js/docs/firebase_hosting.md +23 -0
- package/rules/abie/js/docs/hc_pairing.md +35 -0
- package/rules/abie/js/docs/ua_http_route.md +28 -0
- package/rules/abie/js/docs/ua_node_selector.md +28 -0
- package/rules/abie/lib/docs/enabled.md +29 -0
- package/rules/abie/lib/docs/env-dns.md +35 -0
- package/rules/abie/lib/docs/hc-yaml.md +33 -0
- package/rules/abie/lib/docs/http-route.md +44 -0
- package/rules/abie/lib/docs/k8s-tree.md +40 -0
- package/rules/abie/lib/docs/kustomization-patches.md +47 -0
- package/rules/abie/lib/docs/overlay-paths.md +38 -0
- package/rules/abie/lib/docs/yaml.md +29 -0
- package/rules/adr/docs/fix.md +148 -0
- package/rules/adr/js/docs/hooks.md +259 -0
- package/rules/bun/docs/fix.md +156 -0
- package/rules/bun/js/docs/layout.md +393 -0
- package/rules/capacitor/docs/fix.md +121 -0
- package/rules/capacitor/js/docs/platforms.md +295 -0
- package/rules/changelog/changelog.mdc +2 -2
- package/rules/changelog/docs/fix.md +174 -0
- package/rules/changelog/js/consistency.mjs +114 -13
- package/rules/changelog/js/docs/consistency.md +387 -0
- package/rules/changelog/lib/docs/package-manifest.md +210 -0
- package/rules/ci4/docs/fix.md +179 -0
- package/rules/ci4/js/docs/marksman_config.md +128 -0
- package/rules/docker/docker.mdc +8 -3
- package/rules/docker/docs/fix.md +171 -0
- package/rules/docker/js/docs/lint.md +258 -0
- package/rules/docker/lib/docs/docker-hadolint.md +184 -0
- package/rules/docker/lib/docs/docker-mirror.md +247 -0
- package/rules/docker/lib/docs/docker-native-addon.md +170 -0
- package/rules/docker/lib/docs/docker-nginx-user.md +219 -0
- package/rules/docker/lint/docs/lint.md +193 -0
- package/rules/efes/docs/fix.md +203 -0
- package/rules/feedback/docs/fix.md +140 -0
- package/rules/flow/docs/fix.md +152 -0
- package/rules/ga/docs/fix.md +158 -0
- package/rules/ga/js/docs/lint.md +100 -0
- package/rules/ga/js/docs/workflows.md +217 -0
- package/rules/ga/lint/docs/lint.md +209 -0
- package/rules/ga/policy/clean_merged_branch/clean_merged_branch.rego +11 -2
- package/rules/ga/policy/clean_merged_branch/template/clean-merged-branch.yml.snippet.yml +3 -4
- package/rules/graphql/docs/fix.md +126 -0
- package/rules/graphql/js/docs/tooling.md +264 -0
- package/rules/graphql/lib/docs/graphql-gql-scan.md +219 -0
- package/rules/hasura/docs/fix.md +120 -0
- package/rules/hasura/hasura.mdc +14 -0
- package/rules/hasura/js/docs/internal_urls.md +326 -0
- package/rules/image-avif/docs/fix.md +132 -0
- package/rules/image-avif/js/docs/avif_generation.md +241 -0
- package/rules/image-compress/docs/fix.md +150 -0
- package/rules/image-compress/js/docs/package_setup.md +191 -0
- package/rules/js-bun-db/docs/fix.md +148 -0
- package/rules/js-bun-db/js/docs/safety.md +231 -0
- package/rules/js-bun-db/js-bun-db.mdc +42 -13
- package/rules/js-bun-db/lib/docs/bun-sql-scan.md +347 -0
- package/rules/js-bun-redis/docs/fix.md +123 -0
- package/rules/js-bun-redis/js/docs/imports.md +176 -0
- package/rules/js-bun-redis/lib/docs/redis-imports.md +223 -0
- package/rules/js-lint/docs/fix.md +117 -0
- package/rules/js-lint/js/docs/lint.md +250 -0
- package/rules/js-lint/js/docs/tooling.md +348 -0
- package/rules/js-lint/js/docs/utils_imports.md +207 -0
- package/rules/js-lint-ci/docs/fix.md +154 -0
- package/rules/js-lint-ci/js/docs/lint.md +144 -0
- package/rules/js-mssql/docs/fix.md +128 -0
- package/rules/js-mssql/js/docs/deps.md +263 -0
- package/rules/js-mssql/lib/docs/mssql-pool-scan.md +367 -0
- package/rules/js-run/docs/fix.md +144 -0
- package/rules/js-run/js/docs/runtime.md +388 -0
- package/rules/js-run/lib/docs/bunyan-imports.md +117 -0
- package/rules/js-run/lib/docs/check-env-scan.md +433 -0
- package/rules/js-run/lib/docs/conn-file-rules.md +300 -0
- package/rules/js-run/lib/docs/conn-imports-scan.md +204 -0
- package/rules/js-run/lib/docs/promise-settimeout-scan.md +326 -0
- package/rules/k8s/docs/fix.md +129 -0
- package/rules/k8s/js/docs/manifests.md +344 -0
- package/rules/k8s/js/manifests.mjs +6 -2
- package/rules/k8s/k8s.mdc +4 -2
- package/rules/k8s/lint/docs/lint.md +411 -0
- package/rules/k8s/policy/network_policy/template/deployment.snippet.yaml +2 -0
- package/rules/k8s/policy/network_policy/template/stateful-set.snippet.yaml +2 -0
- package/rules/nginx-default-tpl/docs/fix.md +124 -0
- package/rules/nginx-default-tpl/js/docs/template.md +378 -0
- package/rules/npm-module/docs/fix.md +98 -0
- package/rules/npm-module/js/docs/package_structure.md +274 -0
- package/rules/npm-module/js/docs/rule_meta.md +137 -0
- package/rules/npm-module/js/docs/skill_meta.md +190 -0
- package/rules/php/docs/fix.md +107 -0
- package/rules/php/js/docs/tooling.md +152 -0
- package/rules/php/lint/docs/lint.md +215 -0
- package/rules/python/docs/fix.md +163 -0
- package/rules/python/js/docs/applies.md +108 -0
- package/rules/python/js/docs/tooling.md +153 -0
- package/rules/python/lint/docs/lint.md +322 -0
- package/rules/rego/docs/fix.md +121 -0
- package/rules/rego/js/docs/applies.md +174 -0
- package/rules/rego/js/docs/lint.md +118 -0
- package/rules/rego/lint/docs/lint.md +204 -0
- package/rules/release/docs/change.md +185 -0
- package/rules/release/docs/fix.md +119 -0
- package/rules/release/docs/release.md +222 -0
- package/rules/release/lib/docs/aggregate.md +246 -0
- package/rules/release/lib/docs/change-file.md +200 -0
- package/rules/release/lib/docs/fallback.md +203 -0
- package/rules/rust/docs/fix.md +129 -0
- package/rules/rust/js/docs/applies.md +140 -0
- package/rules/rust/lib/docs/has-cargo-toml.md +130 -0
- package/rules/security/docs/fix.md +86 -0
- package/rules/security/js/docs/lint.md +171 -0
- package/rules/security/js/docs/sample_secret.md +190 -0
- package/rules/security/js/docs/trufflehog.md +137 -0
- package/rules/security/js/lint.mjs +9 -1
- package/rules/style-lint/docs/fix.md +155 -0
- package/rules/style-lint/js/docs/lint.md +184 -0
- package/rules/style-lint/js/docs/tooling.md +194 -0
- package/rules/tauri/docs/fix.md +158 -0
- package/rules/tauri/js/docs/cargo_mutants_config.md +168 -0
- package/rules/tauri/js/docs/tooling.md +228 -0
- package/rules/test/coverage/coverage.mjs +15 -3
- package/rules/test/docs/fix.md +132 -0
- package/rules/test/js/data/stryker_config/docs/stryker-vue-macros-ignorer.md +138 -0
- package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +134 -0
- package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +160 -0
- package/rules/test/js/data/vitest_config/docs/vitest.config.baseline.md +195 -0
- package/rules/test/js/docs/cargo_mutants_config.md +173 -0
- package/rules/test/js/docs/location.md +136 -0
- package/rules/test/js/docs/no-process-chdir.md +160 -0
- package/rules/test/js/docs/no-relative-fs-path.md +271 -0
- package/rules/test/js/docs/stryker_config.md +152 -0
- package/rules/test/js/docs/vitest-config-pool-forks.md +174 -0
- package/rules/text/docs/fix.md +118 -0
- package/rules/text/js/docs/forbidden-prettier.md +143 -0
- package/rules/text/js/docs/formatting.md +256 -0
- package/rules/text/js/docs/lint.md +122 -0
- package/rules/text/lint/docs/lint.md +220 -0
- package/rules/text/lint/docs/run-dotenv-linter.md +157 -0
- package/rules/text/lint/docs/run-shellcheck.md +212 -0
- package/rules/text/lint/docs/run-v8r.md +197 -0
- package/rules/vue/docs/fix.md +127 -0
- package/rules/vue/js/docs/packages.md +335 -0
- package/rules/vue/lib/docs/vue-forbidden-imports.md +261 -0
- package/rules/worktree/docs/fix.md +161 -0
- package/schemas/rule-meta.json +5 -1
- package/scripts/auto-rules.mjs +7 -4
- package/scripts/coverage-classify/docs/apply.md +202 -0
- package/scripts/coverage-classify/docs/cache.md +203 -0
- package/scripts/coverage-classify/docs/index.md +218 -0
- package/scripts/coverage-classify/docs/prompt.md +132 -0
- package/scripts/coverage-classify/docs/verdict-schema.md +169 -0
- package/scripts/coverage-fix-extract.mjs +122 -0
- package/scripts/coverage-fix.mjs +1 -1
- package/scripts/dispatcher/docs/graph.md +346 -0
- package/scripts/dispatcher/docs/index.md +236 -0
- package/scripts/dispatcher/docs/trace.md +296 -0
- package/scripts/dispatcher/index.mjs +1 -1
- package/scripts/dispatcher/lib/active.mjs +4 -8
- package/scripts/dispatcher/lib/commands.mjs +7 -11
- package/scripts/dispatcher/lib/docs/active.md +348 -0
- package/scripts/dispatcher/lib/docs/artifact.md +232 -0
- package/scripts/dispatcher/lib/docs/budget.md +167 -0
- package/scripts/dispatcher/lib/docs/capability.md +196 -0
- package/scripts/dispatcher/lib/docs/commands.md +210 -0
- package/scripts/dispatcher/lib/docs/events.md +182 -0
- package/scripts/dispatcher/lib/docs/executor.md +190 -0
- package/scripts/dispatcher/lib/docs/flow-lock.md +161 -0
- package/scripts/dispatcher/lib/docs/flow-resolve.md +267 -0
- package/scripts/dispatcher/lib/docs/gate.md +231 -0
- package/scripts/dispatcher/lib/docs/level.md +335 -0
- package/scripts/dispatcher/lib/docs/plan-panel.md +181 -0
- package/scripts/dispatcher/lib/docs/plan.md +200 -0
- package/scripts/dispatcher/lib/docs/planner.md +269 -0
- package/scripts/dispatcher/lib/docs/review.md +255 -0
- package/scripts/dispatcher/lib/docs/reviewer.md +240 -0
- package/scripts/dispatcher/lib/docs/snapshot.md +247 -0
- package/scripts/dispatcher/lib/docs/spec.md +203 -0
- package/scripts/dispatcher/lib/docs/state-store.md +303 -0
- package/scripts/dispatcher/lib/docs/subagent-runner.md +173 -0
- package/scripts/dispatcher/lib/executor.mjs +6 -1
- package/scripts/dispatcher/lib/flow-resolve.mjs +3 -1
- package/scripts/dispatcher/lib/level.mjs +29 -3
- package/scripts/dispatcher/lib/review.mjs +1 -1
- package/scripts/dispatcher/lib/subagent-runner.mjs +5 -3
- package/scripts/docs/auto-rules.md +376 -0
- package/scripts/docs/auto-skills.md +173 -0
- package/scripts/docs/build-agents-commands.md +183 -0
- package/scripts/docs/cli-entry.md +153 -0
- package/scripts/docs/coverage-fix.md +177 -0
- package/scripts/docs/ensure-nitra-cursor-dev-dependencies.md +189 -0
- package/scripts/lib/changed-files.mjs +4 -1
- package/scripts/lib/docs/changed-files.md +149 -0
- package/scripts/lib/docs/check-mdc-template-refs.md +222 -0
- package/scripts/lib/docs/check-reporter.md +175 -0
- package/scripts/lib/docs/discover-check-rules-from-cursor.md +157 -0
- package/scripts/lib/docs/discover-checkable-rules.md +165 -0
- package/scripts/lib/docs/ensure-tool.md +254 -0
- package/scripts/lib/docs/generated-markdown.md +275 -0
- package/scripts/lib/docs/gha-workflow.md +326 -0
- package/scripts/lib/docs/inline-template-links.md +303 -0
- package/scripts/lib/docs/list-rule-ids.md +156 -0
- package/scripts/lib/docs/load-cursor-config.md +147 -0
- package/scripts/lib/docs/mirror-parity.md +167 -0
- package/scripts/lib/worktree.mjs +26 -0
- package/scripts/worktree-cli.mjs +12 -2
- package/skills/coverage-fix/SKILL.md +34 -45
- package/skills/docgen/SKILL.md +44 -23
- package/skills/docgen/bench/etalon/firebase_hosting.md +19 -0
- package/skills/docgen/bench/etalon/k8s-tree.md +24 -0
- package/skills/docgen/bench/etalon/overlay-paths.md +24 -0
- package/skills/docgen/js/docgen-ignore.mjs +54 -0
- package/skills/docgen/js/docgen-scan.mjs +37 -21
- package/skills/llm-patch/SKILL.md +23 -2
- package/skills/start-check/SKILL.md +26 -53
- package/skills/start-check/js/check.mjs +211 -0
- package/skills/taze/SKILL.md +9 -3
- package/skills/taze/js/diff.mjs +154 -0
- package/types/bin/n-cursor.d.ts +1 -1
- package/skills/fix-tests/SKILL.md +0 -119
- package/skills/fix-tests/meta.json +0 -1
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# run-dotenv-linter.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `run-dotenv-linter.mjs` інкапсулює інтеграцію зовнішнього інструмента **dotenv-linter** у ланцюжок `lint-text` правила `text` (тека `npm/rules/text/lint/`). Він виконує дві послідовні фази:
|
|
6
|
+
|
|
7
|
+
1. **Авто-фікс** — `dotenv-linter fix -r --no-backup --quiet . --exclude …`. dotenv-linter сам застосовує всі підтримувані виправлення на місці (на відміну від shellcheck, який лише пропонує diff).
|
|
8
|
+
2. **Фінальна перевірка** — `dotenv-linter check -r --quiet . --exclude …`. Якщо лишаються порушення, їх вивід ретранслюється у `stdout`/`stderr`, а функція повертає код `1`.
|
|
9
|
+
|
|
10
|
+
`dotenv-linter` — швидкий лінтер для `.env`-файлів, що ловить правила на кшталт `LowercaseKey`, `DuplicatedKey`, `IncorrectDelimiter`, `UnorderedKey` тощо. Він **очікується у `PATH`** і **не** додається в `dependencies` / `devDependencies` проєкту (та сама модель, що й `shellcheck`). Якщо бінарника немає, користувач отримує підказки встановлення (`brew`, `curl`, `cargo`), а функція повертає `1`.
|
|
11
|
+
|
|
12
|
+
Файли модуль не перераховує самостійно — це робить сам `dotenv-linter` у режимі `-r` (рекурсивний обхід дерева проєкту). З обходу виключаються `node_modules` (стороння кодова база) та `.envrc` (синтаксис direnv shell, не `key=value`). Резервні `.bak`-файли інструмент ігнорує самостійно. За відсутності `.env*`-файлів `dotenv-linter` повертає `0` ("Nothing to check").
|
|
13
|
+
|
|
14
|
+
Файл одночасно є **бібліотечним модулем** (експортує функцію `runDotenvLinter`) та **CLI-точкою входу**: за прямого запуску (`node run-dotenv-linter.mjs`) він викликає функцію та виставляє `process.exitCode` у її результат.
|
|
15
|
+
|
|
16
|
+
## Експорти / API
|
|
17
|
+
|
|
18
|
+
| Експорт | Тип | Призначення |
|
|
19
|
+
| ----------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
20
|
+
| `runDotenvLinter(cwd?)` | named function | Запуск двофазного циклу `fix` + `check` для `.env`-файлів у дереві `cwd`. Повертає числовий exit-code (`0` або `1`). |
|
|
21
|
+
|
|
22
|
+
Інші ідентифікатори модуля (`EXCLUDED_PATHS`, `printDotenvLinterInstallHints`, `buildExcludeArgs`) — внутрішні, **не** експортуються.
|
|
23
|
+
|
|
24
|
+
## Функції
|
|
25
|
+
|
|
26
|
+
### `runDotenvLinter(cwd?)`
|
|
27
|
+
|
|
28
|
+
Публічний експорт; основна логіка модуля.
|
|
29
|
+
|
|
30
|
+
- **Сигнатура:** `export function runDotenvLinter(cwd = process.cwd()): number`
|
|
31
|
+
- **Параметри:**
|
|
32
|
+
- `cwd` _(string, optional)_ — кореневий каталог для рекурсивного сканування. За замовчуванням — `process.cwd()`. Перед використанням нормалізується через `node:path#resolve` в абсолютний шлях.
|
|
33
|
+
- **Повертає:** `number`
|
|
34
|
+
- `0` — `dotenv-linter check` завершився без порушень (включно з ситуацією "Nothing to check").
|
|
35
|
+
- `1` — будь-яка з наступних умов:
|
|
36
|
+
- бінарник `dotenv-linter` не знайдено у `PATH` (`resolveCmd` повернув falsy);
|
|
37
|
+
- `spawnSync` для фази `fix` повернув `error` (наприклад, помилка спавну процесу);
|
|
38
|
+
- `spawnSync` для фази `check` повернув `error`;
|
|
39
|
+
- `check`-прогон завершився з ненульовим `status` (виявлені залишкові порушення).
|
|
40
|
+
- **Побічні ефекти:**
|
|
41
|
+
- Викликає `spawnSync` із зовнішнім бінарником `dotenv-linter` — два окремих процеси (`fix`, потім `check`).
|
|
42
|
+
- Може модифікувати файли `.env*` у дереві `cwd` (фаза `fix`). Прапорець `--no-backup` гарантує, що `.bak`-файли не створюються.
|
|
43
|
+
- Записує підказки встановлення у `process.stderr`, якщо бінарника немає.
|
|
44
|
+
- Записує повідомлення про помилку `spawnSync` у `process.stderr` (поле `.error.message`), якщо процес не вдалося запустити.
|
|
45
|
+
- На фазі `check`: при ненульовому `status` ретранслює зібрані `stdout`/`stderr` дочірнього процесу в `process.stdout` / `process.stderr` (захищено `?.length`).
|
|
46
|
+
- Не змінює глобального стану модуля; чистий exit-code-based контракт.
|
|
47
|
+
- **Контракт виклику дочірніх процесів:**
|
|
48
|
+
- Аргументи fix: `['fix', '-r', '--no-backup', '--quiet', ...exclude, '.']`.
|
|
49
|
+
- Аргументи check: `['check', '-r', '--quiet', ...exclude, '.']`.
|
|
50
|
+
- `cwd` — нормалізований `root`.
|
|
51
|
+
- `encoding: 'utf8'`, `env: process.env`, `stdio: ['ignore', 'pipe', 'pipe']` (stdin закритий, stdout/stderr захоплюються в буфер).
|
|
52
|
+
|
|
53
|
+
### `printDotenvLinterInstallHints()` _(internal)_
|
|
54
|
+
|
|
55
|
+
- **Сигнатура:** `function printDotenvLinterInstallHints(): void`
|
|
56
|
+
- **Параметри:** немає.
|
|
57
|
+
- **Повертає:** `void`.
|
|
58
|
+
- **Побічні ефекти:** Пише у `process.stderr` багаторядкове повідомлення з трьома варіантами встановлення (`brew`, `curl … sh`, `cargo install`) та заголовком `❌ dotenv-linter не знайдено в PATH.`. Завершується порожнім рядком (зручне відокремлення в логах).
|
|
59
|
+
|
|
60
|
+
### `buildExcludeArgs()` _(internal)_
|
|
61
|
+
|
|
62
|
+
- **Сигнатура:** `function buildExcludeArgs(): string[]`
|
|
63
|
+
- **Параметри:** немає.
|
|
64
|
+
- **Повертає:** плоский масив аргументів CLI у форматі, який очікує `dotenv-linter`: `['--exclude', 'node_modules', '--exclude', '.envrc']`. Формується через `EXCLUDED_PATHS.flatMap(p => ['--exclude', p])`, тож додавання нового елемента в `EXCLUDED_PATHS` автоматично продовжує перелік прапорців.
|
|
65
|
+
- **Побічні ефекти:** немає (чиста функція).
|
|
66
|
+
|
|
67
|
+
## Константи
|
|
68
|
+
|
|
69
|
+
### `EXCLUDED_PATHS`
|
|
70
|
+
|
|
71
|
+
- **Тип:** `string[]`
|
|
72
|
+
- **Значення:** `['node_modules', '.envrc']`
|
|
73
|
+
- **Призначення:** Перелік каталогів/файлів, які виключаються з рекурсивного сканування `dotenv-linter -r`. Семантика:
|
|
74
|
+
- `node_modules` — стороння кодова база, її `.env*` не наша зона відповідальності.
|
|
75
|
+
- `.envrc` — файл direnv із shell-синтаксисом (`export FOO=bar`, `source_up` тощо), формально не key=value `.env`, лінтер на ньому давав би false positives.
|
|
76
|
+
|
|
77
|
+
## Залежності
|
|
78
|
+
|
|
79
|
+
### Стандартна бібліотека Node.js
|
|
80
|
+
|
|
81
|
+
- `node:child_process` — функція `spawnSync` для синхронного запуску дочірніх процесів `dotenv-linter`.
|
|
82
|
+
- `node:path` — функція `resolve` для нормалізації `cwd` в абсолютний шлях.
|
|
83
|
+
|
|
84
|
+
### Внутрішні модулі проєкту
|
|
85
|
+
|
|
86
|
+
- `../../../scripts/cli-entry.mjs` — функція `isRunAsCli(import.meta.url)`. Детектує, чи модуль запущений як CLI (а не імпортований). При `true` виставляється `process.exitCode`.
|
|
87
|
+
- `../../../scripts/utils/resolve-cmd.mjs` — функція `resolveCmd(name)`. Шукає виконуваний файл у `PATH`; повертає абсолютний шлях або falsy-значення, якщо бінарника немає.
|
|
88
|
+
|
|
89
|
+
### Зовнішній інструмент
|
|
90
|
+
|
|
91
|
+
- **`dotenv-linter`** — нативний бінарник, очікується у `PATH`. **Не** є npm-залежністю проєкту. Встановлюється окремо командами:
|
|
92
|
+
- macOS: `brew install dotenv-linter`
|
|
93
|
+
- Linux: `curl -sSfL https://git.io/JLbXn | sh -s -- -b /usr/local/bin`
|
|
94
|
+
- cargo: `cargo install dotenv-linter`
|
|
95
|
+
|
|
96
|
+
## Потік виконання / Використання
|
|
97
|
+
|
|
98
|
+
### Як CLI
|
|
99
|
+
|
|
100
|
+
Файл містить хвостовий блок:
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
if (isRunAsCli(import.meta.url)) {
|
|
104
|
+
process.exitCode = runDotenvLinter()
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Тобто прямий запуск виставить exit-code процесу у `0` (успіх) або `1` (помилка/порушення). Виклик:
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
node npm/rules/text/lint/run-dotenv-linter.mjs
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`cwd` при цьому — поточний робочий каталог процесу (`process.cwd()`).
|
|
115
|
+
|
|
116
|
+
### Як бібліотечний модуль
|
|
117
|
+
|
|
118
|
+
Імпортується з оркестратора `lint-text`:
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
import { runDotenvLinter } from './run-dotenv-linter.mjs'
|
|
122
|
+
|
|
123
|
+
const code = runDotenvLinter('/abs/path/to/project')
|
|
124
|
+
if (code !== 0) process.exit(code)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Покроковий потік
|
|
128
|
+
|
|
129
|
+
1. **Нормалізація `cwd`.** `resolve(cwd)` → абсолютний `root`.
|
|
130
|
+
2. **Резолв бінарника.** `resolveCmd('dotenv-linter')` шукає виконуваний файл у `PATH`.
|
|
131
|
+
- Якщо `null`/`undefined`/порожній рядок → виклик `printDotenvLinterInstallHints()` → повернення `1`. Подальші кроки не виконуються.
|
|
132
|
+
3. **Підготовка списку виключень.** `buildExcludeArgs()` → `['--exclude', 'node_modules', '--exclude', '.envrc']`.
|
|
133
|
+
4. **Фаза fix.** `spawnSync(bin, ['fix', '-r', '--no-backup', '--quiet', '--exclude', 'node_modules', '--exclude', '.envrc', '.'], { cwd: root, encoding: 'utf8', env: process.env, stdio: ['ignore', 'pipe', 'pipe'] })`.
|
|
134
|
+
- Якщо `fixRun.error` (наприклад, EACCES/ENOENT при спавні) → запис `fixRun.error.message` у `stderr` → повернення `1`.
|
|
135
|
+
- `status` фази `fix` **не** перевіряється — інструмент може повертати ненульовий код, якщо лишилися ще не виправлені порушення; це штатно, бо далі йде фінальний `check`.
|
|
136
|
+
5. **Фаза check.** `spawnSync(bin, ['check', '-r', '--quiet', '--exclude', 'node_modules', '--exclude', '.envrc', '.'], …)` з тими самими опціями оточення.
|
|
137
|
+
- Якщо `checkRun.error` → запис `checkRun.error.message` у `stderr` → повернення `1`.
|
|
138
|
+
- Якщо `checkRun.status === 0` → повернення `0` (успіх).
|
|
139
|
+
- Інакше: за наявності `checkRun.stdout` пишемо його у `process.stdout`, за наявності `checkRun.stderr` — у `process.stderr`, повертаємо `1`.
|
|
140
|
+
|
|
141
|
+
### Контракт побічних ефектів
|
|
142
|
+
|
|
143
|
+
| Подія | Куди пишеться | Коли |
|
|
144
|
+
| ------------------------------ | ---------------- | ---------------------------------------- |
|
|
145
|
+
| Підказки встановлення | `process.stderr` | бінарника немає у `PATH` |
|
|
146
|
+
| `error.message` зі `spawnSync` | `process.stderr` | помилка спавну `fix` або `check` |
|
|
147
|
+
| `stdout` дочірнього `check` | `process.stdout` | `check.status !== 0` та буфер непорожній |
|
|
148
|
+
| `stderr` дочірнього `check` | `process.stderr` | `check.status !== 0` та буфер непорожній |
|
|
149
|
+
| Модифікація `.env*`-файлів | дерево `cwd` | фаза `fix` (без `.bak`) |
|
|
150
|
+
|
|
151
|
+
### Інтеграція в `lint-text`
|
|
152
|
+
|
|
153
|
+
Модуль викликається сусіднім оркестратором `lint.mjs` тієї ж теки разом із `run-shellcheck.mjs` і `run-v8r.mjs`. Кожен раннер повертає `0` / `1`, оркестратор агрегує коди для фінального exit-status команди `lint-text`. Кожна перевірка ізольована — падіння одного раннера не блокує запуск інших, якщо це визначено архітектурою оркестратора.
|
|
154
|
+
|
|
155
|
+
### Тестування
|
|
156
|
+
|
|
157
|
+
Поруч у теці є `tests/`, що містить юніт-тести для раннерів `lint-`. Модуль навмисно структуровано так, щоб тестам було легко мокати `spawnSync` і `resolveCmd`: усі гілки повернення (`bin not found`, `fix error`, `check error`, `check non-zero`, `check ok`) відокремлені й деталі поведінки видимі через `process.stdout`/`process.stderr`.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# run-shellcheck.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `run-shellcheck.mjs` — частина ланцюжка `lint-text` для перевірки якості shell-скриптів (`*.sh`) у проєкті за допомогою зовнішнього інструменту **ShellCheck**. Скрипт виконує два етапи:
|
|
6
|
+
|
|
7
|
+
1. **Авто-виправлення**: оскільки ShellCheck не має прапорця `--fix`, для кожного знайденого `*.sh` файла модуль запускає `shellcheck -f diff` і застосовує отриманий unified-diff через системну утиліту `patch -p1` у корені проєкту. Цикл повторюється до `MAX_FIX_ROUNDS_PER_FILE` (32) разів, поки залишаються авто-виправні зауваження.
|
|
8
|
+
2. **Фінальний прогон**: звичайний `shellcheck` по всіх зібраних файлах. Будь-яке попередження чи помилка дають ненульовий код виходу.
|
|
9
|
+
|
|
10
|
+
Модуль уміє працювати як **CLI** (через `isRunAsCli`) і як **бібліотека** (експортує функції). Шляхи скриптів збираються з `git ls-files` у git-репозиторії або через `globSync` (з виключенням `node_modules`) поза git-деревом.
|
|
11
|
+
|
|
12
|
+
Якщо у системі немає `shellcheck` чи `patch`, у stderr друкуються підказки встановлення для macOS (Homebrew), Debian/Ubuntu (apt) та Arch (pacman), а скрипт завершується з кодом 1.
|
|
13
|
+
|
|
14
|
+
## Експорти / API
|
|
15
|
+
|
|
16
|
+
| Експорт | Тип | Призначення |
|
|
17
|
+
| --------------------------- | -------------- | ---------------------------------------------------------------------------------- |
|
|
18
|
+
| `listShellScriptPaths(cwd)` | named function | Зібрати відсортований унікальний список відносних шляхів до `*.sh` файлів у `cwd`. |
|
|
19
|
+
| `runShellcheckText(cwd?)` | named function | Запустити повний цикл: авто-фікси + фінальна перевірка. Повертає код виходу (0/1). |
|
|
20
|
+
|
|
21
|
+
Модуль також виконується як CLI: якщо `import.meta.url` відповідає поточному запуску — викликається `runShellcheckText()` і її результат присвоюється `process.exitCode`.
|
|
22
|
+
|
|
23
|
+
## Функції
|
|
24
|
+
|
|
25
|
+
### `printShellcheckInstallHints()`
|
|
26
|
+
|
|
27
|
+
- **Сигнатура**: `function printShellcheckInstallHints(): void`
|
|
28
|
+
- **Параметри**: немає.
|
|
29
|
+
- **Повертає**: `void`.
|
|
30
|
+
- **Side effects**: пише у `process.stderr` повідомлення з інструкціями встановлення `shellcheck` для macOS, Debian/Ubuntu та Arch.
|
|
31
|
+
|
|
32
|
+
### `printPatchInstallHints()`
|
|
33
|
+
|
|
34
|
+
- **Сигнатура**: `function printPatchInstallHints(): void`
|
|
35
|
+
- **Параметри**: немає.
|
|
36
|
+
- **Повертає**: `void`.
|
|
37
|
+
- **Side effects**: пише у `process.stderr` підказку про встановлення `patch` (на macOS зазвичай є; на Debian/Ubuntu — `sudo apt-get install -y patch`).
|
|
38
|
+
|
|
39
|
+
### `listShellScriptPaths(cwd)` (export)
|
|
40
|
+
|
|
41
|
+
- **Сигнатура**: `function listShellScriptPaths(cwd: string): string[]`
|
|
42
|
+
- **Параметри**:
|
|
43
|
+
- `cwd` — абсолютний шлях до кореня проєкту (вже має бути нормалізований через `resolve`, якщо потрібно).
|
|
44
|
+
- **Повертає**: відсортований масив унікальних відносних шляхів до `*.sh` файлів від `cwd`. Слеші уніфікуються в forward-slash (`'/'`).
|
|
45
|
+
- **Side effects**: виконує `git rev-parse --is-inside-work-tree` і `git ls-files -z -- ':(glob)**/*.sh'` через `spawnSync`; у безгітовому контексті — викликає `globSync('**/*.sh')` з виключенням `node_modules`.
|
|
46
|
+
- **Логіка**:
|
|
47
|
+
- Якщо `git` доступний і `cwd` — частина git-робочого дерева, читаються лише tracked файли (`ls-files` з NUL-розділенням, дублікати усуваються через `Set`).
|
|
48
|
+
- Якщо `git` доступний, але `ls-files` повертає ненульовий статус — повертається порожній масив.
|
|
49
|
+
- Інакше — `globSync` з `exclude`, який відкидає шляхи, що містять `node_modules` (на будь-якому рівні), і нормалізує бекслеші (`\\` → `/`).
|
|
50
|
+
|
|
51
|
+
### `runShellcheckText(cwd = process.cwd())` (export)
|
|
52
|
+
|
|
53
|
+
- **Сигнатура**: `function runShellcheckText(cwd?: string): number`
|
|
54
|
+
- **Параметри**:
|
|
55
|
+
- `cwd` — необов'язковий робочий каталог. За замовчуванням — `process.cwd()`.
|
|
56
|
+
- **Повертає**: `0` — все OK; `1` — помилка середовища (немає `shellcheck`/`patch`), помилка spawn, або залишкові зауваження ShellCheck після авто-фіксів.
|
|
57
|
+
- **Side effects**:
|
|
58
|
+
- Можливий вивід підказок у `stderr` (якщо немає `shellcheck`/`patch`).
|
|
59
|
+
- Запис патчів у файли через `patch -p1` (модифікує source-файли).
|
|
60
|
+
- Друк звичайного звіту ShellCheck у `stdout`/`stderr` на фінальному кроці.
|
|
61
|
+
- **Алгоритм**:
|
|
62
|
+
1. `root = resolve(cwd)`.
|
|
63
|
+
2. `shellcheck = resolveCmd('shellcheck')` → якщо немає, друк підказки та `return 1`.
|
|
64
|
+
3. `patchBin = resolveCmd('patch')` → якщо немає, друк підказки та `return 1`.
|
|
65
|
+
4. `files = listShellScriptPaths(root)` → якщо порожньо, `return 0`.
|
|
66
|
+
5. Для кожного `rel` із `files`: `autofixOneFile(...)`. Якщо ненульовий код — негайний `return`.
|
|
67
|
+
6. `return runFinalShellcheck(shellcheck, files, root)`.
|
|
68
|
+
|
|
69
|
+
### `autofixOneFile(shellcheck, patchBin, root, rel)`
|
|
70
|
+
|
|
71
|
+
- **Сигнатура**: `function autofixOneFile(shellcheck: string, patchBin: string, root: string, rel: string): number`
|
|
72
|
+
- **Параметри**:
|
|
73
|
+
- `shellcheck` — абсолютний шлях до бінарника ShellCheck.
|
|
74
|
+
- `patchBin` — абсолютний шлях до бінарника `patch`.
|
|
75
|
+
- `root` — абсолютний `cwd` для spawn.
|
|
76
|
+
- `rel` — відносний шлях файла від `root`.
|
|
77
|
+
- **Повертає**: `0` — більше нема чого фіксити (нормальне завершення циклу); `1` — помилка spawn чи помилка `patch`.
|
|
78
|
+
- **Side effects**: до 32 викликів `spawnSync(shellcheck, ['-f', 'diff', rel])` і `applyShellcheckDiff(...)` на файл; модифікує сам файл через `patch`.
|
|
79
|
+
- **Логіка**:
|
|
80
|
+
- У циклі `0..MAX_FIX_ROUNDS_PER_FILE-1`:
|
|
81
|
+
- Запускає `shellcheck -f diff <rel>` з `maxBuffer: 10MiB`.
|
|
82
|
+
- Якщо `diffResult.error` — друкує повідомлення і повертає 1.
|
|
83
|
+
- Якщо `shouldStopAutofixLoop(diffResult)` — повертає 0.
|
|
84
|
+
- Інакше — `applyShellcheckDiff(...)`. На помилку — повертає 1.
|
|
85
|
+
- Якщо досягнуто `MAX_FIX_ROUNDS_PER_FILE` — повертає 0 (захист від зациклення).
|
|
86
|
+
|
|
87
|
+
### `shouldStopAutofixLoop(diffResult)`
|
|
88
|
+
|
|
89
|
+
- **Сигнатура**: `function shouldStopAutofixLoop(diffResult: { status: number | null, stdout?: string | null, stderr?: string | null }): boolean`
|
|
90
|
+
- **Параметри**:
|
|
91
|
+
- `diffResult` — об'єкт результату `spawnSync` (потрібні `status`, `stdout`, `stderr`).
|
|
92
|
+
- **Повертає**: `true`, якщо подальші ітерації авто-фіксів не мають сенсу.
|
|
93
|
+
- **Side effects**: немає (чиста перевірка).
|
|
94
|
+
- **Логіка**: повертає `true`, якщо
|
|
95
|
+
- `status === 0` (ShellCheck не знайшов зауважень), **або**
|
|
96
|
+
- `stderr` містить підрядок `'none were auto-fixable'` (`NON_AUTOFIXABLE_HINT`), **або**
|
|
97
|
+
- `stdout` після `trim()` порожній (нема дифу для застосування).
|
|
98
|
+
|
|
99
|
+
### `applyShellcheckDiff(patchBin, root, rel, diffStdout)`
|
|
100
|
+
|
|
101
|
+
- **Сигнатура**: `function applyShellcheckDiff(patchBin: string, root: string, rel: string, diffStdout: string): number`
|
|
102
|
+
- **Параметри**:
|
|
103
|
+
- `patchBin` — абсолютний шлях до `patch`.
|
|
104
|
+
- `root` — `cwd` для spawn (корінь проєкту).
|
|
105
|
+
- `rel` — відносний шлях файла, для якого формується diff (використовується тільки у повідомленні про помилку).
|
|
106
|
+
- `diffStdout` — вміст unified-diff, отриманий від `shellcheck -f diff`.
|
|
107
|
+
- **Повертає**: `0` — патч застосовано; `1` — `patch` повернув ненульовий код.
|
|
108
|
+
- **Side effects**:
|
|
109
|
+
- Запускає `patch -p1` із `diffStdout` через stdin.
|
|
110
|
+
- На помилку виливає `stderr` і `stdout` процесу `patch` у `process.stderr`, а також додає рядок `run-shellcheck-text: patch не застосував diff для ${rel}`.
|
|
111
|
+
|
|
112
|
+
### `runFinalShellcheck(shellcheck, files, root)`
|
|
113
|
+
|
|
114
|
+
- **Сигнатура**: `function runFinalShellcheck(shellcheck: string, files: string[], root: string): number`
|
|
115
|
+
- **Параметри**:
|
|
116
|
+
- `shellcheck` — абсолютний шлях до ShellCheck.
|
|
117
|
+
- `files` — відносні шляхи усіх файлів для перевірки.
|
|
118
|
+
- `root` — `cwd` для spawn.
|
|
119
|
+
- **Повертає**: `0` — звіт чистий; `1` — `spawnSync` віддав `error` або `status !== 0`.
|
|
120
|
+
- **Side effects**:
|
|
121
|
+
- Виконує `shellcheck <files...>` без прапорця `-f diff`, з `maxBuffer: 10MiB` і `stdio: ['ignore', 'pipe', 'pipe']`.
|
|
122
|
+
- Якщо є помилка — друкує `error.message` у `stderr`.
|
|
123
|
+
- Якщо `status !== 0` — друкує `stdout` у `process.stdout` і `stderr` у `process.stderr`.
|
|
124
|
+
|
|
125
|
+
## Константи
|
|
126
|
+
|
|
127
|
+
| Ім'я | Значення | Призначення |
|
|
128
|
+
| ------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
129
|
+
| `NON_AUTOFIXABLE_HINT` | `'none were auto-fixable'` | Маркер у stderr ShellCheck про відсутність авто-виправлень — критерій виходу з циклу `autofixOneFile`. |
|
|
130
|
+
| `MAX_FIX_ROUNDS_PER_FILE` | `32` | Жорсткий ліміт ітерацій `diff`+`patch` на один файл для уникнення зациклення. |
|
|
131
|
+
|
|
132
|
+
## Залежності
|
|
133
|
+
|
|
134
|
+
### Node.js (built-in)
|
|
135
|
+
|
|
136
|
+
- `node:child_process` → `spawnSync` — синхронний запуск `git`, `shellcheck`, `patch`.
|
|
137
|
+
- `node:fs` → `globSync` — пошук `*.sh` поза git-деревом.
|
|
138
|
+
- `node:path` → `resolve` — нормалізація `cwd` у абсолютний шлях.
|
|
139
|
+
|
|
140
|
+
### Внутрішні модулі
|
|
141
|
+
|
|
142
|
+
- `../../../scripts/cli-entry.mjs` → `isRunAsCli(import.meta.url)` — перевіряє, чи модуль запущено напряму як CLI (а не імпортовано).
|
|
143
|
+
- `../../../scripts/utils/resolve-cmd.mjs` → `resolveCmd(name)` — повертає абсолютний шлях до бінарника з PATH або `null`.
|
|
144
|
+
|
|
145
|
+
### Зовнішні бінарники (runtime)
|
|
146
|
+
|
|
147
|
+
- **shellcheck** — обов'язковий; без нього `runShellcheckText` повертає 1.
|
|
148
|
+
- **patch** — обов'язковий для авто-фіксів; без нього `runShellcheckText` повертає 1.
|
|
149
|
+
- **git** — необов'язковий: якщо доступний і ми у робочому дереві — використовується `git ls-files`; інакше — `globSync`.
|
|
150
|
+
|
|
151
|
+
## Потік виконання / Використання
|
|
152
|
+
|
|
153
|
+
### CLI
|
|
154
|
+
|
|
155
|
+
Скрипт можна запустити напряму:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
node npm/rules/text/lint/run-shellcheck.mjs
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Логіка CLI-входу:
|
|
162
|
+
|
|
163
|
+
```js
|
|
164
|
+
if (isRunAsCli(import.meta.url)) {
|
|
165
|
+
process.exitCode = runShellcheckText()
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`process.exitCode` встановлюється у значення, повернуте `runShellcheckText()` (0 або 1).
|
|
170
|
+
|
|
171
|
+
### Програмний виклик
|
|
172
|
+
|
|
173
|
+
```js
|
|
174
|
+
import { runShellcheckText, listShellScriptPaths } from './run-shellcheck.mjs'
|
|
175
|
+
|
|
176
|
+
// Повний прогон у поточному каталозі:
|
|
177
|
+
const code = runShellcheckText()
|
|
178
|
+
|
|
179
|
+
// Тільки список *.sh файлів:
|
|
180
|
+
const files = listShellScriptPaths(process.cwd())
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Високорівнева послідовність кроків `runShellcheckText`
|
|
184
|
+
|
|
185
|
+
1. `resolve(cwd)` → абсолютний `root`.
|
|
186
|
+
2. `resolveCmd('shellcheck')` → перевірка наявності бінарника. **Fail-fast**: підказка + `return 1`.
|
|
187
|
+
3. `resolveCmd('patch')` → перевірка наявності бінарника. **Fail-fast**: підказка + `return 1`.
|
|
188
|
+
4. `listShellScriptPaths(root)`:
|
|
189
|
+
- `git` доступний і ми в робочому дереві → `git ls-files -z -- ':(glob)**/*.sh'` (NUL-роздільник, унікальні, відсортовані).
|
|
190
|
+
- Інакше → `globSync('**/*.sh', { exclude: ... })` з виключенням `node_modules` (forward-slashes).
|
|
191
|
+
5. Якщо файлів немає → `return 0` (no-op).
|
|
192
|
+
6. Для кожного файла → `autofixOneFile(...)`:
|
|
193
|
+
- Цикл до 32 разів: `shellcheck -f diff <rel>` → `patch -p1` (через stdin).
|
|
194
|
+
- Виходить раніше, якщо `status === 0`, `none were auto-fixable`, або порожній diff.
|
|
195
|
+
7. `runFinalShellcheck(shellcheck, files, root)`:
|
|
196
|
+
- `shellcheck <files...>` без формату diff.
|
|
197
|
+
- Будь-який ненульовий статус → друк stdout/stderr у консоль і `return 1`.
|
|
198
|
+
|
|
199
|
+
### Коди виходу
|
|
200
|
+
|
|
201
|
+
| Код | Причина |
|
|
202
|
+
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
203
|
+
| `0` | Немає `*.sh` файлів, або всі пройшли фінальний `shellcheck` чисто (можливо, після авто-фіксів). |
|
|
204
|
+
| `1` | Немає `shellcheck`/`patch` у PATH; помилка `spawnSync` (наприклад, `ENOENT`); `patch` не застосував diff; фінальний `shellcheck` повернув ненульовий статус. |
|
|
205
|
+
|
|
206
|
+
### Ефекти на файлову систему
|
|
207
|
+
|
|
208
|
+
Авто-фікси **модифікують сам source `.sh`-файл** через `patch -p1`. Запуск передбачає, що користувач готовий побачити змінений вміст (наприклад, виконує перевірку перед коммітом у CI або локально).
|
|
209
|
+
|
|
210
|
+
### Контекст застосування
|
|
211
|
+
|
|
212
|
+
Модуль використовується у складі `lint-text` ланцюжка (правила `npm/rules/text/lint/`) для перевірки текстової частини проєкту, де shell-скрипти підлягають аналізу ShellCheck. Це частина набору `rules/text/lint/*`, описаного у `n-text.mdc` і `n-js-lint.mdc`.
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# run-v8r.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `run-v8r.mjs` — це послідовний раннер CLI-валідатора схем `v8r` для всіх типів файлів, які він підтримує (`.json`, `.json5`, `.yml`, `.yaml`, `.toml`). Він є проміжним рівнем між кореневою командою `lint-text` та `bunx v8r`.
|
|
6
|
+
|
|
7
|
+
Призначення модуля — обійти специфічну ваду `v8r`: якщо у єдиний виклик передати кілька glob-патернів і хоча б один із них не знаходить жодного файлу, процес `v8r` падає з кодом виходу `98`, тому решта розширень узагалі не перевіряється. Щоб уникнути цього, скрипт виконує **окремий** `bunx v8r` на **кожен** glob і толерантно ставиться до коду `98` (порожнє співпадіння — не помилка).
|
|
8
|
+
|
|
9
|
+
Додатково модуль автоматично підставляє у виклик `v8r` параметр `-c <шлях до v8r-catalog.json>` — каталог схем пакета `@nitra/cursor`, який лежить у `npm/schemas/v8r-catalog.json` від кореня репозиторію. Завдяки цьому користувачі цього раннера не зобовʼязані пам’ятати про каталог схем.
|
|
10
|
+
|
|
11
|
+
Поведінка щодо виводу:
|
|
12
|
+
|
|
13
|
+
- Якщо v8r для конкретного glob завершився з кодом `0` (успіх) або `98` (порожнє співпадіння) — вивід **не друкується**, обхід продовжується.
|
|
14
|
+
- Якщо v8r повернув будь-який інший код — `stdout` і `stderr` друкуються «як є», а сам раннер повертає той самий код виходу і **припиняє** подальший обхід glob-патернів.
|
|
15
|
+
|
|
16
|
+
Файл одночасно є модулем (експортує API) і CLI-точкою входу: під час прямого запуску він читає glob-и з `process.argv` (починаючи з 3-го елемента) і виставляє `process.exitCode`.
|
|
17
|
+
|
|
18
|
+
## Експорти / API
|
|
19
|
+
|
|
20
|
+
Модуль експортує дві іменовані сутності:
|
|
21
|
+
|
|
22
|
+
| Імʼя | Тип | Призначення |
|
|
23
|
+
| ------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
24
|
+
| `DEFAULT_V8R_GLOBS` | `string[]` | Список glob-патернів за замовчуванням для форматів, які підтримує `v8r`. |
|
|
25
|
+
| `V8R_CATALOG_PATH` | `string` | Абсолютний шлях до файлу `v8r-catalog.json` у каталозі `schemas` пакета `@nitra/cursor`. |
|
|
26
|
+
| `runV8rWithGlobs` | `function` | Послідовний запуск `bunx v8r` по списку glob-патернів; повертає числовий код виходу. Не змінює `process.exitCode`. |
|
|
27
|
+
|
|
28
|
+
### `DEFAULT_V8R_GLOBS`
|
|
29
|
+
|
|
30
|
+
Значення:
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
;['**/*.json', '**/*.json5', '**/*.yml', '**/*.yaml', '**/*.toml']
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Використовується як значення за замовчуванням для `runV8rWithGlobs`, а також як fallback у CLI-режимі, коли користувач не передав власних glob-аргументів.
|
|
37
|
+
|
|
38
|
+
### `V8R_CATALOG_PATH`
|
|
39
|
+
|
|
40
|
+
Обчислюється на момент завантаження модуля з `import.meta.url`:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
join(dirname(fileURLToPath(import.meta.url)), '../../../schemas/v8r-catalog.json')
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
З урахуванням розташування самого модуля (`npm/rules/text/lint/run-v8r.mjs`) три рівні `..` піднімають шлях до `npm/`, після чого додається `schemas/v8r-catalog.json`. Результат — абсолютний шлях, який не залежить від поточної робочої директорії процесу.
|
|
47
|
+
|
|
48
|
+
### `runV8rWithGlobs(globs?)`
|
|
49
|
+
|
|
50
|
+
Див. секцію [«Функції»](#функції).
|
|
51
|
+
|
|
52
|
+
## Функції
|
|
53
|
+
|
|
54
|
+
### `runV8rWithGlobs(globs = DEFAULT_V8R_GLOBS)`
|
|
55
|
+
|
|
56
|
+
Сигнатура:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
/**
|
|
60
|
+
* @param {string[]} [globs] патерни; за замовчуванням DEFAULT_V8R_GLOBS
|
|
61
|
+
* @returns {number} 0 — OK, 1 — помилка spawn, 2 — немає каталогу схем, інше — код v8r
|
|
62
|
+
*/
|
|
63
|
+
export function runV8rWithGlobs(globs = DEFAULT_V8R_GLOBS)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### Параметри
|
|
67
|
+
|
|
68
|
+
- `globs` (`string[]`, опційно) — масив glob-патернів, які буде передано в `v8r` по черзі (по одному на виклик). За замовчуванням — `DEFAULT_V8R_GLOBS`.
|
|
69
|
+
|
|
70
|
+
#### Повертає
|
|
71
|
+
|
|
72
|
+
Числовий код виходу, який повинен стати `process.exitCode` у CLI-режимі:
|
|
73
|
+
|
|
74
|
+
- `0` — усі glob-и оброблено успішно (або вони порожні);
|
|
75
|
+
- `1` — `spawnSync` повернув `result.error` (наприклад, не вдалося запустити процес);
|
|
76
|
+
- `2` — не знайдено файл `V8R_CATALOG_PATH` (відсутній каталог схем `npm/schemas/v8r-catalog.json`);
|
|
77
|
+
- будь-яке інше число — реальний код виходу від `v8r` для першого glob-а, який не дав `0` або `98`.
|
|
78
|
+
|
|
79
|
+
#### Side effects
|
|
80
|
+
|
|
81
|
+
- Може писати в `process.stderr` повідомлення про відсутній каталог схем (код `2`) або текст помилки `spawnSync.error.message` (код `1`).
|
|
82
|
+
- Для невдалого `v8r` (код, відмінний від `0`/`98`) друкує його `stdout` і `stderr` без змін.
|
|
83
|
+
- **Не** мутує `process.exitCode` і **не** викликає `process.exit`. Цією операцією займається CLI-блок наприкінці файла.
|
|
84
|
+
- Виконує синхронний `spawnSync` (`bun x v8r ...`) — блокуючий виклик на кожен glob.
|
|
85
|
+
|
|
86
|
+
#### Алгоритм
|
|
87
|
+
|
|
88
|
+
1. Перевіряє існування `V8R_CATALOG_PATH` через `existsSync`. Якщо файла немає — пише повідомлення в `stderr` і повертає `2`.
|
|
89
|
+
2. Перебирає `globs` у тому ж порядку, у якому вони передані:
|
|
90
|
+
1. Резолвить `bun` через `resolveCmd('bun')`; якщо `bun` не знайдено в `PATH`, використовує `process.execPath` як fallback (тобто запустить поточний інтерпретатор, що збігається з шляхом до `node`/`bun`-runtime, який запустив скрипт).
|
|
91
|
+
2. Викликає `spawnSync(bunPath, ['x', 'v8r', pattern, '-c', V8R_CATALOG_PATH], { encoding: 'utf8', maxBuffer: 50 * 1024 * 1024, shell: false, stdio: ['ignore', 'pipe', 'pipe'] })`.
|
|
92
|
+
3. Якщо `result.error` — пише `result.error.message` у `stderr` і повертає `1`.
|
|
93
|
+
4. Бере `result.status ?? 1` (якщо процес був вбитий сигналом, `status === null`, тоді трактує це як `1`).
|
|
94
|
+
5. Якщо код **не** `0` і **не** `98` — друкує накопичений `stdout`/`stderr` (якщо вони непорожні) і повертає цей код, **зупиняючи** подальший обхід.
|
|
95
|
+
3. Якщо всі glob-и пройшли — повертає `0`.
|
|
96
|
+
|
|
97
|
+
### CLI-блок (нижній рівень файла)
|
|
98
|
+
|
|
99
|
+
Сигнатура (не функція, а side-effect блок):
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
if (isRunAsCli(import.meta.url)) {
|
|
103
|
+
const globs = process.argv.length > 2 ? process.argv.slice(2) : DEFAULT_V8R_GLOBS
|
|
104
|
+
process.exitCode = runV8rWithGlobs(globs)
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### Поведінка
|
|
109
|
+
|
|
110
|
+
- `isRunAsCli(import.meta.url)` повертає `true`, коли модуль викликано як точку входу (а не імпортовано).
|
|
111
|
+
- Якщо передано хоча б один аргумент після назви скрипта (`process.argv.length > 2`) — усі аргументи (зрізані з індексу 2) використовуються як glob-патерни.
|
|
112
|
+
- Інакше — `DEFAULT_V8R_GLOBS`.
|
|
113
|
+
- Результат `runV8rWithGlobs(...)` записується у `process.exitCode`, що дозволяє завершити процес із потрібним кодом без переривання поточного call-stack.
|
|
114
|
+
|
|
115
|
+
## Залежності
|
|
116
|
+
|
|
117
|
+
### Внутрішні (Node.js core)
|
|
118
|
+
|
|
119
|
+
- `node:child_process` → `spawnSync` — синхронний запуск зовнішнього процесу `bun x v8r ...`.
|
|
120
|
+
- `node:fs` → `existsSync` — перевірка наявності файлу каталогу схем.
|
|
121
|
+
- `node:path` → `dirname`, `join` — побудова шляху до `v8r-catalog.json`.
|
|
122
|
+
- `node:url` → `fileURLToPath` — конвертація `import.meta.url` в абсолютний файловий шлях.
|
|
123
|
+
|
|
124
|
+
### Внутрішні (`@nitra/cursor`)
|
|
125
|
+
|
|
126
|
+
- `../../../scripts/cli-entry.mjs` → `isRunAsCli(metaUrl)` — детектор «запущено як CLI чи імпортовано як модуль».
|
|
127
|
+
- `../../../scripts/utils/resolve-cmd.mjs` → `resolveCmd(name)` — пошук абсолютного шляху до бінарника за `PATH`-у (з повним fallback-резолвером, на відміну від простого `which`).
|
|
128
|
+
|
|
129
|
+
### Зовнішні (через `bunx`)
|
|
130
|
+
|
|
131
|
+
- `v8r` — CLI-валідатор JSON/YAML/TOML за каталогом JSON-схем. Запускається через `bun x v8r ...`.
|
|
132
|
+
- `bun` — runtime, через який запускається `v8r`. Якщо `bun` відсутній у `PATH`, використовується `process.execPath`.
|
|
133
|
+
|
|
134
|
+
### Конфігурація / артефакти
|
|
135
|
+
|
|
136
|
+
- `npm/schemas/v8r-catalog.json` (в межах пакета `@nitra/cursor`) — обовʼязковий файл-каталог відповідностей `glob → schemaURL`. Без нього раннер повертає код `2`.
|
|
137
|
+
|
|
138
|
+
## Потік виконання / Використання
|
|
139
|
+
|
|
140
|
+
### Як частина команди `lint-text`
|
|
141
|
+
|
|
142
|
+
Раннер призначений для виклику з кореневої лінт-команди репозиторію (`lint-text` у `package.json`). Замість того, щоб запускати `v8r` чотирма (або п’ятьма) окремими командами в `package.json`, використовується один виклик цього модуля, який сам обходить усі типові glob-и:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
node ./npm/rules/text/lint/run-v8r.mjs
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Це еквівалентно послідовним викликам:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
bunx v8r '**/*.json' -c npm/schemas/v8r-catalog.json
|
|
152
|
+
bunx v8r '**/*.json5' -c npm/schemas/v8r-catalog.json
|
|
153
|
+
bunx v8r '**/*.yml' -c npm/schemas/v8r-catalog.json
|
|
154
|
+
bunx v8r '**/*.yaml' -c npm/schemas/v8r-catalog.json
|
|
155
|
+
bunx v8r '**/*.toml' -c npm/schemas/v8r-catalog.json
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
але без падіння на коді `98`, коли якесь розширення не зустрічається в проєкті.
|
|
159
|
+
|
|
160
|
+
### Запуск з кастомними glob-ами
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
node ./npm/rules/text/lint/run-v8r.mjs '**/*.json' 'configs/*.yaml'
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
У цьому режимі `DEFAULT_V8R_GLOBS` ігноруються, обходяться лише передані патерни.
|
|
167
|
+
|
|
168
|
+
### Імпорт як модуль
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
import { runV8rWithGlobs, DEFAULT_V8R_GLOBS, V8R_CATALOG_PATH } from './run-v8r.mjs'
|
|
172
|
+
|
|
173
|
+
const code = runV8rWithGlobs(['**/*.json'])
|
|
174
|
+
if (code !== 0) {
|
|
175
|
+
// обробити збій валідації
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Корисно з тестів і інших скриптів, які мають потребу перевалідовувати лише підмножину файлів.
|
|
180
|
+
|
|
181
|
+
### Семантика кодів виходу
|
|
182
|
+
|
|
183
|
+
| Код | Значення |
|
|
184
|
+
| ---- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
185
|
+
| `0` | Усі glob-и пройшли (валідно або порожньо). |
|
|
186
|
+
| `1` | Внутрішня помилка запуску процесу (`spawnSync.error`); зокрема, якщо ані `bun`, ані `process.execPath` не змогли стартувати. |
|
|
187
|
+
| `2` | Не знайдено каталог схем `npm/schemas/v8r-catalog.json` у пакеті `@nitra/cursor`. |
|
|
188
|
+
| `98` | **Не повертається**: внутрішньо трактується як «успіх для цього glob» і обхід триває далі. |
|
|
189
|
+
| інше | Дослівний код виходу від `v8r` для першого glob, на якому валідація провалилася. |
|
|
190
|
+
|
|
191
|
+
### Особливості реалізації, на які варто звернути увагу
|
|
192
|
+
|
|
193
|
+
- `shell: false` запобігає інтерполяції glob-патернів shell-ом — їх обробляє безпосередньо `v8r`.
|
|
194
|
+
- `maxBuffer: 50 * 1024 * 1024` (50 MiB) — захист від обрізання довгого виводу `v8r` на великих репозиторіях.
|
|
195
|
+
- `stdio: ['ignore', 'pipe', 'pipe']` — `stdin` процесу не використовується; вивід буферизується й друкується лише при помилці.
|
|
196
|
+
- Обхід «лінивий» по помилкам: на першому невдалому glob цикл завершується. Це навмисно — повертати треба перший реальний код помилки, а решта валідації не має сенсу до її усунення.
|
|
197
|
+
- Раннер **не** мутує `process.exitCode` усередині `runV8rWithGlobs`, тому імпортери можуть безпечно викликати функцію кілька разів і самостійно вирішувати, що з кодом робити.
|