@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,256 @@
|
|
|
1
|
+
# `formatting.mjs` — перевірка текстового стека й форматування за правилом `text.mdc`
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `formatting.mjs` реалізує **JS-частину** перевірки правила `n-text.mdc` / `npm/mdc/text.mdc` для пакета `@nitra/cursor`. Його роль — **доповнити** Rego-перевірки тими аспектами, які не вдається коректно описати декларативно: наявність файлів на файловій системі, вміст plain-text файлу `.v8rignore`, наявність абзацу про український апостроф у markdown-тексті правила, форма скрипта `lint-text` у `package.json` та виклик `bun run lint-text` у GitHub Actions workflow.
|
|
6
|
+
|
|
7
|
+
Модуль експортує функцію `check(cwd)`, яка повертає код виходу процесу (`0` — все ок, `1` — є порушення). Усі повідомлення про успіх/провал агрегує `createCheckReporter()` зі спільної бібліотеки `scripts/lib/check-reporter.mjs`.
|
|
8
|
+
|
|
9
|
+
Розподіл відповідальностей JS ↔ Rego (зафіксований у JSDoc-коментарі на початку файлу):
|
|
10
|
+
|
|
11
|
+
- **JS (цей файл):**
|
|
12
|
+
- `.v8rignore` (текстовий формат, рядки шляхів);
|
|
13
|
+
- наявність FS-файлів `.oxfmtrc.json`, `.cspell.json`, `.markdownlint-cli2.jsonc`, `.vscode/extensions.json`, `.vscode/settings.json`, `package.json`;
|
|
14
|
+
- абзац про український апостроф у `.cursor/rules/n-text.mdc` / `npm/mdc/text.mdc` (markdown-текст);
|
|
15
|
+
- перевірка скрипта `lint-text` у `package.json`;
|
|
16
|
+
- наявність кроку `bun run lint-text` у workflow `lint-text.yml`.
|
|
17
|
+
- **Rego (`npx @nitra/cursor check`):**
|
|
18
|
+
- `npm/policy/text/oxfmtrc/` — обовʼязкові ключі `.oxfmtrc.json` і канонічні значення (`semi`/`singleQuote`/`tabWidth`/`useTabs`/`printWidth`) + канонічні `ignorePatterns`;
|
|
19
|
+
- `npm/policy/text/cspell/` — `.cspell.json` (`version "0.2"`, `language`, імпорт `@nitra/cspell-dict`, заборона `@cspell/dict-*`, обовʼязкові `ignorePaths`);
|
|
20
|
+
- `npm/policy/text/markdownlint/` — `.markdownlint-cli2.jsonc` (`gitignore: true`, валідний JSON без коментарів);
|
|
21
|
+
- `npm/policy/text/package_json/` — заборона Prettier (`prettier` поле + `prettier`/`@nitra/prettier-config` у залежностях), `@nitra/cspell-dict ^2.0.0+` у `devDependencies`, заборона `markdownlint-cli2` у залежностях;
|
|
22
|
+
- `npm/policy/bun/package_json/` — у `devDependencies` лише пакети з `@nitra/*`;
|
|
23
|
+
- `text.vscode_extensions` / `text.vscode_settings` — вміст `.vscode/extensions.json` та `.vscode/settings.json`.
|
|
24
|
+
|
|
25
|
+
## Експорти / API
|
|
26
|
+
|
|
27
|
+
| Назва | Тип | Опис |
|
|
28
|
+
| ------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
29
|
+
| `check` | `(cwd?: string) => Promise<number>` | Єдиний публічний експорт. Виконує всі перевірки текстового стека й повертає код виходу: `0` — порушень немає, `1` — є хоча б одне. |
|
|
30
|
+
|
|
31
|
+
Решта функцій (`verifyUkApostropheRuleParagraph`, `checkV8rIgnore`, `checkTextConfigsExistence`, `checkPackageJsonText`, `checkLintTextScript`) — внутрішні, не експортуються.
|
|
32
|
+
|
|
33
|
+
Також у файлі є модульна константа:
|
|
34
|
+
|
|
35
|
+
| Назва | Значення | Призначення |
|
|
36
|
+
| ----------------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
37
|
+
| `UK_APOSTROPHE_HEADING` | `'**Український апостроф:**'` | Заголовок абзацу про апостроф у `text.mdc` / `n-text.mdc`, по якому ведеться пошук у вмісті правила. |
|
|
38
|
+
|
|
39
|
+
## Функції
|
|
40
|
+
|
|
41
|
+
### `verifyUkApostropheRuleParagraph(filePath, body, failFn, passFn)`
|
|
42
|
+
|
|
43
|
+
**Сигнатура:** `(filePath: string, body: string, failFn: (msg: string) => void, passFn: (msg: string) => void) => void`
|
|
44
|
+
|
|
45
|
+
**Параметри:**
|
|
46
|
+
|
|
47
|
+
- `filePath` — шлях до файлу `.mdc`, використовується лише для повідомлень.
|
|
48
|
+
- `body` — вміст `.mdc` у кодуванні UTF-8 (рядок).
|
|
49
|
+
- `failFn` — callback, який реєструє порушення (фактично змушує `check()` повернути `1`).
|
|
50
|
+
- `passFn` — callback успіху для звіту.
|
|
51
|
+
|
|
52
|
+
**Повертає:** `void`. Чистих значень не повертає; вплив — виключно через callback-и.
|
|
53
|
+
|
|
54
|
+
**Поведінка:**
|
|
55
|
+
|
|
56
|
+
1. Якщо в тексті немає підрядка `**Український апостроф:**` — `failFn` з підказкою «додай абзац» і одразу `return`.
|
|
57
|
+
2. Якщо немає згадки `U+0027` **або** `U+2019` — `failFn`, `return`.
|
|
58
|
+
3. Якщо у вмісті немає символу `’` (типографська одинарна лапка U+2019) — `failFn`, `return`.
|
|
59
|
+
4. Інакше — `passFn('… абзац про український апостроф на місці')`.
|
|
60
|
+
|
|
61
|
+
**Side effects:** виклики `failFn` / `passFn` (запис у звіт `check-reporter`).
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
### `checkV8rIgnore(passFn, failFn, cwd)`
|
|
66
|
+
|
|
67
|
+
**Сигнатура:** `async (passFn: (msg: string) => void, failFn: (msg: string) => void, cwd: string) => Promise<void>`
|
|
68
|
+
|
|
69
|
+
**Параметри:**
|
|
70
|
+
|
|
71
|
+
- `passFn`, `failFn` — callback-и звіту.
|
|
72
|
+
- `cwd` — корінь репозиторію.
|
|
73
|
+
|
|
74
|
+
**Повертає:** `Promise<void>`.
|
|
75
|
+
|
|
76
|
+
**Поведінка:**
|
|
77
|
+
|
|
78
|
+
1. Будує шлях `cwd + '.v8rignore'` через `path.join`.
|
|
79
|
+
2. Якщо файлу немає (`existsSync` → `false`) — `failFn` з підказкою про мінімально потрібний вміст, `return`.
|
|
80
|
+
3. Читає файл `readFile(..., 'utf8')`.
|
|
81
|
+
4. Парсить рядки: розбиває за `\n`, обрізає пробіли (`trim`), відкидає порожні й коментарі (рядки, що починаються з `#`), кладе результат у `Set`.
|
|
82
|
+
5. Для кожного з обовʼязкових шляхів `.vscode/extensions.json`, `.vscode/settings.json` перевіряє наявність у `Set`: знайдено → `passFn`, ні → `failFn` з підказкою додати рядок.
|
|
83
|
+
|
|
84
|
+
**Side effects:** читання файлу `.v8rignore` з диска (`fs/promises.readFile`), виклики `failFn` / `passFn`.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### `checkTextConfigsExistence(passFn, failFn, cwd)`
|
|
89
|
+
|
|
90
|
+
**Сигнатура:** `(passFn: (msg: string) => void, failFn: (msg: string) => void, cwd: string) => Promise<void>`
|
|
91
|
+
|
|
92
|
+
Хоч JSDoc описує функцію як таку, що повертає `Promise<void>`, тіло функції — синхронне. Наприкінці явно повертається `Promise.resolve()` для збереження асинхронного контракту (можна `await`-ити без шкоди).
|
|
93
|
+
|
|
94
|
+
**Параметри:**
|
|
95
|
+
|
|
96
|
+
- `passFn`, `failFn` — callback-и звіту.
|
|
97
|
+
- `cwd` — корінь репозиторію.
|
|
98
|
+
|
|
99
|
+
**Повертає:** `Promise<void>`.
|
|
100
|
+
|
|
101
|
+
**Поведінка:**
|
|
102
|
+
|
|
103
|
+
Ітерується по фіксованому масиву пар `[path, mdcRef]`:
|
|
104
|
+
|
|
105
|
+
| path | mdcRef (Rego-пакет із описом структури) |
|
|
106
|
+
| -------------------------- | --------------------------------------- |
|
|
107
|
+
| `.oxfmtrc.json` | `text.oxfmtrc` |
|
|
108
|
+
| `.cspell.json` | `text.cspell` |
|
|
109
|
+
| `.markdownlint-cli2.jsonc` | `text.markdownlint` |
|
|
110
|
+
| `.vscode/extensions.json` | `text.vscode_extensions` |
|
|
111
|
+
| `.vscode/settings.json` | `text.vscode_settings` |
|
|
112
|
+
|
|
113
|
+
Для кожної пари:
|
|
114
|
+
|
|
115
|
+
- `existsSync(join(cwd, path))` → `passFn` із підказкою, що структуру перевіряє `npx @nitra/cursor fix` через відповідний Rego-пакет.
|
|
116
|
+
- Файлу немає → `failFn('<path> не існує — створи згідно n-text.mdc')`.
|
|
117
|
+
|
|
118
|
+
**Side effects:** `existsSync` (синхронне звернення до файлової системи), виклики callback-ів. Запис у файли не виконується.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### `checkPackageJsonText(passFn, failFn, cwd)`
|
|
123
|
+
|
|
124
|
+
**Сигнатура:** `async (passFn: (msg: string) => void, failFn: (msg: string) => void, cwd: string) => Promise<void>`
|
|
125
|
+
|
|
126
|
+
**Параметри:**
|
|
127
|
+
|
|
128
|
+
- `passFn`, `failFn` — callback-и звіту.
|
|
129
|
+
- `cwd` — корінь репозиторію.
|
|
130
|
+
|
|
131
|
+
**Повертає:** `Promise<void>`.
|
|
132
|
+
|
|
133
|
+
**Поведінка:**
|
|
134
|
+
|
|
135
|
+
1. `pkgPath = join(cwd, 'package.json')`. Якщо файлу немає — мовчазний `return` (відсутність `package.json` — окремий concern, цей модуль його не валідує).
|
|
136
|
+
2. `pkg = JSON.parse(await readFile(pkgPath, 'utf8'))` — кидає виключення, якщо JSON битий (свідома відмова: краще вибух у CI, ніж проковтнута помилка).
|
|
137
|
+
3. Викликає `checkLintTextScript(pkg.scripts?.['lint-text'], passFn, failFn)` — валідація команди.
|
|
138
|
+
4. `lintTextWf = join(cwd, '.github/workflows/lint-text.yml')`.
|
|
139
|
+
- Якщо workflow існує:
|
|
140
|
+
- читає YAML, парсить `parseWorkflowYaml(wf)`.
|
|
141
|
+
- якщо парсер повернув root — перевіряє `anyRunStepIncludes(root, 'bun run lint-text')`; якщо ні — fallback `wf.includes('bun run lint-text')`.
|
|
142
|
+
- результат: `passFn('lint-text.yml викликає bun run lint-text')` або `failFn('lint-text.yml має містити крок bun run lint-text')`.
|
|
143
|
+
- Якщо workflow немає — `failFn('.github/workflows/lint-text.yml не існує — створи згідно n-text.mdc')`.
|
|
144
|
+
|
|
145
|
+
**Side effects:** читання `package.json` і потенційно `lint-text.yml`, виклики callback-ів, можливе виключення `JSON.parse` на пошкодженому `package.json`.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
### `checkLintTextScript(lintText, passFn, failFn)`
|
|
150
|
+
|
|
151
|
+
**Сигнатура:** `(lintText: unknown, passFn: (msg: string) => void, failFn: (msg: string) => void) => void`
|
|
152
|
+
|
|
153
|
+
**Параметри:**
|
|
154
|
+
|
|
155
|
+
- `lintText` — значення `scripts['lint-text']` із `package.json` (очікувано — рядок, але приймається `unknown` для безпечного narrowing).
|
|
156
|
+
- `passFn`, `failFn` — callback-и звіту.
|
|
157
|
+
|
|
158
|
+
**Повертає:** `void`.
|
|
159
|
+
|
|
160
|
+
**Поведінка:**
|
|
161
|
+
|
|
162
|
+
1. Якщо `typeof lintText === 'string'` — обрізає пробіли (`trim`), інакше `lt = ''`.
|
|
163
|
+
2. Якщо `lt === 'n-cursor lint-text'` — `passFn('lint-text делегує CLI n-cursor lint-text (cspell + shellcheck + markdownlint + v8r)')`.
|
|
164
|
+
3. Інакше — `failFn('package.json: lint-text має бути "n-cursor lint-text" — CLI пакета @nitra/cursor виконує cspell → shellcheck → markdownlint-cli2 → v8r (text.mdc)')`.
|
|
165
|
+
|
|
166
|
+
Це канонічна форма: пакет `@nitra/cursor` CLI-командою `n-cursor lint-text` всередині запускає послідовність `cspell` → `runShellcheckText()` → `bunx markdownlint-cli2 --fix` → `runV8rWithGlobs()`. Дозволені тільки пробіли навколо команди — інші варіанти забороняються.
|
|
167
|
+
|
|
168
|
+
**Side effects:** виклики callback-ів.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### `check(cwd = process.cwd())`
|
|
173
|
+
|
|
174
|
+
**Сигнатура:** `async (cwd?: string) => Promise<number>`
|
|
175
|
+
|
|
176
|
+
**Параметри:**
|
|
177
|
+
|
|
178
|
+
- `cwd` — корінь репозиторію. За замовчуванням `process.cwd()` (звичайний кейс — запуск зі скрипта `npx @nitra/cursor`).
|
|
179
|
+
|
|
180
|
+
**Повертає:** `Promise<number>` — `0`, якщо порушень немає; `1`, якщо є хоча б одне (значення обчислюється `reporter.getExitCode()`).
|
|
181
|
+
|
|
182
|
+
**Поведінка (послідовність кроків):**
|
|
183
|
+
|
|
184
|
+
1. `reporter = createCheckReporter()` — створює агрегатор звіту з полями `pass`, `fail`, `getExitCode`.
|
|
185
|
+
2. `await checkV8rIgnore(pass, fail, cwd)` — перевірка `.v8rignore`.
|
|
186
|
+
3. `await checkTextConfigsExistence(pass, fail, cwd)` — наявність FS-конфігів.
|
|
187
|
+
4. Шукає `.cursor/rules/n-text.mdc` та `npm/mdc/text.mdc` (через `existsSync`):
|
|
188
|
+
- якщо жодного — нейтральний `pass('n-text.mdc / npm/mdc/text.mdc відсутні — перевірку абзацу про апостроф пропущено')`;
|
|
189
|
+
- якщо є — для кожного існуючого файлу читає вміст і викликає `verifyUkApostropheRuleParagraph(p, body, fail, pass)`.
|
|
190
|
+
5. `await checkPackageJsonText(pass, fail, cwd)` — `lint-text` + workflow.
|
|
191
|
+
6. Повертає `reporter.getExitCode()`.
|
|
192
|
+
|
|
193
|
+
**Side effects:** читання файлів `.v8rignore`, `.cursor/rules/n-text.mdc`, `npm/mdc/text.mdc`, `package.json`, `.github/workflows/lint-text.yml`; `existsSync`-перевірки конфігів і файлів правил. Запис у файлову систему не виконується.
|
|
194
|
+
|
|
195
|
+
Зверніть увагу: коментар у тілі функції `check` фіксує, що **Prettier-конфіги / ignore** — окремий concern, обробляється в `rules/text/js/forbidden-prettier.mjs`, тут не валідується.
|
|
196
|
+
|
|
197
|
+
## Залежності
|
|
198
|
+
|
|
199
|
+
### Стандартна бібліотека Node.js
|
|
200
|
+
|
|
201
|
+
- `node:fs` → `existsSync` — синхронна перевірка наявності файлу.
|
|
202
|
+
- `node:fs/promises` → `readFile` — асинхронне читання текстових файлів у UTF-8.
|
|
203
|
+
- `node:path` → `join` — кросплатформенне склеювання шляхів від `cwd`.
|
|
204
|
+
|
|
205
|
+
### Внутрішні модулі пакета `@nitra/cursor`
|
|
206
|
+
|
|
207
|
+
- `../../../scripts/lib/check-reporter.mjs` → `createCheckReporter` — фабрика звіту з API `{ pass, fail, getExitCode }`.
|
|
208
|
+
- `../../../scripts/lib/gha-workflow.mjs` → `anyRunStepIncludes`, `parseWorkflowYaml` — парсер GitHub Actions workflow YAML + утиліта пошуку фрагмента в командах `run:`.
|
|
209
|
+
|
|
210
|
+
### Файли в репозиторії, які читає модуль
|
|
211
|
+
|
|
212
|
+
- `.v8rignore` (text).
|
|
213
|
+
- `.cursor/rules/n-text.mdc`, `npm/mdc/text.mdc` (markdown зі сторінки правила).
|
|
214
|
+
- `package.json` (JSON.parse).
|
|
215
|
+
- `.github/workflows/lint-text.yml` (YAML, парситься, з fallback на текстовий `includes`).
|
|
216
|
+
|
|
217
|
+
### Файли, які лише перевіряються на існування
|
|
218
|
+
|
|
219
|
+
- `.oxfmtrc.json`, `.cspell.json`, `.markdownlint-cli2.jsonc`, `.vscode/extensions.json`, `.vscode/settings.json`.
|
|
220
|
+
|
|
221
|
+
## Потік виконання / Використання
|
|
222
|
+
|
|
223
|
+
Файл — частина «JS-чек-пакета» правила `text` усередині CLI `@nitra/cursor`. Типовий запуск:
|
|
224
|
+
|
|
225
|
+
1. CI / локально викликає кореневий скрипт пакета `@nitra/cursor`, який обходить `npm/rules/<rule>/js/*.mjs` і шукає експорт `check`.
|
|
226
|
+
2. Для правила `text` серед інших підключається й `formatting.mjs`. CLI імпортує `check` і викликає `await check(repoRoot)`.
|
|
227
|
+
3. Усередині `check`:
|
|
228
|
+
- Запускаються чотири блоки перевірок: `.v8rignore`, FS-існування конфігів, абзац про апостроф у `.mdc`, `package.json` + workflow.
|
|
229
|
+
- Кожна знахідка фіксується в `reporter` (через локально розпаковані `pass` / `fail`).
|
|
230
|
+
- Після всіх перевірок `reporter.getExitCode()` повертає `0` або `1`.
|
|
231
|
+
4. CLI агрегує результати з усіх правил і виходить з відповідним кодом, який підхоплює GitHub Actions як статус кроку.
|
|
232
|
+
|
|
233
|
+
Контракт із Rego-частиною: цей модуль **не дублює** контентну валідацію. Якщо файл `.oxfmtrc.json` / `.cspell.json` / `.markdownlint-cli2.jsonc` / `.vscode/*.json` існує, його структуру перевіряє відповідний Rego-пакет (див. таблицю в розділі «Огляд»). JS-модуль гарантує лише сам факт існування, формат `.v8rignore` (plain-text), markdown-абзац про апостроф та форму скрипта `lint-text` + наявність workflow-кроку.
|
|
234
|
+
|
|
235
|
+
Розширення: щоб додати ще одну FS-перевірку — додай пару `[path, mdcRef]` у масив `checkTextConfigsExistence`. Щоб міняти канонічну команду — править `checkLintTextScript`. Більш складна валідація структурованих файлів повинна йти **в Rego**, а не в цей JS-модуль.
|
|
236
|
+
|
|
237
|
+
## Rebuild Test
|
|
238
|
+
|
|
239
|
+
Цей розділ — контрольний перелік фактів, за якими файл `formatting.mjs` має бути відновлюваним:
|
|
240
|
+
|
|
241
|
+
- Експорт єдиний — `async function check(cwd = process.cwd()): Promise<number>`.
|
|
242
|
+
- Імпорти: `existsSync` із `node:fs`; `readFile` із `node:fs/promises`; `join` із `node:path`; `createCheckReporter` із `../../../scripts/lib/check-reporter.mjs`; `anyRunStepIncludes`, `parseWorkflowYaml` із `../../../scripts/lib/gha-workflow.mjs`.
|
|
243
|
+
- Константа `UK_APOSTROPHE_HEADING = '**Український апостроф:**'`.
|
|
244
|
+
- `verifyUkApostropheRuleParagraph` — 4 кроки (heading → `U+0027` і `U+2019` → `’` → pass), кожен невдалий крок робить `failFn` і `return`.
|
|
245
|
+
- `checkV8rIgnore` — обовʼязкові рядки `['.vscode/extensions.json', '.vscode/settings.json']`; парсинг: split `\n` → `trim` → відкинути порожні й `#`-коментарі → `Set`.
|
|
246
|
+
- `checkTextConfigsExistence` — точна таблиця з 5 пар `[path, mdcRef]`; синхронне тіло; повертає `Promise.resolve()`.
|
|
247
|
+
- `checkPackageJsonText` — silent return якщо `package.json` відсутній; `JSON.parse` без try/catch; передає `pkg.scripts?.['lint-text']` у `checkLintTextScript`; перевіряє `.github/workflows/lint-text.yml`, спочатку через `parseWorkflowYaml` + `anyRunStepIncludes(..., 'bun run lint-text')`, з fallback на `wf.includes('bun run lint-text')`, якщо парсер не повернув root.
|
|
248
|
+
- `checkLintTextScript` — успіх **тільки** на `'n-cursor lint-text'` (після `trim`).
|
|
249
|
+
- `check`:
|
|
250
|
+
1. `createCheckReporter()` → деструктуризація `{ pass, fail }`;
|
|
251
|
+
2. `await checkV8rIgnore(pass, fail, cwd)`;
|
|
252
|
+
3. `await checkTextConfigsExistence(pass, fail, cwd)`;
|
|
253
|
+
4. збір `textRulePaths` із `.cursor/rules/n-text.mdc` і `npm/mdc/text.mdc` через `existsSync` → якщо пусто, `pass('… пропущено')`; інакше `for…of` із `readFile` і `verifyUkApostropheRuleParagraph(p, body, fail, pass)`;
|
|
254
|
+
5. `await checkPackageJsonText(pass, fail, cwd)`;
|
|
255
|
+
6. `return reporter.getExitCode()`.
|
|
256
|
+
- Коментар у тілі `check` явно фіксує, що `text.forbidden-prettier` живе в `rules/text/js/forbidden-prettier.mjs`.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# lint.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `lint.mjs` — це тонкий адаптер-обгортка, що реалізує CI-крок (контракт `lint(files)`) для правила `text` у складі n-cursor правил репозиторію. Його єдина роль — делегувати виконання у внутрішній CLI правила `text`, який знаходиться поряд у директорії `../lint/lint.mjs`.
|
|
6
|
+
|
|
7
|
+
Особливості модуля:
|
|
8
|
+
|
|
9
|
+
- Працює в режимі **whole-repo**: незалежно від того, які файли передає викликаюча сторона (наприклад, обгортка `lint-js` чи CI-оркестратор), аналіз виконується по всьому репозиторію цілком.
|
|
10
|
+
- Per-file режиму **немає**: вхідний параметр `files` навмисно ігнорується (підкреслено префіксом `_` у назві аргумента).
|
|
11
|
+
- Повертає числовий exit code (Promise), сумісний з конвенцією Unix-процесів: `0` — успіх, ненульовий — помилки лінту.
|
|
12
|
+
- Модуль не містить власної логіки перевірки тексту: вся реальна робота (запуск пов’язаних інструментів типу `dotenv-linter`, `shellcheck`, `v8r` тощо) — у `../lint/lint.mjs`.
|
|
13
|
+
|
|
14
|
+
Файл написаний у форматі **ES Modules** (`.mjs`) з JSDoc-анотаціями для статичної типізації без TypeScript-компіляції.
|
|
15
|
+
|
|
16
|
+
## Експорти / API
|
|
17
|
+
|
|
18
|
+
| Експорт | Тип | Призначення |
|
|
19
|
+
| ------- | ------------------------------------------------------ | ---------------------------------------------------------------------- |
|
|
20
|
+
| `lint` | `async function (files?: string[]) => Promise<number>` | CI-крок-делегат у `runLintTextCli`. Іменований експорт (named export). |
|
|
21
|
+
|
|
22
|
+
Default-експорту немає. Сторонніх ефектів на рівні модуля (top-level side effects) також немає — лише імпорт залежності.
|
|
23
|
+
|
|
24
|
+
### Сигнатура
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
export async function lint(_files): Promise<number>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Контракт CI-кроку
|
|
31
|
+
|
|
32
|
+
Модуль реалізує загальний інтерфейс CI-крока правил n-cursor:
|
|
33
|
+
|
|
34
|
+
- Назва експорту: `lint`.
|
|
35
|
+
- Сигнатура приймає опціональний `files: string[] | undefined`.
|
|
36
|
+
- Повертає `Promise<number>` — exit code.
|
|
37
|
+
- Не кидає винятків у нормальному сценарії; помилки сигналізуються через ненульовий exit code, який повертає делегат `runLintTextCli`.
|
|
38
|
+
|
|
39
|
+
## Функції
|
|
40
|
+
|
|
41
|
+
### `lint(_files)`
|
|
42
|
+
|
|
43
|
+
**Сигнатура:** `async function lint(_files: string[] | undefined): Promise<number>`
|
|
44
|
+
|
|
45
|
+
**Параметри:**
|
|
46
|
+
|
|
47
|
+
| Ім’я | Тип | Опис |
|
|
48
|
+
| -------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `_files` | `string[] \| undefined` | Список файлів, які теоретично потрібно перевірити. **Ігнорується** — правило `text` працює у whole-repo режимі. Префікс `_` у назві аргументу — конвенція, що позначає невикористаний параметр (зокрема, щоб ESLint/lint-правила не лаялись на unused arg). |
|
|
50
|
+
|
|
51
|
+
**Повертає:** `Promise<number>` — exit code, отриманий від виклику `runLintTextCli()`. Зазвичай `0` означає, що всі перевірки пройшли успішно, а будь-яке інше число — що знайдено порушення або сталася помилка інструмента.
|
|
52
|
+
|
|
53
|
+
**Side effects:** Сам по собі `lint` додаткових ефектів не вносить. Усі побічні ефекти (запис у stdout/stderr, читання файлів, запуск зовнішніх процесів) виконуються всередині `runLintTextCli()`, на який делегується виклик.
|
|
54
|
+
|
|
55
|
+
**Поведінка:**
|
|
56
|
+
|
|
57
|
+
1. Викликає `runLintTextCli()` без аргументів.
|
|
58
|
+
2. Повертає результат як є (через `await` неявно через `return await`-форму — `async`-функція автоматично «розгортає» Promise з тіла).
|
|
59
|
+
|
|
60
|
+
Логіки трансформації параметрів, фільтрації, перевірки прапорців тощо — немає. Це навмисно мінімалістична функція-адаптер.
|
|
61
|
+
|
|
62
|
+
## Залежності
|
|
63
|
+
|
|
64
|
+
### Імпорти
|
|
65
|
+
|
|
66
|
+
| Що | Звідки | Призначення |
|
|
67
|
+
| ---------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| `runLintTextCli` | `../lint/lint.mjs` | Запускає реальний CLI-обробник правила `text`. Саме він агрегує запуски під-інструментів (наприклад, `run-dotenv-linter.mjs`, `run-shellcheck.mjs`, `run-v8r.mjs`, які лежать у тій самій директорії `../lint/`). |
|
|
69
|
+
|
|
70
|
+
### Зовнішні (npm) залежності
|
|
71
|
+
|
|
72
|
+
Прямих імпортів з npm у файлі немає. Усі npm-залежності (якщо такі є) приховані у транзитивній залежності — `../lint/lint.mjs`.
|
|
73
|
+
|
|
74
|
+
### Структурний контекст
|
|
75
|
+
|
|
76
|
+
Файл лежить у `npm/rules/text/js/lint.mjs`. У тій самій директорії розташовані інші CI-крокові модулі правила `text` (наприклад, `formatting.mjs`, `forbidden-prettier.mjs`). Усі вони — однотипні делегати, що реалізують контракт CI-крока.
|
|
77
|
+
|
|
78
|
+
Бенефіціар:
|
|
79
|
+
|
|
80
|
+
- `../lint/lint.mjs` — справжній CLI правила `text`, що містить runner-логіку (parsing аргументів, оркестрація під-інструментів, формування звіту, exit code).
|
|
81
|
+
|
|
82
|
+
## Потік виконання / Використання
|
|
83
|
+
|
|
84
|
+
### Сценарій 1: виклик через CI-оркестратор правил
|
|
85
|
+
|
|
86
|
+
1. Оркестратор правил (наприклад, агрегований раннер у `lint-js` чи кореневий `bun run lint`) виявляє правило `text` і знаходить його CI-кроки в `npm/rules/text/js/*.mjs`.
|
|
87
|
+
2. Для кожного CI-крока (зокрема `lint.mjs`) оркестратор імпортує named export `lint`.
|
|
88
|
+
3. Викликає `await lint(filesArrayOrUndefined)`.
|
|
89
|
+
4. Адаптер ігнорує `files`, делегує виклик у `runLintTextCli()`.
|
|
90
|
+
5. Реальний CLI робить whole-repo аналіз: запускає `dotenv-linter`, `shellcheck`, `v8r` тощо (відповідно до того, що оголошено в `../lint/lint.mjs`).
|
|
91
|
+
6. Повертає exit code.
|
|
92
|
+
7. Оркестратор агрегує exit code-и всіх CI-кроків і приймає рішення про загальний успіх/невдачу.
|
|
93
|
+
|
|
94
|
+
### Сценарій 2: пряме виконання з коду
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
import { lint } from 'npm/rules/text/js/lint.mjs'
|
|
98
|
+
|
|
99
|
+
const code = await lint()
|
|
100
|
+
process.exit(code)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Передача аргументу `files` нічого не змінює (буде проігнорована):
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
const code = await lint(['some/file.txt']) // _files ігнорується
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Чому per-file немає
|
|
110
|
+
|
|
111
|
+
Правило `text` за своєю природою сканує весь репозиторій (наприклад, перевіряє `.env`-файли, shell-скрипти, JSON Schema-файли через v8r). Для нього передача обмеженого списку файлів від CI не має сенсу — обробляти треба весь робочий простір. Звідси й whole-repo режим, явно зафіксований у JSDoc-коментарі та підкреслений підкресленням у назві параметра.
|
|
112
|
+
|
|
113
|
+
### Очікувані коди виходу
|
|
114
|
+
|
|
115
|
+
| Code | Значення |
|
|
116
|
+
| ----- | --------------------------------------------------------------------------------------------------- |
|
|
117
|
+
| `0` | Усі під-перевірки правила `text` пройшли успішно. |
|
|
118
|
+
| ≠ `0` | Знайдено порушення або помилка одного з інструментів. Конкретний код визначається `runLintTextCli`. |
|
|
119
|
+
|
|
120
|
+
### Обробка помилок
|
|
121
|
+
|
|
122
|
+
Адаптер не містить `try/catch`. Якщо `runLintTextCli` кине виняток (нештатна ситуація), він пробулькується нагору — до викликаючого. Це навмисний дизайн: оркестратор сам вирішує, як трактувати unhandled rejection.
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# `lint.mjs` — CLI-обгортка `lint-text`
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `lint.mjs` — це CLI-обгортка над канонічним підкомандним конвеєром `lint-text` (правило `text.mdc`). Він призначений для запуску ланцюжка лінтерів текстових і конфігураційних артефактів проєкту з автоматичним передвстановленням залежностей та послідовним виконанням кроків з ранньою зупинкою на першій помилці.
|
|
6
|
+
|
|
7
|
+
Що робить файл:
|
|
8
|
+
|
|
9
|
+
1. Авто-встановлює зовнішні бінарники `shellcheck` і `dotenv-linter` через `ensureTool` (механізм brew/scoop/GitHub Release per-platform).
|
|
10
|
+
2. Перевіряє наявність системного `patch` (необхідний для авто-фіксу `shellcheck`) і друкує install-hint, якщо `patch` відсутній.
|
|
11
|
+
3. Послідовно запускає п'ять кроків лінтінгу:
|
|
12
|
+
- `cspell .` — перевірка правопису з використанням словника `@nitra/cspell-dict`;
|
|
13
|
+
- `runShellcheckText()` — авто-фікс і фінальна перевірка `*.sh` через `shellcheck`;
|
|
14
|
+
- `runDotenvLinter()` — авто-фікс і фінальна перевірка `.env*` через `dotenv-linter`;
|
|
15
|
+
- `bunx markdownlint-cli2 --fix "**/*.md" "**/*.mdc"` — авто-фікс Markdown;
|
|
16
|
+
- `runV8rWithGlobs()` — schema-валідація `json/json5/yaml/yml/toml` через `v8r`.
|
|
17
|
+
4. Першу ненульову exit-код у ланцюжку повертає як код виходу всього прогону; наступні кроки не запускаються.
|
|
18
|
+
|
|
19
|
+
Призначення preflight-блоку: без авто-встановлення локальний прогін може успішно пройти `cspell` і `markdownlint`, а CI на `ubuntu-latest` (де `shellcheck` є передвстановленим, але `dotenv-linter` — ні) падає на кроці `dotenv-linter` з неінформативним повідомленням. `ensureTool` збирає всі відсутні бінарники до запуску першого кроку.
|
|
20
|
+
|
|
21
|
+
Канон патерну `lint-*` (серіалізація через `runStandardLint`, без прямого `withLock`) описаний у `.cursor/rules/scripts.mdc`, секція «Серіалізація важких CLI-команд».
|
|
22
|
+
|
|
23
|
+
## Експорти / API
|
|
24
|
+
|
|
25
|
+
| Експорт | Тип | Призначення |
|
|
26
|
+
| ---------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
27
|
+
| `runLintTextCli` | `() => Promise<number>` | Публічна CLI-форма команди `lint-text`. Використовується з `bin/n-cursor.js` як підкоманда `lint-text`. Серіалізує виконання через `withLock('lint-text')` і додатково дедуплікує запуски за станом git-дерева через `runStandardLint`. |
|
|
28
|
+
|
|
29
|
+
Інші ідентифікатори модуля (`PATCH_PREFLIGHT`, `resolvePreflightBin`, `printPreflightMissingMessage`, `preflight`, `runLintTextSteps`) — внутрішні; не експортуються і не призначені для зовнішнього використання.
|
|
30
|
+
|
|
31
|
+
## Функції
|
|
32
|
+
|
|
33
|
+
### `resolvePreflightBin(dep)`
|
|
34
|
+
|
|
35
|
+
Шукає шлях до бінарника `dep.bin` у `PATH`. На Windows (`process.platform === 'win32'`) додатково перебирає альтернативні імена з `dep.winBins` (наприклад, для випадків з `.exe`/`.cmd` варіаціями).
|
|
36
|
+
|
|
37
|
+
- Сигнатура: `function resolvePreflightBin(dep: PreflightDep): string | null`
|
|
38
|
+
- Параметри:
|
|
39
|
+
- `dep` — опис залежності з canon-списку preflight-перевірок (тип `PreflightDep`).
|
|
40
|
+
- Повертає: абсолютний шлях до знайденого бінарника або `null`, якщо бінарник не знайдено.
|
|
41
|
+
- Side effects: відсутні (виклик `resolveCmd` лише читає `PATH`, не модифікує процес).
|
|
42
|
+
|
|
43
|
+
### `printPreflightMissingMessage(dep)`
|
|
44
|
+
|
|
45
|
+
Друкує stderr-повідомлення про відсутній бінарник: рядок-маркер з іменем, пояснення наслідків, install-команди і посилання на правило `text.mdc`.
|
|
46
|
+
|
|
47
|
+
- Сигнатура: `function printPreflightMissingMessage(dep: PreflightDep): void`
|
|
48
|
+
- Параметри:
|
|
49
|
+
- `dep` — опис залежності, джерело пояснення (`dep.explanation`) та install-команд (`dep.install`).
|
|
50
|
+
- Повертає: нічого (`undefined`).
|
|
51
|
+
- Side effects: виводить кілька рядків у `console.error` (stderr). Структура виводу:
|
|
52
|
+
- `❌ <bin> не знайдено в PATH.`
|
|
53
|
+
- відступний рядок з `dep.explanation`;
|
|
54
|
+
- заголовок ` Встанови:`;
|
|
55
|
+
- по рядку для кожного елемента `dep.install`;
|
|
56
|
+
- підказку ` Деталі: text.mdc → секція про lint-text.`
|
|
57
|
+
|
|
58
|
+
### `preflight(dep)`
|
|
59
|
+
|
|
60
|
+
Виконує preflight-перевірку наявності бінарника й сигналізує результат через консоль.
|
|
61
|
+
|
|
62
|
+
- Сигнатура: `function preflight(dep: PreflightDep): boolean`
|
|
63
|
+
- Параметри:
|
|
64
|
+
- `dep` — опис залежності для перевірки в `PATH`.
|
|
65
|
+
- Повертає: `true`, якщо бінарник знайдено; `false`, якщо відсутній.
|
|
66
|
+
- Side effects:
|
|
67
|
+
- На pass-шляху друкує `dep.successMsg` у `console.log`;
|
|
68
|
+
- На fail-шляху викликає `printPreflightMissingMessage(dep)` (вивід у `console.error`).
|
|
69
|
+
|
|
70
|
+
### `runLintTextSteps()`
|
|
71
|
+
|
|
72
|
+
Внутрішня функція, що послідовно прокручує всі кроки `lint-text` без захоплення локу (лок забезпечується зовнішньою обгорткою `runStandardLint`).
|
|
73
|
+
|
|
74
|
+
- Сигнатура: `function runLintTextSteps(): number`
|
|
75
|
+
- Параметри: немає.
|
|
76
|
+
- Повертає: `0`, якщо всі кроки успішні; інакше — exit-код першого кроку, що повернув ненульове значення.
|
|
77
|
+
- Алгоритм:
|
|
78
|
+
1. `ensureTool('shellcheck')` — авто-встановлення `shellcheck`; кидає виключення на провал (поширюється як exit `1` через зовнішній `runStandardLint`).
|
|
79
|
+
2. `ensureTool('dotenv-linter')` — те саме для `dotenv-linter`.
|
|
80
|
+
3. `preflight(PATCH_PREFLIGHT)` — hint-only перевірка `patch`; якщо `patch` відсутній — повертає `1` без подальших спроб.
|
|
81
|
+
4. `runLintStep('cspell', 'npx', ['cspell', '.'])` — `cspell .` через `npx`; ранній return при ненульовому коді.
|
|
82
|
+
5. Друк заголовку `▶ shellcheck (авто-фікс + фінальна перевірка *.sh)` і виклик `runShellcheckText()`; ранній return при ненульовому коді.
|
|
83
|
+
6. Друк заголовку `▶ dotenv-linter (авто-фікс + фінальна перевірка .env*)` і виклик `runDotenvLinter()`; ранній return при ненульовому коді.
|
|
84
|
+
7. `runLintStep('markdownlint', 'bunx', ['markdownlint-cli2', '--fix', '**/*.md', '**/*.mdc'])` — авто-фікс Markdown; ранній return при ненульовому коді.
|
|
85
|
+
8. Друк заголовку `▶ v8r (schema-валідація json/json5/yaml/yml/toml)` і повернення результату `runV8rWithGlobs()` як підсумкового exit-коду.
|
|
86
|
+
- Side effects: записує лог-рядки у `stdout`; запускає дочірні процеси через `runLintStep` / `ensureTool` / спеціалізовані обгортки; модифікує файлову систему через авто-фікс-кроки (`shellcheck -f diff` + `patch -p1`, `dotenv-linter --fix`, `markdownlint-cli2 --fix`).
|
|
87
|
+
|
|
88
|
+
### `runLintTextCli` (експорт)
|
|
89
|
+
|
|
90
|
+
Публічна CLI-форма команди.
|
|
91
|
+
|
|
92
|
+
- Сигнатура: `const runLintTextCli: () => Promise<number>`
|
|
93
|
+
- Параметри: немає.
|
|
94
|
+
- Повертає: `Promise<number>` — фінальний exit-код прогону (після lock + дедупу).
|
|
95
|
+
- Реалізація: делегує до `runStandardLint(import.meta.dirname, () => runLintTextSteps())`, тобто:
|
|
96
|
+
- `runStandardLint` бере лок з ім'ям, похідним від директорії скрипту (`import.meta.dirname`);
|
|
97
|
+
- дедуплікує запуски за станом git-дерева (якщо нічого не змінилося з попереднього успішного прогону — повторного виконання не буде);
|
|
98
|
+
- всередині локу викликає `runLintTextSteps()`.
|
|
99
|
+
- Side effects: лок-файл, лог-рядки, дочірні процеси (через внутрішній `runLintTextSteps`).
|
|
100
|
+
|
|
101
|
+
## Типи
|
|
102
|
+
|
|
103
|
+
### `PreflightDep`
|
|
104
|
+
|
|
105
|
+
Опис однієї залежності preflight-блоку. Визначений локальним JSDoc `@typedef`-ом.
|
|
106
|
+
|
|
107
|
+
| Поле | Тип | Опис |
|
|
108
|
+
| ------------- | ----------------- | ---------------------------------------------------------------------------- |
|
|
109
|
+
| `bin` | `string` | Ім'я виконуваного файлу (POSIX-варіант). |
|
|
110
|
+
| `winBins` | `string[]` (опц.) | Альтернативні імена бінарника на Windows. |
|
|
111
|
+
| `explanation` | `string` | Наслідки відсутності бінарника (для людино-зрозумілого stderr-повідомлення). |
|
|
112
|
+
| `install` | `string[]` | Команди встановлення, по одному рядку на спосіб/платформу. |
|
|
113
|
+
| `successMsg` | `string` | Повідомлення для `console.log` на pass-шляху preflight. |
|
|
114
|
+
|
|
115
|
+
## Константи
|
|
116
|
+
|
|
117
|
+
### `PATCH_PREFLIGHT`
|
|
118
|
+
|
|
119
|
+
Єдиний об'єкт типу `PreflightDep`, що описує системний `patch` як hint-only залежність:
|
|
120
|
+
|
|
121
|
+
- `bin: 'patch'`;
|
|
122
|
+
- `explanation: 'Без `patch` не застосуються авто-виправлення shellcheck (`shellcheck -f diff`+`patch -p1`).'` (зібраний через `[...].join('\n ')` з одного елемента; результат — рівно одна логічна підказка з відступом сумісним з шаблоном виводу `printPreflightMissingMessage`);
|
|
123
|
+
- `install`:
|
|
124
|
+
- `'macOS: зазвичай уже є в системі'`;
|
|
125
|
+
- `'Debian/Ubuntu: sudo apt-get install -y patch'`;
|
|
126
|
+
- `successMsg: '✅ patch знайдено в PATH — shellcheck auto-fix працюватиме'`.
|
|
127
|
+
|
|
128
|
+
`winBins` не задано — на Windows для `patch` шукається лише власне ім'я.
|
|
129
|
+
|
|
130
|
+
## Залежності
|
|
131
|
+
|
|
132
|
+
### Зовнішні модулі (Node built-ins)
|
|
133
|
+
|
|
134
|
+
- `node:process` — імпортується іменована змінна `platform` для детекції Windows у `resolvePreflightBin`.
|
|
135
|
+
|
|
136
|
+
### Внутрішні модулі (відносні імпорти)
|
|
137
|
+
|
|
138
|
+
| Модуль | Що використовується |
|
|
139
|
+
| -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
140
|
+
| `../../../scripts/lib/run-lint-step.mjs` | `runLintStep` — обгортка для запуску одного кроку лінту з префіксованим логом і нормалізованим exit-кодом. Використовується для `cspell` і `markdownlint`. |
|
|
141
|
+
| `../../../scripts/utils/resolve-cmd.mjs` | `resolveCmd` — пошук бінарника в `PATH`. Викликається з `resolvePreflightBin`. |
|
|
142
|
+
| `../../../scripts/lib/run-standard-lint.mjs` | `runStandardLint` — каноном обгортка `lint-*`: серіалізація через `withLock` + дедуп за станом git-дерева. Викликається з `runLintTextCli`. |
|
|
143
|
+
| `../../../scripts/lib/ensure-tool.mjs` | `ensureTool` — авто-встановлення бінарників (brew/scoop/GitHub Release). Викликається для `shellcheck` і `dotenv-linter` на початку `runLintTextSteps`. |
|
|
144
|
+
| `./run-dotenv-linter.mjs` | `runDotenvLinter` — авто-фікс + фінальна перевірка `.env*`. |
|
|
145
|
+
| `./run-shellcheck.mjs` | `runShellcheckText` — авто-фікс + фінальна перевірка `*.sh` через `shellcheck`. |
|
|
146
|
+
| `./run-v8r.mjs` | `runV8rWithGlobs` — schema-валідація `json/json5/yaml/yml/toml`. |
|
|
147
|
+
|
|
148
|
+
### Зовнішні CLI-інструменти (запускаються як дочірні процеси)
|
|
149
|
+
|
|
150
|
+
- `npx cspell .` — перевірка правопису (зі словником `@nitra/cspell-dict`).
|
|
151
|
+
- `shellcheck` — статичний аналізатор `*.sh` (передвстановлюється `ensureTool`).
|
|
152
|
+
- `dotenv-linter` — лінтер `.env*` (передвстановлюється `ensureTool`).
|
|
153
|
+
- `patch` — потрібен для `shellcheck -f diff | patch -p1` (hint-only, не встановлюється авто).
|
|
154
|
+
- `bunx markdownlint-cli2 --fix '**/*.md' '**/*.mdc'` — авто-фікс Markdown.
|
|
155
|
+
- `v8r` — schema-валідація конфігів (через `runV8rWithGlobs`).
|
|
156
|
+
|
|
157
|
+
### Правила-довідники
|
|
158
|
+
|
|
159
|
+
- `.cursor/rules/text.mdc` (`text.mdc`) — канонічний опис набору `lint-text`.
|
|
160
|
+
- `.cursor/rules/scripts.mdc` — секція «Серіалізація важких CLI-команд», що описує патерн `runStandardLint`/`withLock`.
|
|
161
|
+
|
|
162
|
+
## Потік виконання / Використання
|
|
163
|
+
|
|
164
|
+
### Виклик з CLI
|
|
165
|
+
|
|
166
|
+
Файл експортує `runLintTextCli`, який підключається з `bin/n-cursor.js` як підкоманда `lint-text`. Очікувана схема виклику:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
n-cursor lint-text
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Команда повертає exit-код процесу, рівний фінальному коду повернення з `runLintTextCli`.
|
|
173
|
+
|
|
174
|
+
### Послідовність кроків (happy path)
|
|
175
|
+
|
|
176
|
+
1. Зовнішній `runStandardLint` намагається взяти лок `lint-text`. Якщо лок вже зайнятий або стан git-дерева не змінився — прогон може дедуплікуватися або зачекати.
|
|
177
|
+
2. Всередині локу викликається `runLintTextSteps()`:
|
|
178
|
+
1. `ensureTool('shellcheck')` — авто-встановлення (на CI/локально).
|
|
179
|
+
2. `ensureTool('dotenv-linter')` — те саме.
|
|
180
|
+
3. `preflight(PATCH_PREFLIGHT)` — друкує `✅ patch знайдено в PATH — shellcheck auto-fix працюватиме` або hint про встановлення.
|
|
181
|
+
4. `cspell .` — друк префіксованих логів через `runLintStep`.
|
|
182
|
+
5. `▶ shellcheck (авто-фікс + фінальна перевірка *.sh)` → `runShellcheckText()`.
|
|
183
|
+
6. `▶ dotenv-linter (авто-фікс + фінальна перевірка .env*)` → `runDotenvLinter()`.
|
|
184
|
+
7. `markdownlint-cli2 --fix '**/*.md' '**/*.mdc'` через `bunx`.
|
|
185
|
+
8. `▶ v8r (schema-валідація json/json5/yaml/yml/toml)` → `runV8rWithGlobs()`; повертає його результат.
|
|
186
|
+
3. `runStandardLint` повертає підсумковий exit-код як `Promise<number>`.
|
|
187
|
+
|
|
188
|
+
### Семантика помилок
|
|
189
|
+
|
|
190
|
+
- Якщо `ensureTool` падає (не вдалося встановити `shellcheck` або `dotenv-linter`) — викидає виключення, яке поширюється з `runLintTextSteps` і обробляється на верхньому рівні (`runStandardLint`) як exit `1`.
|
|
191
|
+
- Якщо `patch` відсутній — `preflight` повертає `false`, `runLintTextSteps` повертає `1` ще до запуску `cspell`.
|
|
192
|
+
- Будь-який наступний крок (`cspell`, `shellcheck`, `dotenv-linter`, `markdownlint`, `v8r`), який повернув ненульовий код, припиняє ланцюжок: цей код повертається з `runLintTextSteps` і далі з `runLintTextCli`.
|
|
193
|
+
|
|
194
|
+
### Приклад використання у скрипті (Node)
|
|
195
|
+
|
|
196
|
+
```js
|
|
197
|
+
import { runLintTextCli } from './lint.mjs'
|
|
198
|
+
|
|
199
|
+
const code = await runLintTextCli()
|
|
200
|
+
process.exit(code)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Побічні ефекти на файлову систему
|
|
204
|
+
|
|
205
|
+
Кроки `runShellcheckText`, `runDotenvLinter`, `markdownlint-cli2 --fix` і потенційно `v8r` можуть модифікувати файли (авто-фікс). Це штатна поведінка `lint-text` — після прогону можливі правки в робочому дереві.
|
|
206
|
+
|
|
207
|
+
## Rebuild Test
|
|
208
|
+
|
|
209
|
+
З цієї документації можна відновити поведінку модуля:
|
|
210
|
+
|
|
211
|
+
- Один експорт — асинхронна функція `runLintTextCli()` без параметрів, повертає `Promise<number>` (exit-код).
|
|
212
|
+
- Реалізація `runLintTextCli`: `() => runStandardLint(import.meta.dirname, () => runLintTextSteps())`.
|
|
213
|
+
- `runLintTextSteps()` синхронно:
|
|
214
|
+
1. викликає `ensureTool('shellcheck')` і `ensureTool('dotenv-linter')`;
|
|
215
|
+
2. виконує `preflight(PATCH_PREFLIGHT)` і повертає `1`, якщо `patch` відсутній;
|
|
216
|
+
3. послідовно запускає кроки `cspell` → `runShellcheckText` → `runDotenvLinter` → `markdownlint-cli2 --fix '**/*.md' '**/*.mdc'` → `runV8rWithGlobs`, друкуючи відповідні `▶`-заголовки перед shellcheck/dotenv/v8r;
|
|
217
|
+
4. ранній return на першому ненульовому коді; інакше повертає результат `runV8rWithGlobs()`.
|
|
218
|
+
- Допоміжні функції: `resolvePreflightBin` (з підтримкою `winBins` на Windows), `printPreflightMissingMessage` (формат stderr-повідомлення), `preflight` (об'єднання resolve+print і друк `successMsg` на pass).
|
|
219
|
+
- Константа `PATCH_PREFLIGHT` з полями `bin/explanation/install/successMsg` (без `winBins`).
|
|
220
|
+
- Залежності: `node:process` (`platform`); локальні модулі `run-lint-step`, `resolve-cmd`, `run-standard-lint`, `ensure-tool`, `run-dotenv-linter`, `run-shellcheck`, `run-v8r`.
|