@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,393 @@
|
|
|
1
|
+
# layout.mjs — перевірка відповідності правилам Bun (`bun.mdc`)
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `npm/rules/bun/js/layout.mjs` — це JS-частина rule-перевірки **bun** для CLI `@nitra/cursor`. Він перевіряє ту частину правил `bun.mdc`, яку **неможливо** покрити Rego-політиками з `npm/policy/bun/*` (тобто FS-existence та cross-file зв'язки між `.n-cursor.json` і `package.json`).
|
|
6
|
+
|
|
7
|
+
Призначений запускатися як check у кореневому репозиторії (`cwd = process.cwd()`). Перевіряє три блоки інваріантів:
|
|
8
|
+
|
|
9
|
+
1. **FS-наявність обовʼязкових файлів** монорепозиторію Bun:
|
|
10
|
+
- `bun.lock` (lockfile від `bun i`);
|
|
11
|
+
- `bunfig.toml` (наявність файла; структуру (`[install].linker == "hoisted"`) перевіряє Rego — `npm/policy/bun/bunfig/`);
|
|
12
|
+
- `package.json` у корені.
|
|
13
|
+
2. **Заборонені артефакти конкурентних пакет-менеджерів** (yarn / pnpm / npm):
|
|
14
|
+
- файли `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `.yarnrc.yml`;
|
|
15
|
+
- директорія `.yarn/`.
|
|
16
|
+
3. **Двосторонній зв'язок** між `.n-cursor.json:rules` / `disable-rules` та `package.json:scripts` для правил, що мають окрему `lint-<id>`-обгортку:
|
|
17
|
+
- правило активне (присутнє у `rules`) → скрипт `lint-<id>` мусить існувати;
|
|
18
|
+
- правило не активне (немає в `rules` або є в `disable-rules`) → скрипту `lint-<id>` і токена `bun run lint-<id>` в агрегованому `scripts.lint` **не може** бути (інакше `bun run lint` падатиме на правилі, яке у конфізі вимкнено).
|
|
19
|
+
|
|
20
|
+
Те, що покриває **Rego** (тобто **не** перевіряється цим файлом):
|
|
21
|
+
|
|
22
|
+
- `npm/policy/bun/bunfig/` — `[install].linker == "hoisted"` у `bunfig.toml`;
|
|
23
|
+
- `npm/policy/bun/package_json/` — відсутність `packageManager` / `dependencies` у кореневому `package.json`, у `devDependencies` лише пакети `@nitra/*`, агрегований `lint`-скрипт покриває всі `lint-*` через `bun run` і завершується `&& oxfmt .`.
|
|
24
|
+
|
|
25
|
+
JS-копії перевірок `devDependencies` (історично — `isAllowedRootDevDependency`) **видалено**, щоб уникнути дублювання джерела істини.
|
|
26
|
+
|
|
27
|
+
Звітування винесене в `createCheckReporter()` з `../../../scripts/lib/check-reporter.mjs`: модуль не друкує сам, а викликає `pass(msg)` / `fail(msg)`, після чого повертає `reporter.getExitCode()` (`0` — все OK, `1` — є провали).
|
|
28
|
+
|
|
29
|
+
## Експорти / API
|
|
30
|
+
|
|
31
|
+
Модуль експортує **одну** іменовану функцію:
|
|
32
|
+
|
|
33
|
+
| Експорт | Сигнатура | Призначення |
|
|
34
|
+
| ------- | ----------------------------------------- | --------------------------------------------------- |
|
|
35
|
+
| `check` | `async (cwd?: string) => Promise<number>` | Точка входу check-у; повертає exit-код (`0` / `1`). |
|
|
36
|
+
|
|
37
|
+
Усе інше (`WHITESPACE_RE`, `RULE_SCRIPTS`, `loadNCursorRules`, `lintChainHasScript`, `backtickJoin`, `ownerStatus`, `checkCursorRuleScripts`) — **внутрішні** (module-private), не експортуються.
|
|
38
|
+
|
|
39
|
+
## Константи
|
|
40
|
+
|
|
41
|
+
### `WHITESPACE_RE`
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
const WHITESPACE_RE = /\s+/u
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Регулярний вираз для розділення значення `scripts.lint` на токени (послідовність пробільних символів). Використовується в `lintChainHasScript` для безпечного матчингу токена `bun run <script>` як **окремого** токена (а не префікса). Прапорець `u` — Unicode-сумісний матчинг пробілів.
|
|
48
|
+
|
|
49
|
+
### `RULE_SCRIPTS`
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
const RULE_SCRIPTS = [
|
|
53
|
+
{ rules: ['docker'], script: 'lint-docker', doc: 'docker.mdc' },
|
|
54
|
+
{ rules: ['k8s'], script: 'lint-k8s', doc: 'k8s.mdc' },
|
|
55
|
+
{ rules: ['image-avif', 'image-compress'], script: 'lint-image', doc: 'image-avif.mdc / image-compress.mdc' }
|
|
56
|
+
]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Декларативна таблиця обгорток `lint-<id>` та їхніх правил-власників. Один скрипт може мати **кілька** власників (`lint-image` обслуговує і `image-avif`, і `image-compress`); скрипт вважається «потрібним», якщо **хоча б одне** з власних правил активне у `.n-cursor.json:rules`.
|
|
60
|
+
|
|
61
|
+
Кожен елемент має тип `RuleScript`:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
type RuleScript = {
|
|
65
|
+
rules: string[] // id правил-власників (>= 1); поки активний хоча б один — скрипт обовʼязковий
|
|
66
|
+
script: string // ім'я скрипта в package.json:scripts (напр. "lint-docker")
|
|
67
|
+
doc: string // .mdc-файл (або кома-список) для повідомлення check-у
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Розширення таблиці — єдиний спосіб додати нову `lint-<id>`-обгортку під перевірку двостороннього звʼязку.
|
|
72
|
+
|
|
73
|
+
## Функції
|
|
74
|
+
|
|
75
|
+
### `loadNCursorRules(cwd)`
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
async function loadNCursorRules(cwd: string): Promise<{ rules: Set<string>, disabled: Set<string> }>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Зчитує `.n-cursor.json` із кореня репозиторію та повертає набори активних і явно вимкнених правил.
|
|
82
|
+
|
|
83
|
+
**Параметри:**
|
|
84
|
+
|
|
85
|
+
- `cwd` — абсолютний шлях до кореня репозиторію.
|
|
86
|
+
|
|
87
|
+
**Повертає:** `Promise<{ rules: Set<string>, disabled: Set<string> }>`:
|
|
88
|
+
|
|
89
|
+
- `rules` — `Set` рядків зі значення `rules` у JSON; якщо поле відсутнє або не масив — порожній `Set`.
|
|
90
|
+
- `disabled` — `Set` рядків зі значення `disable-rules`; якщо поле відсутнє або не масив — порожній `Set`.
|
|
91
|
+
|
|
92
|
+
**Поведінка при помилках (fail-safe):**
|
|
93
|
+
|
|
94
|
+
- файл `.n-cursor.json` відсутній — повертає `{ rules: new Set(), disabled: new Set() }`;
|
|
95
|
+
- помилка `JSON.parse` — повертає той самий «порожній» обʼєкт (через `try/catch` без логування).
|
|
96
|
+
|
|
97
|
+
**Side effects:** виключно `fs.promises.readFile` (асинхронне читання UTF-8) та `existsSync` для перевірки наявності.
|
|
98
|
+
|
|
99
|
+
### `lintChainHasScript(lintScript, target)`
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
function lintChainHasScript(lintScript: string, target: string): boolean
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Перевіряє, чи містить chain зі `scripts.lint` виклик саме `bun run <target>` **як окремий токен**.
|
|
106
|
+
|
|
107
|
+
**Параметри:**
|
|
108
|
+
|
|
109
|
+
- `lintScript` — значення `scripts.lint` (або порожній рядок, якщо його немає).
|
|
110
|
+
- `target` — ім'я скрипта без префіксів (`lint-docker`, `lint-k8s`, `lint-image`).
|
|
111
|
+
|
|
112
|
+
**Повертає:** `boolean` — `true`, якщо в розбитому за пробілами chain'і знайдено послідовність токенів `bun`, `run`, `<target>` поруч.
|
|
113
|
+
|
|
114
|
+
**Чому саме «токени», а не `String.prototype.includes`:** інакше виникне false-positive для `bun run lint-k8s-foo`, який матчиться як префікс `bun run lint-k8s`. Розбиття за `WHITESPACE_RE` усуває цю проблему — кожен токен порівнюється повним рівнем.
|
|
115
|
+
|
|
116
|
+
**Side effects:** немає (чиста функція).
|
|
117
|
+
|
|
118
|
+
### `backtickJoin(items, sep)`
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
function backtickJoin(items: string[], sep: string): string
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Загортає кожен елемент у backticks і зʼєднує через `sep`. Винесено окремо, щоб не нестити template literals у `pass` / `fail`-повідомленнях.
|
|
125
|
+
|
|
126
|
+
**Параметри:**
|
|
127
|
+
|
|
128
|
+
- `items` — масив ідентифікаторів (наприклад id правил).
|
|
129
|
+
- `sep` — роздільник (`', '` для перерахування, `'/'` для альтернативного списку).
|
|
130
|
+
|
|
131
|
+
**Повертає:** рядок виду `` `a`, `b` `` (або `` `a`/`b` ``).
|
|
132
|
+
|
|
133
|
+
**Side effects:** немає (чиста функція).
|
|
134
|
+
|
|
135
|
+
### `ownerStatus(owners, cursorRules)`
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
function ownerStatus(
|
|
139
|
+
owners: string[],
|
|
140
|
+
cursorRules: { rules: Set<string>, disabled: Set<string> }
|
|
141
|
+
): { enabled: string[], reason: string }
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Описує стан правил-власників скрипта для повідомлень про `reason`. Повертає або список увімкнених власників (для passing-кейсу «правило є»), або компактний опис, чому всі вимкнені (для inverse-fail).
|
|
145
|
+
|
|
146
|
+
**Параметри:**
|
|
147
|
+
|
|
148
|
+
- `owners` — id правил-власників (`>= 1`).
|
|
149
|
+
- `cursorRules` — `{ rules, disabled }`, отриманий від `loadNCursorRules`.
|
|
150
|
+
|
|
151
|
+
**Повертає:** `{ enabled, reason }`:
|
|
152
|
+
|
|
153
|
+
- `enabled` — підмасив `owners`, які присутні в `cursorRules.rules`.
|
|
154
|
+
- `reason` — людинозрозумілий текст про стан правил для логу:
|
|
155
|
+
- якщо є хоча б один активний — `` правило `x` `` або `` правила `x`, `y` `` (з узгодженням числа: `правило` / `правила`);
|
|
156
|
+
- якщо власник один і не активний — `` правило `x` `` + (`в disable-rules` | `відсутнє в rules`);
|
|
157
|
+
- якщо власників кілька й жоден не активний — `` `a`/`b` `` + (`усі власники в disable-rules` | `жоден власник не активний у rules`); вибір ноти залежить від того, чи **всі** власники у `disable-rules`, чи лише «не в rules».
|
|
158
|
+
|
|
159
|
+
**Side effects:** немає (чиста функція).
|
|
160
|
+
|
|
161
|
+
### `checkCursorRuleScripts(reporter, scripts, cursorRules)`
|
|
162
|
+
|
|
163
|
+
```js
|
|
164
|
+
function checkCursorRuleScripts(
|
|
165
|
+
reporter: { pass: (msg: string) => void, fail: (msg: string) => void },
|
|
166
|
+
scripts: Record<string, string>,
|
|
167
|
+
cursorRules: { rules: Set<string>, disabled: Set<string> }
|
|
168
|
+
): void
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Перевіряє двосторонній зв'язок `rules` ↔ `scripts.lint-<id>` для всіх записів із `RULE_SCRIPTS`.
|
|
172
|
+
|
|
173
|
+
**Параметри:**
|
|
174
|
+
|
|
175
|
+
- `reporter` — обʼєкт з callback-ами `pass(msg)` / `fail(msg)` (від `createCheckReporter()`).
|
|
176
|
+
- `scripts` — обʼєкт `scripts` із розпарсеного `package.json`.
|
|
177
|
+
- `cursorRules` — `{ rules, disabled }` з `loadNCursorRules`.
|
|
178
|
+
|
|
179
|
+
**Алгоритм на кожен запис `RULE_SCRIPTS`:**
|
|
180
|
+
|
|
181
|
+
1. `status = ownerStatus(owners, cursorRules)`.
|
|
182
|
+
2. `present = Boolean(scripts[script])` — чи є скрипт у `package.json:scripts`.
|
|
183
|
+
3. `inChain = lintChainHasScript(scripts.lint, script)` — чи згаданий `bun run <script>` у chain'і `scripts.lint` (якщо `scripts.lint` не рядок — `lintScript = ''`).
|
|
184
|
+
4. **Якщо хоч одне правило-власник активне** (`status.enabled.length > 0`):
|
|
185
|
+
- `present === true` → `pass`: ``package.json: є `<script>` (<reason> у .n-cursor.json)``;
|
|
186
|
+
- `present === false` → `fail`: ``У .n-cursor.json увімкнено <reason> — додай скрипт `<script>` у кореневий package.json (див. <doc>)``;
|
|
187
|
+
- далі `continue` (не перевіряємо inverse-кейс).
|
|
188
|
+
5. **Якщо жоден власник не активний:**
|
|
189
|
+
- `present === true` → `fail`: ``У .n-cursor.json немає активних власників `a`/`b` — прибери скрипт `<script>` з кореневого package.json (див. <doc>)``;
|
|
190
|
+
- `inChain === true` → `fail`: ``У `scripts.lint` є `bun run <script>`, але серед `a/b` жоден не активний у .n-cursor.json — прибери з ланцюжка lint (див. <doc>)``;
|
|
191
|
+
- `!present && !inChain` → `pass`: ``package.json: `<script>` відсутній (<reason>)``.
|
|
192
|
+
|
|
193
|
+
Зверни увагу: коли `present === true` і `inChain === true` одночасно (при неактивних власниках), будуть **два** `fail`-повідомлення — окремо про скрипт і окремо про chain. Це дозволяє лагодити обидва місця одним проходом.
|
|
194
|
+
|
|
195
|
+
**Side effects:** виклики `reporter.pass()` / `reporter.fail()` (тобто акумуляція в звіт, що формує exit-код).
|
|
196
|
+
|
|
197
|
+
### `check(cwd?)` — публічна точка входу
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
export async function check(cwd: string = process.cwd()): Promise<number>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Параметри:**
|
|
204
|
+
|
|
205
|
+
- `cwd` — корінь репозиторію. За замовчуванням — `process.cwd()`.
|
|
206
|
+
|
|
207
|
+
**Повертає:** `Promise<number>` — exit-код від `reporter.getExitCode()`: `0` (усі перевірки pass) або `1` (є хоча б один `fail`).
|
|
208
|
+
|
|
209
|
+
**Послідовність перевірок:**
|
|
210
|
+
|
|
211
|
+
1. Створюється `reporter = createCheckReporter()`; деструктуруються `{ pass, fail }`.
|
|
212
|
+
2. **Заборонені lockfile / конфіги конкурентних PM:** для кожного з `['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', '.yarnrc.yml']` — `existsSync` → `fail`("Знайдено заборонений файл: ..."); інакше `pass`("Немає ...").
|
|
213
|
+
3. **Директорія `.yarn/`** — `existsSync` → `fail`("Знайдено директорію .yarn — видали її") / `pass`("Немає .yarn/").
|
|
214
|
+
4. **`bun.lock`** — `existsSync` → `pass`("bun.lock є") / `fail`("Відсутній bun.lock — запусти bun i").
|
|
215
|
+
5. **`bunfig.toml`** — `existsSync` → `pass` (з підказкою, що структуру перевіряє Rego через `npx @nitra/cursor fix → bun.bunfig`) / `fail` ("Відсутній bunfig.toml — створи з `[install] linker = \"hoisted\"` (bun.mdc)").
|
|
216
|
+
6. **Завантаження `.n-cursor.json`** через `loadNCursorRules(cwd)`.
|
|
217
|
+
7. **Кореневий `package.json`:**
|
|
218
|
+
- якщо відсутній → `fail`("Відсутній package.json у корені") і **ранній вихід** через `return reporter.getExitCode()` (далі чекери не виконуються);
|
|
219
|
+
- інакше — читається через `readFile(...,'utf8')` + `JSON.parse`.
|
|
220
|
+
8. **`scripts`** береться як `pkg.scripts` (з захистом від `null`/не-обʼєктів через `typeof === 'object'`); якщо `scripts` некоректний — `{}`.
|
|
221
|
+
9. **Виклик** `checkCursorRuleScripts(reporter, scripts, cursorRules)`.
|
|
222
|
+
10. **Повернення** `reporter.getExitCode()`.
|
|
223
|
+
|
|
224
|
+
**Side effects:**
|
|
225
|
+
|
|
226
|
+
- синхронні читання FS через `existsSync` (≥ 8 викликів);
|
|
227
|
+
- асинхронне читання `.n-cursor.json` і `package.json` через `fs/promises.readFile`;
|
|
228
|
+
- мутація стану `reporter` через `pass` / `fail`;
|
|
229
|
+
- `JSON.parse` обох конфігів — для `package.json` **без** `try/catch` (битий JSON у корені призведе до `throw` назовні; це усвідомлений вибір — кореневий `package.json` має бути валідним JSON, інакше нічого не запуститься).
|
|
230
|
+
|
|
231
|
+
## Залежності
|
|
232
|
+
|
|
233
|
+
### Зовнішні (Node.js core)
|
|
234
|
+
|
|
235
|
+
- `node:fs` → `existsSync` — синхронна перевірка FS-існування файлів і директорії `.yarn/`.
|
|
236
|
+
- `node:fs/promises` → `readFile` — асинхронне читання `.n-cursor.json` і `package.json` (UTF-8).
|
|
237
|
+
- `node:path` → `join` — побудова шляхів у `cwd`.
|
|
238
|
+
|
|
239
|
+
### Внутрішні
|
|
240
|
+
|
|
241
|
+
- `../../../scripts/lib/check-reporter.mjs` → `createCheckReporter` — фабрика репортера з API `{ pass, fail, getExitCode }`. Це **єдина** проєктна залежність модуля; усі повідомлення йдуть через неї, exit-код — теж.
|
|
242
|
+
|
|
243
|
+
### Що **не** імпортується (свідомо)
|
|
244
|
+
|
|
245
|
+
- Rego-логіка політик `npm/policy/bun/*` — викликається окремо через `npx @nitra/cursor check` / `fix`; цей модуль ані не запускає Rego, ані не дублює його перевірки.
|
|
246
|
+
- Жодних спільних утиліт для роботи з `package.json` — `JSON.parse` робиться інлайн, бо схема локальна (потрібен лише `scripts`).
|
|
247
|
+
|
|
248
|
+
## Потік виконання / Використання
|
|
249
|
+
|
|
250
|
+
### Як викликається з CLI
|
|
251
|
+
|
|
252
|
+
Файл `layout.mjs` — це JS-частина rule-перевірки `bun` (директорія `npm/rules/bun/js/`). CLI `@nitra/cursor` (через свій диспатчер у `npm/cli/*` та `npm/scripts/lint/*`) автоматично знаходить такі `layout.mjs`-точки входу для всіх включених у `.n-cursor.json:rules` правил і викликає експортовану `check(cwd)`. Exit-код пробрасується назовні: якщо `check` повертає `1`, CLI рапортує `fail` цього rule-у, що далі піднімається на рівень агрегованого `bun run lint`.
|
|
253
|
+
|
|
254
|
+
### Типовий потік
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
n-cursor check / lint (CLI)
|
|
258
|
+
|
|
|
259
|
+
v
|
|
260
|
+
import { check } from 'npm/rules/bun/js/layout.mjs'
|
|
261
|
+
|
|
|
262
|
+
v
|
|
263
|
+
check(cwd)
|
|
264
|
+
|
|
|
265
|
+
+-> existsSync × forbidden files / .yarn -> pass | fail
|
|
266
|
+
|
|
|
267
|
+
+-> existsSync bun.lock / bunfig.toml -> pass | fail
|
|
268
|
+
|
|
|
269
|
+
+-> loadNCursorRules(cwd)
|
|
270
|
+
| \-> readFile '.n-cursor.json' (silent on error)
|
|
271
|
+
|
|
|
272
|
+
+-> readFile + JSON.parse 'package.json'
|
|
273
|
+
| (early return on missing)
|
|
274
|
+
|
|
|
275
|
+
+-> checkCursorRuleScripts(reporter, scripts, cursorRules)
|
|
276
|
+
| \-> for each RULE_SCRIPTS:
|
|
277
|
+
| ownerStatus -> lintChainHasScript -> pass/fail
|
|
278
|
+
|
|
|
279
|
+
+-> return reporter.getExitCode() // 0 | 1
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Сценарій A — все OK
|
|
283
|
+
|
|
284
|
+
`.n-cursor.json`:
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{ "rules": ["docker", "k8s"] }
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
`package.json:scripts`:
|
|
291
|
+
|
|
292
|
+
```json
|
|
293
|
+
{
|
|
294
|
+
"lint-docker": "n-cursor lint-docker",
|
|
295
|
+
"lint-k8s": "n-cursor lint-k8s",
|
|
296
|
+
"lint": "bun run lint-docker && bun run lint-k8s && oxfmt ."
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
- `bun.lock`, `bunfig.toml`, `package.json` — присутні.
|
|
301
|
+
- Жоден із заборонених файлів / `.yarn/` не існує.
|
|
302
|
+
- Для `docker` і `k8s` — `enabled.length > 0`, `present === true` → два `pass`.
|
|
303
|
+
- Для `lint-image` — жоден власник (`image-avif`/`image-compress`) не активний, скрипт відсутній, у chain'і його немає → `pass` ("`lint-image` відсутній (`image-avif`/`image-compress` — жоден власник не активний у rules)").
|
|
304
|
+
- Exit-код: `0`.
|
|
305
|
+
|
|
306
|
+
### Сценарій B — k8s у `disable-rules`, але `lint-k8s` залишився
|
|
307
|
+
|
|
308
|
+
`.n-cursor.json`:
|
|
309
|
+
|
|
310
|
+
```json
|
|
311
|
+
{ "disable-rules": ["k8s"] }
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
`package.json:scripts`:
|
|
315
|
+
|
|
316
|
+
```json
|
|
317
|
+
{
|
|
318
|
+
"lint-k8s": "n-cursor lint-k8s",
|
|
319
|
+
"lint": "bun run lint-k8s && oxfmt ."
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
- `ownerStatus(['k8s'], ...)` → `enabled: []`, `reason: "правило \`k8s\` в disable-rules"`.
|
|
324
|
+
- `present === true` → `fail`: "У .n-cursor.json немає активних власників `k8s` — прибери скрипт `lint-k8s` з кореневого package.json (див. k8s.mdc)".
|
|
325
|
+
- `inChain === true` → `fail`: "У `scripts.lint` є `bun run lint-k8s`, але серед `k8s` жоден не активний у .n-cursor.json — прибери з ланцюжка lint (див. k8s.mdc)".
|
|
326
|
+
- Exit-код: `1`.
|
|
327
|
+
|
|
328
|
+
### Сценарій C — `image-avif` активний, `lint-image` відсутній
|
|
329
|
+
|
|
330
|
+
`.n-cursor.json`:
|
|
331
|
+
|
|
332
|
+
```json
|
|
333
|
+
{ "rules": ["image-avif"] }
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
`package.json:scripts`: (немає `lint-image`)
|
|
337
|
+
|
|
338
|
+
- `ownerStatus(['image-avif', 'image-compress'], ...)` → `enabled: ['image-avif']`, `reason: "правило \`image-avif\`"`.
|
|
339
|
+
- `present === false` → `fail`: "У .n-cursor.json увімкнено правило `image-avif` — додай скрипт `lint-image` у кореневий package.json (див. image-avif.mdc / image-compress.mdc)".
|
|
340
|
+
- Exit-код: `1`.
|
|
341
|
+
|
|
342
|
+
### Сценарій D — відсутній `package.json`
|
|
343
|
+
|
|
344
|
+
- `pass` / `fail` по lockfile + `bun.lock` + `bunfig.toml` як завжди.
|
|
345
|
+
- Після `loadNCursorRules` — `existsSync(pkgPath) === false` → `fail`("Відсутній package.json у корені") і **ранній `return`**.
|
|
346
|
+
- `checkCursorRuleScripts` **не викликається**.
|
|
347
|
+
- Exit-код: `1`.
|
|
348
|
+
|
|
349
|
+
### Контракт із Rego / `.mdc`
|
|
350
|
+
|
|
351
|
+
Цей файл є JS-частиною rule `bun`. Він **доповнює** Rego-політики `npm/policy/bun/bunfig/` і `npm/policy/bun/package_json/`, які перевіряють структуру файлів (а не їх існування або crossref). Опис правила людською мовою — у `bun.mdc` (на нього посилаються `pass`/`fail`-повідомлення про `bunfig.toml`).
|
|
352
|
+
|
|
353
|
+
При додаванні нової `lint-<id>`-обгортки достатньо розширити масив `RULE_SCRIPTS` — функції `ownerStatus`, `checkCursorRuleScripts`, `lintChainHasScript` та `backtickJoin` працюють декларативно й нової JS-логіки не потребують.
|
|
354
|
+
|
|
355
|
+
## Rebuild Test
|
|
356
|
+
|
|
357
|
+
Цей блок описує, що саме треба було б реалізувати, щоб отримати функціонально еквівалентний `layout.mjs` з нуля — для перевірки повноти документації:
|
|
358
|
+
|
|
359
|
+
1. **ESM-модуль** на стандартному Node.js (без зовнішніх пакетів окрім `createCheckReporter`).
|
|
360
|
+
2. Імпортувати `existsSync` з `node:fs`, `readFile` з `node:fs/promises`, `join` з `node:path`, `createCheckReporter` з `../../../scripts/lib/check-reporter.mjs`.
|
|
361
|
+
3. Оголосити константу `WHITESPACE_RE = /\s+/u`.
|
|
362
|
+
4. Оголосити масив `RULE_SCRIPTS` з трьох елементів (`docker`/`lint-docker`/`docker.mdc`, `k8s`/`lint-k8s`/`k8s.mdc`, `['image-avif','image-compress']`/`lint-image`/`'image-avif.mdc / image-compress.mdc'`).
|
|
363
|
+
5. Реалізувати `loadNCursorRules(cwd)`:
|
|
364
|
+
- перевірити `existsSync(join(cwd, '.n-cursor.json'))`; якщо ні — повернути `{ rules: new Set(), disabled: new Set() }`;
|
|
365
|
+
- `try { JSON.parse(await readFile(..., 'utf8')) } catch { return empty }`;
|
|
366
|
+
- витягти `rules` і `disable-rules` як масиви (з захистом `Array.isArray`), кожен елемент привести `String(...)`, обернути в `Set`.
|
|
367
|
+
6. Реалізувати `lintChainHasScript(lintScript, target)`:
|
|
368
|
+
- якщо `lintScript` falsy → `false`;
|
|
369
|
+
- інакше `lintScript.split(WHITESPACE_RE)` і пошук послідовності токенів `bun`, `run`, `target` через `tokens.some((tok, i) => ...)`.
|
|
370
|
+
7. Реалізувати `backtickJoin(items, sep)` як `items.map(r => '`' + r + '`').join(sep)`.
|
|
371
|
+
8. Реалізувати `ownerStatus(owners, cursorRules)`:
|
|
372
|
+
- `enabled = owners.filter(r => cursorRules.rules.has(r))`;
|
|
373
|
+
- якщо `enabled.length > 0` → reason `правил{о|а} <backtickJoin(enabled, ', ')>` (узгодження числа);
|
|
374
|
+
- інакше якщо `owners.length === 1` → reason `правило \`<x>\` <в disable-rules | відсутнє в rules>`;
|
|
375
|
+
- інакше — підрахунок `disabledCount`; reason `<backtickJoin(owners, '/')> — <усі власники в disable-rules | жоден власник не активний у rules>`.
|
|
376
|
+
9. Реалізувати `checkCursorRuleScripts(reporter, scripts, cursorRules)`:
|
|
377
|
+
- `lintScript = typeof scripts.lint === 'string' ? scripts.lint : ''`;
|
|
378
|
+
- для кожного запису `RULE_SCRIPTS` обчислити `status`, `present`, `inChain`;
|
|
379
|
+
- якщо `status.enabled.length > 0` → `pass` (present) / `fail` (!present) і `continue`;
|
|
380
|
+
- інакше: `fail` якщо `present`; `fail` якщо `inChain`; `pass` якщо `!present && !inChain`.
|
|
381
|
+
10. Реалізувати `export async function check(cwd = process.cwd())`:
|
|
382
|
+
- `reporter = createCheckReporter()`, деструктурувати `{ pass, fail }`;
|
|
383
|
+
- цикл по `['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', '.yarnrc.yml']` → fail/pass;
|
|
384
|
+
- `.yarn/` директорія → fail/pass;
|
|
385
|
+
- `bun.lock` → pass/fail("Відсутній bun.lock — запусти bun i");
|
|
386
|
+
- `bunfig.toml` → pass(з підказкою про Rego) / fail("Відсутній bunfig.toml — створи з [install] linker = \"hoisted\" (bun.mdc)");
|
|
387
|
+
- `cursorRules = await loadNCursorRules(cwd)`;
|
|
388
|
+
- перевірка `package.json` → fail + ранній `return reporter.getExitCode()` якщо відсутній;
|
|
389
|
+
- `pkg = JSON.parse(await readFile(pkgPath, 'utf8'))`;
|
|
390
|
+
- `scripts = pkg.scripts && typeof pkg.scripts === 'object' ? pkg.scripts : {}`;
|
|
391
|
+
- `checkCursorRuleScripts(reporter, scripts, cursorRules)`;
|
|
392
|
+
- `return reporter.getExitCode()`.
|
|
393
|
+
11. Експортувати **тільки** `check`.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# fix.mjs — Capacitor rule entry-point
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Файл `npm/rules/capacitor/fix.mjs` — це уніфікована точка входу для правила `capacitor` у системі правил `@nitra/cursor`. Файл виконує дві ролі одночасно:
|
|
6
|
+
|
|
7
|
+
1. **Library mode** — експортує функцію `run(ctx)`, яку зовнішній CLI-оркестратор (наприклад, `npx @nitra/cursor fix capacitor` або інший composite-агент) імпортує й викликає, передаючи спільний контекст прогону (`ctx`) з кешем обходу файлової системи (`walkCache`) тощо.
|
|
8
|
+
2. **Standalone mode** — якщо файл запущено напряму (`bun npm/rules/capacitor/fix.mjs`), він самостійно піднімає повну CLI-обв'язку (`runRuleCli`), включно з завантаженням конфігурації, whitelist-фільтрами, друком summary та завершенням процесу з відповідним exit-кодом.
|
|
9
|
+
|
|
10
|
+
Сама логіка правила винесена в загальний раннер `runStandardRule`, який поетапно виконує стандартний пайплайн правила:
|
|
11
|
+
|
|
12
|
+
1. **applies** — перевірка, чи правило взагалі релевантне для поточного проєкту.
|
|
13
|
+
2. **JS-concerns** — прогін JS/TS перевірок із підкатки `js/` правила.
|
|
14
|
+
3. **policy** — прогін policy-перевірок із підкатки `policy/` правила (Rego/OPA або інші policy-файли).
|
|
15
|
+
4. **mdc-refs** — валідація посилань у `capacitor.mdc` (правило-документ).
|
|
16
|
+
|
|
17
|
+
Цей файл свідомо тонкий: він не містить доменної логіки правила, а лише підключає правило до інфраструктури `runStandardRule` / `runRuleCli`. Доменна частина живе в каталогах `js/`, `policy/` та у файлі `capacitor.mdc` поряд.
|
|
18
|
+
|
|
19
|
+
## Експорти / API
|
|
20
|
+
|
|
21
|
+
| Експорт | Тип | Призначення |
|
|
22
|
+
| ------- | ---------------------------------------- | ----------------------------------------------------------------------- |
|
|
23
|
+
| `run` | `(ctx?: RuleContext) => Promise<number>` | Запуск правила в library-режимі. Повертає `0` (OK) або `1` (порушення). |
|
|
24
|
+
|
|
25
|
+
Інших іменованих експортів немає. Default-експорту немає.
|
|
26
|
+
|
|
27
|
+
Файл також має **side-effect виконання на верхньому рівні**: блок `if (isRunAsCli(import.meta.url))` запускає CLI, якщо модуль виконується безпосередньо, а не імпортується.
|
|
28
|
+
|
|
29
|
+
## Функції
|
|
30
|
+
|
|
31
|
+
### `run(ctx)`
|
|
32
|
+
|
|
33
|
+
Запускає правило `capacitor` у library-режимі — той самий пайплайн, який виконує standalone `fix.mjs <id>`, але без CLI-обв'язки (без завантаження зовнішнього конфіга, без друку summary, без `process.exit`).
|
|
34
|
+
|
|
35
|
+
- **Сигнатура:** `function run(ctx)`
|
|
36
|
+
- **Параметри:**
|
|
37
|
+
- `ctx` — необов'язковий об'єкт типу `RuleContext` (тип імпортується з `../../scripts/lib/run-standard-rule.mjs`). Призначений для передачі спільного стану між кількома правилами в одному прогоні (наприклад, `walkCache` — кеш обходу файлової системи, щоб не сканувати дерево повторно). Якщо не передано — раннер створить дефолтний контекст усередині.
|
|
38
|
+
- **Повертає:** `Promise<number>` — exit-код правила:
|
|
39
|
+
- `0` — правило пройшло (порушень нема).
|
|
40
|
+
- `1` — є щонайменше одне порушення.
|
|
41
|
+
- **Side effects:**
|
|
42
|
+
- Читання файлів проєкту (через `walkCache` усередині `runStandardRule`).
|
|
43
|
+
- Можливе записування виправлень у файли проєкту (якщо правило має fix-логіку у фазі `js`/`policy`).
|
|
44
|
+
- Друк діагностичних повідомлень у stdout/stderr через раннер.
|
|
45
|
+
- НЕ викликає `process.exit` — це задача CLI-обгортки.
|
|
46
|
+
- **Реалізація:** делегує виклик до `runStandardRule(import.meta.dirname, ctx)`, передаючи абсолютний шлях до директорії правила (`npm/rules/capacitor/`), щоб раннер міг автоматично знайти підкаталоги `js/`, `policy/`, файл `capacitor.mdc` та `meta.json`.
|
|
47
|
+
|
|
48
|
+
### Top-level CLI-блок
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
if (isRunAsCli(import.meta.url)) {
|
|
52
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- **Призначення:** якщо файл запущено напряму через `bun` / `node` (а не імпортовано з іншого модуля), піднімає повну CLI-обв'язку.
|
|
57
|
+
- **Як визначається CLI-запуск:** утиліта `isRunAsCli(import.meta.url)` зіставляє URL поточного модуля з `process.argv[1]` (стандартна ідіома для ESM).
|
|
58
|
+
- **Що робить `runRuleCli`:** завантажує конфіг проєкту (whitelist/ignore-list), запускає правило (той самий пайплайн, що й `run`), формує summary та повертає exit-код.
|
|
59
|
+
- **`process.exit(await ...)`** — обов'язковий для CLI/CI/IDE інтеграції, щоб батьківський процес отримав коректний код виходу. Лінт-правила `n/no-process-exit` та `unicorn/no-process-exit` свідомо відключені одним коментарем — це задокументовано як винятковий standalone entry-point.
|
|
60
|
+
|
|
61
|
+
## Залежності
|
|
62
|
+
|
|
63
|
+
Файл імпортує дві внутрішні утиліти-раннера:
|
|
64
|
+
|
|
65
|
+
| Шлях | Що береться | Роль |
|
|
66
|
+
| ----------------------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
67
|
+
| `../../scripts/lib/run-rule-cli.mjs` | `isRunAsCli`, `runRuleCli` | Детектор CLI-режиму та повна CLI-обв'язка (config, whitelist, summary, exit-code). |
|
|
68
|
+
| `../../scripts/lib/run-standard-rule.mjs` | `runStandardRule` (+ тип `RuleContext` у JSDoc) | Уніфікований пайплайн правила: `applies → JS-concerns → policy → mdc-refs`. |
|
|
69
|
+
|
|
70
|
+
Зовнішніх npm-залежностей немає. Файл використовує лише стандартні ESM-можливості: `import.meta.url`, `import.meta.dirname`.
|
|
71
|
+
|
|
72
|
+
Сусідні артефакти правила, на які спирається `runStandardRule` через `import.meta.dirname`:
|
|
73
|
+
|
|
74
|
+
- `npm/rules/capacitor/capacitor.mdc` — людинозрозумілий опис правила (для фази `mdc-refs`).
|
|
75
|
+
- `npm/rules/capacitor/meta.json` — метадані правила (id, applies-маркери, версія тощо).
|
|
76
|
+
- `npm/rules/capacitor/js/` — підкаталог з JS/TS-перевірками (`check-*.mjs`, fix-логіка).
|
|
77
|
+
- `npm/rules/capacitor/policy/` — підкаталог з policy-файлами правила.
|
|
78
|
+
|
|
79
|
+
## Потік виконання / Використання
|
|
80
|
+
|
|
81
|
+
### Сценарій 1. Library-виклик (з оркестратора)
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
import { run } from '@nitra/cursor/rules/capacitor/fix.mjs'
|
|
85
|
+
|
|
86
|
+
const exitCode = await run({ walkCache })
|
|
87
|
+
// exitCode: 0 → OK, 1 → порушення
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Потік:
|
|
91
|
+
|
|
92
|
+
1. Оркестратор імпортує `run` (CLI-блок НЕ виконується, бо `isRunAsCli` повертає `false`).
|
|
93
|
+
2. Викликає `run(ctx)` зі спільним контекстом.
|
|
94
|
+
3. Усередині `runStandardRule(dirname, ctx)` поетапно виконує `applies → JS-concerns → policy → mdc-refs`.
|
|
95
|
+
4. Повертає `Promise<number>` — оркестратор сам вирішує, що робити з кодом (агрегувати, друкувати, тощо).
|
|
96
|
+
|
|
97
|
+
### Сценарій 2. Standalone-виклик (CLI/CI/IDE)
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
bun npm/rules/capacitor/fix.mjs
|
|
101
|
+
# або еквівалент
|
|
102
|
+
npx @nitra/cursor fix capacitor
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Потік:
|
|
106
|
+
|
|
107
|
+
1. Bun/Node стартує модуль як entry-point.
|
|
108
|
+
2. `isRunAsCli(import.meta.url)` повертає `true`.
|
|
109
|
+
3. Виконується `await runRuleCli(import.meta.dirname)`:
|
|
110
|
+
- завантажується конфіг проєкту;
|
|
111
|
+
- застосовується whitelist/ignore;
|
|
112
|
+
- запускається той самий пайплайн правила;
|
|
113
|
+
- друкується summary;
|
|
114
|
+
- повертається числовий exit-code.
|
|
115
|
+
4. `process.exit(<code>)` завершує процес із цим кодом для CI/IDE.
|
|
116
|
+
|
|
117
|
+
### Інваріанти
|
|
118
|
+
|
|
119
|
+
- **Один файл — дві ролі:** library (`run`) і standalone (top-level CLI-блок). Жоден інший правило-специфічний код у файлі не з'являється — все має йти в `js/`, `policy/`, або в `capacitor.mdc`.
|
|
120
|
+
- **`process.exit` лише в standalone-гілці.** Library-режим повертає число, не вбиває процес.
|
|
121
|
+
- **Контракт ідентичний для всіх правил:** будь-яке інше правило `@nitra/cursor` має такий самий `fix.mjs` (тонкий wrapper над `runStandardRule` + `runRuleCli`) — це домовленість архітектури правил.
|