@nitra/cursor 3.21.1 → 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 +37 -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/js/lint-findings.mjs +110 -0
- package/rules/js-lint/js/lint.mjs +86 -15
- 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/diff-added-lines.mjs +85 -0
- 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,107 @@
|
|
|
1
|
+
# fix.mjs — правило `php`
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Файл `npm/rules/php/fix.mjs` — це точка входу правила `php` у системі `@nitra/cursor`. Він виконує дві ролі одночасно:
|
|
6
|
+
|
|
7
|
+
1. **Library mode** — експортує функцію `run(ctx)`, яку викликає CLI-оркестратор (`cursor.mjs`) під час масового прогону правил. У цьому режимі правило інтегрується у спільний pipeline і використовує загальний `walkCache` та інший контекст оркестратора.
|
|
8
|
+
2. **Standalone mode** — коли файл запускається напряму через `bun rules/php/fix.mjs`, він діє як самостійний CLI-скрипт, що є повним еквівалентом команди `npx @nitra/cursor fix php` (з підтягуванням конфігурації, whitelist і фінальним summary).
|
|
9
|
+
|
|
10
|
+
Логіка правила (applies → JS-concerns → policy → mdc-refs) інкапсульована у спільному оркестраторі `runStandardRule`, тож сам файл `fix.mjs` залишається мінімальним «glue»-шаром і не містить специфічної для PHP логіки безпосередньо — конкретні чекери лежать поряд у директорії правила (`checks/`, `applies.mjs`, `policy.mjs` тощо), а їх виявлення робить `runStandardRule` за конвенцією шляху `import.meta.dirname`.
|
|
11
|
+
|
|
12
|
+
## Експорти / API
|
|
13
|
+
|
|
14
|
+
| Експорт | Тип | Призначення |
|
|
15
|
+
| ------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
16
|
+
| `run` | `function(ctx?: RuleContext): Promise<number>` | Іменований експорт — основний entry-point library-режиму. Запускає стандартний pipeline правила в каталозі поточного модуля. Повертає exit-code (0 — OK, 1 — порушення). |
|
|
17
|
+
|
|
18
|
+
Файл **не** має `default`-експорту. Side-effect на верхньому рівні модуля: блок `if (isRunAsCli(...))` із викликом `process.exit(...)` — спрацьовує лише при прямому запуску.
|
|
19
|
+
|
|
20
|
+
## Функції
|
|
21
|
+
|
|
22
|
+
### `run(ctx)`
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
export function run(ctx)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- **Параметри:**
|
|
29
|
+
- `ctx` _(опціональний)_ — об'єкт типу `RuleContext`, імпортований з `../../scripts/lib/run-standard-rule.mjs`. Передається оркестратором і містить кеш обходу файлової системи (`walkCache`) та інші розшаровані між правилами дані. Якщо не передано — `runStandardRule` створює власний локальний контекст.
|
|
30
|
+
- **Повертає:** `Promise<number>` — exit-code прогону:
|
|
31
|
+
- `0` — правило пройшло без порушень;
|
|
32
|
+
- `1` — виявлено порушення (або правило завершилось помилкою, яку оркестратор мапить на ненульовий код).
|
|
33
|
+
- **Side effects:**
|
|
34
|
+
- Делегує всю реальну роботу `runStandardRule`: читання файлів, виконання чекерів, друк summary до `stdout`/`stderr`.
|
|
35
|
+
- Сам `run` не має власних побічних ефектів — це тонкий wrapper.
|
|
36
|
+
- **Як визначається корінь правила:** через `import.meta.dirname` — абсолютний шлях до директорії, в якій лежить `fix.mjs`. Це дозволяє `runStandardRule` знайти сусідні файли правила (`applies.mjs`, `policy.mjs`, `checks/*.mjs`, `*.mdc` тощо) без додаткових параметрів.
|
|
37
|
+
|
|
38
|
+
## Залежності
|
|
39
|
+
|
|
40
|
+
Файл імпортує дві утиліти з внутрішньої бібліотеки `npm/scripts/lib/`:
|
|
41
|
+
|
|
42
|
+
| Імпорт | Звідки | Призначення |
|
|
43
|
+
| ----------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
44
|
+
| `isRunAsCli` | `../../scripts/lib/run-rule-cli.mjs` | Детектор «чи модуль запущений напряму як CLI». Порівнює `import.meta.url` із `process.argv[1]`. Використовується для гілки standalone. |
|
|
45
|
+
| `runRuleCli` | `../../scripts/lib/run-rule-cli.mjs` | Повноцінний CLI-runner правила: парсить аргументи, підтягує config, застосовує whitelist, друкує summary. Повертає `Promise<number>` із exit-кодом. |
|
|
46
|
+
| `runStandardRule` | `../../scripts/lib/run-standard-rule.mjs` | Стандартний pipeline правила: послідовно виконує стадії `applies → JS-concerns → policy → mdc-refs`. Приймає шлях до директорії правила та опційний `RuleContext`. |
|
|
47
|
+
|
|
48
|
+
Зовнішніх npm-залежностей файл не має — лише внутрішні модулі монорепо.
|
|
49
|
+
|
|
50
|
+
## Потік виконання / Використання
|
|
51
|
+
|
|
52
|
+
### Сценарій A — виклик з оркестратора (library mode)
|
|
53
|
+
|
|
54
|
+
1. CLI `@nitra/cursor fix` (або агрегатор) знаходить правило `php` за його директорією.
|
|
55
|
+
2. Динамічно імпортує `fix.mjs` і викликає `await run(ctx)`, передаючи спільний `RuleContext` (зокрема `walkCache`, щоб уникнути повторного обходу ФС між правилами).
|
|
56
|
+
3. `run` делегує виклик `runStandardRule(import.meta.dirname, ctx)`.
|
|
57
|
+
4. `runStandardRule` повертає exit-code; оркестратор агрегує коди всіх правил.
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
import { run } from '@nitra/cursor/rules/php/fix.mjs'
|
|
61
|
+
const code = await run(sharedCtx)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Сценарій B — прямий запуск файлу (standalone mode)
|
|
65
|
+
|
|
66
|
+
1. Користувач (або IDE/CI) виконує:
|
|
67
|
+
```bash
|
|
68
|
+
bun npm/rules/php/fix.mjs
|
|
69
|
+
```
|
|
70
|
+
2. Top-level `if (isRunAsCli(import.meta.url))` повертає `true` (бо `import.meta.url` збігається з шляхом запущеного скрипта).
|
|
71
|
+
3. Викликається `await runRuleCli(import.meta.dirname)` — повноцінний CLI-режим: підвантажує конфігурацію, застосовує whitelist, друкує підсумок.
|
|
72
|
+
4. `process.exit(...)` завершує процес із отриманим exit-кодом, аби CI/IDE могли коректно інтерпретувати результат.
|
|
73
|
+
|
|
74
|
+
### Семантика exit-кодів
|
|
75
|
+
|
|
76
|
+
- `0` — правило застосовне та порушень не виявлено (або правило незастосовне для цього проєкту — `applies` повернув `false`).
|
|
77
|
+
- `1` — є порушення, які треба виправити.
|
|
78
|
+
|
|
79
|
+
### Коментарі та лінт-винятки
|
|
80
|
+
|
|
81
|
+
У standalone-гілці явно вимкнено два правила лінтера:
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
// eslint-disable-next-line n/no-process-exit, unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
85
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Це свідомий виняток: `process.exit` тут потрібний, щоб CLI/CI отримали ненульовий код у разі порушень — без нього процес міг би завершитись із кодом `0` навіть за наявності знайдених проблем.
|
|
89
|
+
|
|
90
|
+
## Архітектурний контекст
|
|
91
|
+
|
|
92
|
+
- **Конвенція файлу `fix.mjs`** — у директорії кожного правила (`npm/rules/<id>/fix.mjs`) лежить однотипний тонкий wrapper із двома ролями (library + standalone). Це гарантує:
|
|
93
|
+
- єдиний інтерфейс для оркестратора (`run(ctx)`);
|
|
94
|
+
- можливість запускати окреме правило ізольовано (для дебагу/CI per-rule).
|
|
95
|
+
- **Чому через `import.meta.dirname`** — `runStandardRule` за директорією правила автоматично знаходить усі сусідні артефакти (`applies.mjs`, `policy.mjs`, `checks/*.mjs`, `*.mdc`). Це уникає дублювання id-правила як рядка.
|
|
96
|
+
- **Patch-free wrapper** — у самому `fix.mjs` не повинно з'являтися PHP-специфічної логіки; будь-які зміни поведінки правила робляться у сусідніх файлах правила, а не в цьому entry-point'і.
|
|
97
|
+
|
|
98
|
+
## Rebuild Test
|
|
99
|
+
|
|
100
|
+
Файл можна повністю відтворити з опису вище за такими інваріантами:
|
|
101
|
+
|
|
102
|
+
1. Два імпорти: `{ isRunAsCli, runRuleCli }` з `../../scripts/lib/run-rule-cli.mjs` і `{ runStandardRule }` з `../../scripts/lib/run-standard-rule.mjs`.
|
|
103
|
+
2. Іменований експорт `function run(ctx)` — однорядкове тіло `return runStandardRule(import.meta.dirname, ctx)`.
|
|
104
|
+
3. JSDoc над `run`: опис стадій (applies → JS-concerns → policy → mdc-refs), згадка library mode, тип параметра `ctx` через `import('...').RuleContext`, тип повернення `Promise<number>` із семантикою 0/1.
|
|
105
|
+
4. Блок `if (isRunAsCli(import.meta.url)) { ... }` із викликом `process.exit(await runRuleCli(import.meta.dirname))` і коментарем-поясненням про дві ролі fix.mjs.
|
|
106
|
+
5. Лінт-disable перед `process.exit`: `n/no-process-exit, unicorn/no-process-exit` із обґрунтуванням «standalone entry-point має повертати exit-code для CI/IDE».
|
|
107
|
+
6. Жодних інших top-level операцій, жодного `default`-експорту.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# tooling.mjs — перевірка вимог правила `php.mdc`
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `tooling.mjs` реалізує локальну перевірку відповідності PHP-проєкту вимогам внутрішнього правила `php.mdc`. Це частина системи правил Cursor: під кожне правило існує парний `check`-модуль, що повертає exit-code, придатний для запуску в CI або з CLI.
|
|
6
|
+
|
|
7
|
+
Цей конкретний модуль покриває **лише ту частину вимог, яку неможливо (або недоречно) перевірити через Rego/conftest** — а саме факт існування файлів у файловій системі:
|
|
8
|
+
|
|
9
|
+
- наявність `composer.json` у корені репозиторію;
|
|
10
|
+
- наявність `package.json` у корені (його структуру, зокрема скрипт `lint-php`, перевіряє вже Rego-політика `npm/policy/php/package_json/`);
|
|
11
|
+
- наявність файлу GitHub Actions workflow `.github/workflows/lint-php.yml` (його вміст, зокрема крок `run: bun run lint-php`, перевіряє Rego-політика `npm/policy/php/lint_php_yml/`).
|
|
12
|
+
|
|
13
|
+
Тобто `tooling.mjs` — це **FS-existence чек**. Структурний аналіз JSON/YAML делегований на `npx @nitra/cursor check` (Rego). Така декомпозиція явно прописана в JSDoc-заголовку файлу.
|
|
14
|
+
|
|
15
|
+
Модуль є **ESM** (`.mjs`), не має побічних ефектів на рівні імпорту: уся робота виконується всередині експортованої функції `check()`. Робоча директорія — поточна (`process.cwd()`), оскільки всі шляхи передаються в `existsSync` як відносні.
|
|
16
|
+
|
|
17
|
+
## Експорти / API
|
|
18
|
+
|
|
19
|
+
| Експорт | Тип | Призначення |
|
|
20
|
+
| --------- | ---------- | ------------------------------------------------------------------ |
|
|
21
|
+
| `check()` | `function` | Запускає перевірку правила `php.mdc` у поточній робочій директорії |
|
|
22
|
+
|
|
23
|
+
Інших експортів немає (default-експорту немає). Імпорт здійснюється як іменований:
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
import { check } from './tooling.mjs'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Функції
|
|
30
|
+
|
|
31
|
+
### `check()`
|
|
32
|
+
|
|
33
|
+
**Сигнатура:**
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
export function check(): number
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Параметри:** немає.
|
|
40
|
+
|
|
41
|
+
**Повертає:** `number` — exit-code:
|
|
42
|
+
|
|
43
|
+
- `0` — усі перевірки пройшли (`composer.json`, `package.json` та `.github/workflows/lint-php.yml` існують у поточній директорії);
|
|
44
|
+
- `1` — хоча б одна перевірка провалилася (`fail` був викликаний принаймні один раз).
|
|
45
|
+
|
|
46
|
+
Конкретне значення exit-code формує об’єкт-репортер через `reporter.getExitCode()` — модуль `tooling.mjs` не визначає логіку підрахунку самостійно, а делегує її в `createCheckReporter()`.
|
|
47
|
+
|
|
48
|
+
**Алгоритм (по кроках):**
|
|
49
|
+
|
|
50
|
+
1. Створює локальний репортер: `const reporter = createCheckReporter()`.
|
|
51
|
+
2. Розпаковує методи `pass` і `fail` з репортера: `const { pass, fail } = reporter`.
|
|
52
|
+
3. Перевіряє наявність `composer.json` у CWD:
|
|
53
|
+
- якщо файл існує → `pass('composer.json існує')`;
|
|
54
|
+
- інакше → `fail('composer.json не знайдено в корені — додай (php.mdc)')`.
|
|
55
|
+
4. Перевіряє наявність `package.json` у CWD:
|
|
56
|
+
- якщо файл існує → `pass('package.json є (наявність lint-php перевіряє npx @nitra/cursor fix → php.package_json)')`;
|
|
57
|
+
- інакше → `fail('package.json не знайдено в корені — додай (php.mdc)')`.
|
|
58
|
+
5. Перевіряє наявність workflow-файлу `.github/workflows/lint-php.yml`:
|
|
59
|
+
- якщо файл існує → `pass(`${wfPath} є (структуру перевіряє npx @nitra/cursor fix → php.lint_php_yml)`)`;
|
|
60
|
+
- інакше → `fail(`${wfPath} не існує — створи згідно php.mdc`)`.
|
|
61
|
+
6. Повертає `reporter.getExitCode()`.
|
|
62
|
+
|
|
63
|
+
**Side effects:**
|
|
64
|
+
|
|
65
|
+
- **Файлова система:** виключно **читання** (`existsSync`). Жодних записів/створень файлів.
|
|
66
|
+
- **Stdout/stderr:** опосередковано через `pass`/`fail` репортера. Сам модуль не звертається напряму до `console.log` / `process.stdout`. Конкретний канал, формат і обсяг виводу визначає реалізація `createCheckReporter()`.
|
|
67
|
+
- **Process exit:** функція **не викликає** `process.exit()` самостійно — вона лише повертає число. Виклик `process.exit(check())` (або еквівалент) — відповідальність викликаючої сторони.
|
|
68
|
+
- **Глобальний стан:** не змінюється.
|
|
69
|
+
- **CWD-залежність:** усі шляхи відносні, тому результат залежить від поточної робочої директорії на момент виклику.
|
|
70
|
+
|
|
71
|
+
**Помилки/винятки:** функція не використовує `try/catch`. `existsSync` синхронна та не кидає виняток для відсутніх файлів — повертає `false`. Винятки можуть прилетіти лише з імпортованих залежностей (`createCheckReporter`), якщо ті змінять контракт; цей модуль їх не обробляє.
|
|
72
|
+
|
|
73
|
+
**Ідемпотентність:** так, виклик не змінює стан системи; повторні виклики дадуть той самий результат за тих самих файлів у CWD.
|
|
74
|
+
|
|
75
|
+
## Залежності
|
|
76
|
+
|
|
77
|
+
### Зовнішні (Node.js builtin)
|
|
78
|
+
|
|
79
|
+
- **`node:fs`** → іменований імпорт `existsSync`. Використовується для синхронної перевірки наявності файлу/директорії за вказаним шляхом. Імпортується саме через префікс `node:` (явна вказівка на builtin-модуль).
|
|
80
|
+
|
|
81
|
+
### Внутрішні (відносні)
|
|
82
|
+
|
|
83
|
+
- **`../../../scripts/lib/check-reporter.mjs`** → іменований імпорт `createCheckReporter`. Фабрика репортера для check-модулів. Очікуваний контракт (виведений із використання в цьому файлі):
|
|
84
|
+
- повертає об’єкт із методами `pass(message: string): void` та `fail(message: string): void`;
|
|
85
|
+
- має метод `getExitCode(): number`, який повертає `0` за відсутності провалів і `1` (або інше ненульове) за наявності хоча б одного `fail`.
|
|
86
|
+
|
|
87
|
+
Шлях `../../../scripts/lib/check-reporter.mjs` відраховується від розташування файлу `npm/rules/php/js/tooling.mjs` й веде до `npm/scripts/lib/check-reporter.mjs`.
|
|
88
|
+
|
|
89
|
+
### Зв’язки з правилами та політиками (контекст екосистеми)
|
|
90
|
+
|
|
91
|
+
Модуль явно посилається на сусідні артефакти, які виконують споріднену перевірку (вони **не** імпортуються кодом, але є частиною повного контракту правила `php.mdc`):
|
|
92
|
+
|
|
93
|
+
- `npm/policy/php/package_json/` — Rego-політика, що перевіряє наявність скрипта `lint-php` у `package.json`;
|
|
94
|
+
- `npm/policy/php/lint_php_yml/` — Rego-політика, що перевіряє крок `run: bun run lint-php` у workflow `lint-php.yml`;
|
|
95
|
+
- запускається разом із цим модулем через CLI `npx @nitra/cursor check`.
|
|
96
|
+
|
|
97
|
+
## Потік виконання / Використання
|
|
98
|
+
|
|
99
|
+
### Очікуваний контекст запуску
|
|
100
|
+
|
|
101
|
+
Файл є **check-модулем правила `php.mdc`** і інтегрується в систему `@nitra/cursor`. Виклик відбувається з кореня PHP-проєкту, де очікуються:
|
|
102
|
+
|
|
103
|
+
- `composer.json` у корені;
|
|
104
|
+
- `package.json` у корені (зі скриптом `lint-php`);
|
|
105
|
+
- `.github/workflows/lint-php.yml` із кроком, який виконує `bun run lint-php`.
|
|
106
|
+
|
|
107
|
+
### Типовий програмний виклик
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
import { check } from './npm/rules/php/js/tooling.mjs'
|
|
111
|
+
|
|
112
|
+
const code = check()
|
|
113
|
+
process.exit(code)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Сценарій 1 — усе на місці
|
|
117
|
+
|
|
118
|
+
CWD містить `composer.json`, `package.json` і `.github/workflows/lint-php.yml`. Репортер отримує три `pass`-повідомлення, жодного `fail`. `check()` повертає `0`.
|
|
119
|
+
|
|
120
|
+
### Сценарій 2 — немає одного з файлів
|
|
121
|
+
|
|
122
|
+
Наприклад, відсутній `.github/workflows/lint-php.yml`. Репортер отримує два `pass` і один `fail` із вказівкою «`.github/workflows/lint-php.yml` не існує — створи згідно php.mdc». `check()` повертає `1`.
|
|
123
|
+
|
|
124
|
+
### Сценарій 3 — порожня директорія
|
|
125
|
+
|
|
126
|
+
Жоден із трьох файлів не існує. Репортер отримує три `fail` (із підказками щодо `php.mdc`), `check()` повертає `1`. Усі три перевірки виконуються незалежно — функція не виходить на першому ж провалі, тому користувач бачить **повний** список проблем за один прогін.
|
|
127
|
+
|
|
128
|
+
### Обмеження
|
|
129
|
+
|
|
130
|
+
- Перевіряється **лише існування** файлів — не їхній уміст. Аналіз структури `package.json` / `lint-php.yml` належить Rego-політикам, які запускаються окремо (`npx @nitra/cursor check`).
|
|
131
|
+
- Не перевіряється, що `composer.json` — валідний JSON або містить обов’язкові поля.
|
|
132
|
+
- Не перевіряється наявність директорії `vendor/`, версії PHP чи що-небудь, пов’язане з виконанням Composer.
|
|
133
|
+
- Усі шляхи відносні до `process.cwd()`. Виклик з іншої директорії дасть інший результат.
|
|
134
|
+
|
|
135
|
+
### Місце в архітектурі правил
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
npm/
|
|
139
|
+
├── rules/
|
|
140
|
+
│ └── php/
|
|
141
|
+
│ └── js/
|
|
142
|
+
│ └── tooling.mjs ← цей файл (FS-existence checks)
|
|
143
|
+
├── policy/
|
|
144
|
+
│ └── php/
|
|
145
|
+
│ ├── package_json/ ← Rego: lint-php у package.json
|
|
146
|
+
│ └── lint_php_yml/ ← Rego: крок bun run lint-php у workflow
|
|
147
|
+
└── scripts/
|
|
148
|
+
└── lib/
|
|
149
|
+
└── check-reporter.mjs ← фабрика репортера (createCheckReporter)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Така структура відображає принцип розділення: JS-модулі правил роблять рівно те, що **не покривається** Rego/conftest (зазвичай — операції з файловою системою, виклики зовнішніх CLI, динамічні перевірки), а декларативні перевірки виносяться в Rego-політики.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# lint.mjs — PHP lint runner
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `lint.mjs` реалізує крок `lint-php` згідно з правилом `php.mdc`. Він виконує статичний та безпековий аналіз PHP-проєкту, який лежить у поточному робочому каталозі (`process.cwd()`), послідовно прогоняючи такі інструменти:
|
|
6
|
+
|
|
7
|
+
1. `composer audit --no-interaction` — обовʼязковий аудит залежностей через Composer.
|
|
8
|
+
2. `vendor/bin/php-cs-fixer fix --dry-run --diff` — перевірка стилю без модифікації файлів.
|
|
9
|
+
3. `vendor/bin/phpcs --standard=Security ...` — перевірка зі стандартом Security для типових директорій коду.
|
|
10
|
+
4. `vendor/bin/phpstan analyse --no-progress` — статичний аналіз.
|
|
11
|
+
5. `vendor/bin/psalm --no-cache` — додатковий статичний аналіз.
|
|
12
|
+
|
|
13
|
+
Ключові властивості модуля:
|
|
14
|
+
|
|
15
|
+
- Якщо в корені репозиторію **немає** `composer.json` — скрипт нічого не запускає й завершується успіхом (`pass` + код виходу 0).
|
|
16
|
+
- Якщо `composer.json` є, але `composer` недоступний у `PATH` — це фатальна помилка (`fail`).
|
|
17
|
+
- Інструменти з `vendor/bin/*` запускаються лише за наявності відповідного бінарника; відсутній інструмент **не** є помилкою, лише пропускається з відповідним `pass`-повідомленням.
|
|
18
|
+
- Помилка будь-якого запущеного інструмента (ненульовий exit code) призводить до негайного завершення з кодом помилки — наступні кроки **не** виконуються (fail-fast).
|
|
19
|
+
- Результат акумулюється через `createCheckReporter()`, а підсумковий код виходу дає `reporter.getExitCode()`.
|
|
20
|
+
|
|
21
|
+
Файл одночасно є модулем (з експортами для повторного використання й тестування) та CLI-точкою входу (через `isRunAsCli(import.meta.url)`).
|
|
22
|
+
|
|
23
|
+
## Експорти / API
|
|
24
|
+
|
|
25
|
+
Модуль експортує дві функції:
|
|
26
|
+
|
|
27
|
+
- `getPhpcsCodePaths(root: string): string[]` — обчислює список директорій для PHPCS.
|
|
28
|
+
- `run(): number` — основна точка входу; повертає код виходу (0 — OK, 1 — є помилки).
|
|
29
|
+
|
|
30
|
+
Функції `vendorBin`, `runTool` і вкладена `runOptionalVendorTool` є приватними (не експортуються) і використовуються тільки всередині модуля.
|
|
31
|
+
|
|
32
|
+
Коли модуль виконується безпосередньо як CLI (через `node` чи `bun`), у блоці `if (isRunAsCli(import.meta.url))` викликається `run()`, а результат присвоюється `process.exitCode`.
|
|
33
|
+
|
|
34
|
+
## Функції
|
|
35
|
+
|
|
36
|
+
### `getPhpcsCodePaths(root)`
|
|
37
|
+
|
|
38
|
+
Сигнатура: `getPhpcsCodePaths(root: string): string[]`
|
|
39
|
+
|
|
40
|
+
Параметри:
|
|
41
|
+
|
|
42
|
+
- `root` — абсолютний шлях до кореня репозиторію.
|
|
43
|
+
|
|
44
|
+
Повертає: масив відносних шляхів (рядків) до директорій, які варто передати у `phpcs`.
|
|
45
|
+
|
|
46
|
+
Алгоритм:
|
|
47
|
+
|
|
48
|
+
1. Перебирає константу `PHPCS_CODE_DIR_CANDIDATES = ['app', 'src', 'lib', 'public', 'www']`.
|
|
49
|
+
2. Для кожного імені `d` будує абсолютний шлях `join(root, d)` і перевіряє, що шлях існує **і** є директорією (`existsSync` + `statSync(...).isDirectory()`).
|
|
50
|
+
3. Якщо знайдено хоча б одну директорію — повертає масив усіх знайдених імен (як **відносні** імена, не абсолютні шляхи).
|
|
51
|
+
4. Якщо жодної не знайдено — повертає `['.']` (тобто PHPCS буде запущений по всьому кореню).
|
|
52
|
+
|
|
53
|
+
Side effects: лише читання файлової системи через `existsSync`/`statSync`. Файлів не модифікує.
|
|
54
|
+
|
|
55
|
+
### `vendorBin(root, name)` (private)
|
|
56
|
+
|
|
57
|
+
Сигнатура: `vendorBin(root: string, name: string): string | null`
|
|
58
|
+
|
|
59
|
+
Параметри:
|
|
60
|
+
|
|
61
|
+
- `root` — корінь репозиторію.
|
|
62
|
+
- `name` — імʼя файла у `vendor/bin` (наприклад, `phpstan`).
|
|
63
|
+
|
|
64
|
+
Повертає: абсолютний шлях `<root>/vendor/bin/<name>`, якщо файл існує; інакше `null`.
|
|
65
|
+
|
|
66
|
+
Side effects: `existsSync` (лише читання).
|
|
67
|
+
|
|
68
|
+
### `runTool(label, abs, args, pass, fail)` (private)
|
|
69
|
+
|
|
70
|
+
Сигнатура: `runTool(label: string, abs: string, args: string[], pass: (msg: string) => void, fail: (msg: string) => void): boolean`
|
|
71
|
+
|
|
72
|
+
Параметри:
|
|
73
|
+
|
|
74
|
+
- `label` — людиночитна назва кроку (наприклад, `"PHPStan"`), використовується в повідомленнях.
|
|
75
|
+
- `abs` — абсолютний шлях до CLI-бінарника.
|
|
76
|
+
- `args` — аргументи командного рядка.
|
|
77
|
+
- `pass` — callback для запису успіху в репортер.
|
|
78
|
+
- `fail` — callback для запису помилки в репортер.
|
|
79
|
+
|
|
80
|
+
Повертає: `true`, якщо процес завершився з кодом 0; `false` — інакше.
|
|
81
|
+
|
|
82
|
+
Алгоритм:
|
|
83
|
+
|
|
84
|
+
1. Запускає `spawnSync(abs, args, { stdio: 'inherit', shell: false })`. `stdio: 'inherit'` означає, що stdout/stderr дитячого процесу пробрасуються користувачу напряму.
|
|
85
|
+
2. Якщо `r.status === 0` — викликає `pass(\`lint-php: ${label} — OK\`)`і повертає`true`.
|
|
86
|
+
3. Інакше визначає код: якщо `r.status` — число, то воно; інакше `1` (захист від `null`, який трапляється, наприклад, при сигналі або провалі запуску).
|
|
87
|
+
4. Викликає `fail(\`lint-php: ${label} — помилка (код ${code}, php.mdc)\`)`і повертає`false`.
|
|
88
|
+
|
|
89
|
+
Side effects: запускає дочірній процес, успадковує stdio батьківського процесу.
|
|
90
|
+
|
|
91
|
+
### `run()`
|
|
92
|
+
|
|
93
|
+
Сигнатура: `run(): number`
|
|
94
|
+
|
|
95
|
+
Параметри: немає.
|
|
96
|
+
|
|
97
|
+
Повертає: код виходу — `0` (успіх) або `1` (хоча б одна помилка). Реальний код визначає `reporter.getExitCode()`.
|
|
98
|
+
|
|
99
|
+
Алгоритм (по кроках):
|
|
100
|
+
|
|
101
|
+
1. Створює репортер: `const reporter = createCheckReporter()`; деструктуризує `pass` і `fail` з нього.
|
|
102
|
+
2. Бере поточний робочий каталог: `const root = process.cwd()`.
|
|
103
|
+
3. Якщо `composer.json` у корені **відсутній** — викликає `pass('lint-php: немає composer.json у корені — кроки PHP пропущено')` і повертає `reporter.getExitCode()` (фактично 0).
|
|
104
|
+
4. Резолвить бінарник `composer` через `resolveCmd('composer')`. Якщо не знайдено — викликає `fail` з повідомленням про відсутність у `PATH` і виходить.
|
|
105
|
+
5. Запускає `composer audit --no-interaction` через `runTool`. Якщо крок провалився — негайно повертає поточний код виходу (далі нічого не виконується).
|
|
106
|
+
6. Оголошує вкладену функцію `runOptionalVendorTool(binName, label, args): boolean` (див. нижче).
|
|
107
|
+
7. Послідовно викликає `runOptionalVendorTool` для:
|
|
108
|
+
- `php-cs-fixer` → label `"PHP-CS-Fixer (dry-run)"`, args `['fix', '--dry-run', '--diff']`.
|
|
109
|
+
- `phpcs` → label `"phpcs (Security)"`, args `['--standard=Security', '--ignore=*/vendor/*,*/node_modules/*,*/.git/*', ...getPhpcsCodePaths(root)]`.
|
|
110
|
+
- `phpstan` → label `"PHPStan"`, args `['analyse', '--no-progress']`.
|
|
111
|
+
- `psalm` → label `"Psalm"`, args `['--no-cache']`.
|
|
112
|
+
8. Після кожного кроку, якщо він повернув `false` (тобто `runTool` зафейлився; пропуск через відсутність бінарника `false` **не** дає), функція негайно повертає `reporter.getExitCode()`.
|
|
113
|
+
9. Якщо всі кроки успішні — повертає `reporter.getExitCode()`.
|
|
114
|
+
|
|
115
|
+
Порядок кроків зафіксований: PHP-CS-Fixer → PHPCS → PHPStan → Psalm. Перший провал зупиняє конвеєр.
|
|
116
|
+
|
|
117
|
+
Side effects:
|
|
118
|
+
|
|
119
|
+
- Читає `process.cwd()` і файли в ньому.
|
|
120
|
+
- Резолвить `composer` через `PATH`.
|
|
121
|
+
- Спавнить дочірні процеси з успадкованим stdio.
|
|
122
|
+
- Не модифікує жодних файлів (PHP-CS-Fixer запускається в `--dry-run --diff`).
|
|
123
|
+
- Підсумково повертає число; не викликає `process.exit` самостійно (це робить CLI-обгортка через `process.exitCode`).
|
|
124
|
+
|
|
125
|
+
### `runOptionalVendorTool(binName, label, args)` (вкладена в `run`)
|
|
126
|
+
|
|
127
|
+
Сигнатура: `runOptionalVendorTool(binName: string, label: string, args: string[]): boolean`
|
|
128
|
+
|
|
129
|
+
Параметри:
|
|
130
|
+
|
|
131
|
+
- `binName` — імʼя файла у `vendor/bin`.
|
|
132
|
+
- `label` — назва кроку для повідомлень.
|
|
133
|
+
- `args` — аргументи CLI.
|
|
134
|
+
|
|
135
|
+
Повертає: `true`, якщо крок успішний **або** пропущений (бінарника немає); `false`, якщо крок виконано й він зафейлився.
|
|
136
|
+
|
|
137
|
+
Алгоритм:
|
|
138
|
+
|
|
139
|
+
1. Резолвить абсолютний шлях через `vendorBin(root, binName)`.
|
|
140
|
+
2. Якщо `null` — викликає `pass(\`lint-php: vendor/bin/${binName} — відсутній, крок пропущено\`)`і повертає`true`.
|
|
141
|
+
3. Інакше делегує `runTool(label, abs, args, pass, fail)` і повертає його результат.
|
|
142
|
+
|
|
143
|
+
Side effects: ті самі, що в `vendorBin` + `runTool` для виконуваного кроку.
|
|
144
|
+
|
|
145
|
+
## Залежності
|
|
146
|
+
|
|
147
|
+
### Стандартна бібліотека Node.js
|
|
148
|
+
|
|
149
|
+
- `node:child_process` → `spawnSync` — синхронний запуск дочірніх процесів.
|
|
150
|
+
- `node:fs` → `existsSync`, `statSync` — перевірка існування файлів і визначення, чи це директорія.
|
|
151
|
+
- `node:path` → `join`, `resolve` — побудова шляхів (`join` — для відносних, `resolve` — для абсолютних).
|
|
152
|
+
|
|
153
|
+
### Внутрішні модулі репозиторію (відносні шляхи)
|
|
154
|
+
|
|
155
|
+
- `../../../scripts/cli-entry.mjs` → `isRunAsCli` — детектор того, що поточний модуль запущений як CLI (порівнює `import.meta.url` зі стартовим файлом).
|
|
156
|
+
- `../../../scripts/lib/check-reporter.mjs` → `createCheckReporter` — фабрика репортера з полями `pass`, `fail`, `getExitCode()`. Усі повідомлення формату `lint-php: <label> — ...` йдуть саме через нього.
|
|
157
|
+
- `../../../scripts/utils/resolve-cmd.mjs` → `resolveCmd` — пошук бінарника в `PATH` (повертає шлях або `null`/`undefined`).
|
|
158
|
+
|
|
159
|
+
### Зовнішні CLI-залежності (виконувані файли)
|
|
160
|
+
|
|
161
|
+
- `composer` — має бути у `PATH`, якщо в проєкті є `composer.json`.
|
|
162
|
+
- `vendor/bin/php-cs-fixer`, `vendor/bin/phpcs`, `vendor/bin/phpstan`, `vendor/bin/psalm` — опційні; відсутній бінарник пропускає відповідний крок.
|
|
163
|
+
|
|
164
|
+
### Правило / контекст
|
|
165
|
+
|
|
166
|
+
- `php.mdc` — правило з `.cursor/rules/`, на яке посилається модуль (тексти повідомлень містять `php.mdc`).
|
|
167
|
+
|
|
168
|
+
## Потік виконання / Використання
|
|
169
|
+
|
|
170
|
+
### Запуск як CLI
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
node npm/rules/php/lint/lint.mjs
|
|
174
|
+
# або через bun
|
|
175
|
+
bun npm/rules/php/lint/lint.mjs
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
При CLI-запуску виконується блок:
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
if (isRunAsCli(import.meta.url)) {
|
|
182
|
+
process.exitCode = run()
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Це означає, що процес завершиться з кодом, що повернула `run()` (0 або 1). Викид `throw` не очікується — усі помилки інструментів повідомляються через `fail`, а не через виключення.
|
|
187
|
+
|
|
188
|
+
### Імпорт як модуля
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
import { run, getPhpcsCodePaths } from './lint.mjs'
|
|
192
|
+
|
|
193
|
+
const exitCode = run()
|
|
194
|
+
if (exitCode !== 0) {
|
|
195
|
+
// обробка помилки
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`getPhpcsCodePaths` корисна для тестування або для повторного використання логіки вибору директорій.
|
|
200
|
+
|
|
201
|
+
### Сценарії
|
|
202
|
+
|
|
203
|
+
1. **Немає `composer.json`** — повертає 0, нічого не запускає, пише в репортер `pass`-повідомлення.
|
|
204
|
+
2. **Є `composer.json`, але немає `composer`** — повертає 1, у репортері є `fail`.
|
|
205
|
+
3. **Є `composer.json` і `composer`, немає жодного `vendor/bin/*`** — запускається лише `composer audit`; якщо він пройде, повертає 0, інші кроки відмічаються як пропущені.
|
|
206
|
+
4. **Усі інструменти встановлені** — послідовний прогін: `composer audit` → `php-cs-fixer` → `phpcs` (за директоріями з `getPhpcsCodePaths`) → `phpstan` → `psalm`. Будь-який ненульовий exit-code дочірнього процесу перериває ланцюг і повертає 1.
|
|
207
|
+
5. **`composer.json` є, директорій `app`/`src`/`lib`/`public`/`www` немає** — PHPCS отримує єдиний шлях `.`.
|
|
208
|
+
|
|
209
|
+
### Особливості
|
|
210
|
+
|
|
211
|
+
- `spawnSync` запускається з `shell: false` — параметри передаються як масив, shell-інʼєкції неможливі.
|
|
212
|
+
- `stdio: 'inherit'` — повний вивід інструментів іде в термінал користувача; репортер додає лише підсумкові `pass/fail`-рядки.
|
|
213
|
+
- Fail-fast: модуль не агрегує помилки кількох інструментів; на першій помилці виходить.
|
|
214
|
+
- Усі повідомлення українською, із префіксом `lint-php:` для легкої грепабельності.
|
|
215
|
+
- Функція `run` не приймає аргументів — корінь визначається через `process.cwd()`, що дозволяє запускати її з будь-якого workspace без передавання шляху.
|