@nitra/cursor 3.22.0 → 3.23.1
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/AGENTS.template.md +4 -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 +4 -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,378 @@
|
|
|
1
|
+
# template.mjs — перевірка nginx-шаблону (правило `nginx-default-tpl`)
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль реалізує JS-частину правила `nginx-default-tpl.mdc`. Він шукає у проєкті файли `default.conf.template` (типовий nginx-шаблон, який рендериться `envsubst` під час старту контейнера) і пов'язані з ними артефакти, після чого:
|
|
6
|
+
|
|
7
|
+
1. виконує міграції старих/невалідних артефактів у канонічний вигляд:
|
|
8
|
+
- перейменовує застаріле `default.tpl.conf` у `default.conf.template` (або перезаписує контентом, якщо ціль уже є, після чого видаляє джерело);
|
|
9
|
+
- замінює невалідну директиву `error_log off;` на канонічну `error_log /dev/null crit;`;
|
|
10
|
+
2. перевіряє вміст кожного `default.conf.template` на наявність обов'язкових директив (порт `8080`, `/healthz`, `gzip_static`, `try_files`, `server_tokens off`, `sendfile_max_chunk` тощо) і **відсутність** будь-яких `*_pass` директив (`proxy_pass`, `fastcgi_pass`, `grpc_pass`, `uwsgi_pass` тощо — бекенд-логіка має бути винесена в HTTPRoute на рівні k8s);
|
|
11
|
+
3. перевіряє, що поруч із шаблоном є щонайменше один `*.ini` (values для середовища), а кожен ключ з ini використовується в шаблоні як `$KEY` (контракт `envsubst`);
|
|
12
|
+
4. перевіряє, що в будь-якому `Dockerfile` / `Containerfile` репозиторію є крок стиснення статики `find … /usr/share/nginx/html … gzip -k` і виклик `envsubst` з `default.conf.template`;
|
|
13
|
+
5. делегує валідацію `.vscode/extensions.json` і `.vscode/settings.json` rego-пакетам `nginx_default_tpl.vscode_extensions` / `nginx_default_tpl.vscode_settings` через `runConftestBatch`.
|
|
14
|
+
|
|
15
|
+
Перевірка є **умовною**: якщо в дереві (після міграції) немає жодного `default.conf.template`, увесь крок пропускається з exit-кодом `0`. Приклад HTTPRoute з правила залишений лише для рев'ю — функція `httpRouteMatchesNginxDefaultTpl` присутня в експорті як інструмент для тестів і потенційного майбутнього вузького застосування, але в `check()` зараз **не** викликається (через різнорідність схем маршрутизації в продукті).
|
|
16
|
+
|
|
17
|
+
### Канонічні константи
|
|
18
|
+
|
|
19
|
+
Літерали правила, винесені в регулярні вирази й рядки модуля:
|
|
20
|
+
|
|
21
|
+
- порт listen: `8080`;
|
|
22
|
+
- кореневий каталог статики: `/usr/share/nginx/html`;
|
|
23
|
+
- директива заміни невалідного логу: `error_log /dev/null crit;`;
|
|
24
|
+
- розширення для стиснення: `*.{js,css}` (через `GZIP_EXTENSION_RE`).
|
|
25
|
+
|
|
26
|
+
## Експорти / API
|
|
27
|
+
|
|
28
|
+
Усі експорти — іменовані; default-експорту немає.
|
|
29
|
+
|
|
30
|
+
| Символ | Тип | Призначення |
|
|
31
|
+
| -------------------------------------------------- | -------------- | -------------------------------------------------------------------------------------- |
|
|
32
|
+
| `findDefaultConfTemplatePaths(root, ignorePaths?)` | async function | Збирає абсолютні шляхи всіх `default.conf.template` у дереві (виключаючи `fixtures/`). |
|
|
33
|
+
| `migrateDefaultTplConfFiles(root, ignorePaths?)` | async function | Мігрує застарілі `default.tpl.conf` у `default.conf.template`. |
|
|
34
|
+
| `migrateErrorLogOffDirective(root, ignorePaths?)` | async function | Замінює `error_log off;` на `error_log /dev/null crit;` у всіх знайдених шаблонах. |
|
|
35
|
+
| `parseIniVariableNames(iniText)` | function | Витягує імена ключів з тексту `.ini` (рядки виду `KEY=value`). |
|
|
36
|
+
| `nginxTemplateViolations(content)` | function | Повертає перше порушення канону шаблону або `null`. |
|
|
37
|
+
| `httpRouteMatchesNginxDefaultTpl(manifest)` | function | Перевіряє структуру `HTTPRoute` (зараз не використовується в `check()`). |
|
|
38
|
+
| `iniKeysMissingInTemplate(keys, template)` | function | Повертає повідомлення про першу ключ-змінну з `*.ini`, якої немає в шаблоні як `$KEY`. |
|
|
39
|
+
| `check(cwd?)` | async function | Головна точка входу правила; повертає `0`/`1` як код виходу. |
|
|
40
|
+
|
|
41
|
+
Внутрішні (не експортовані) хелпери:
|
|
42
|
+
|
|
43
|
+
- `dockerfileHasGzipStaticPipeline(dockerfileContent)` — детектор стиснення статики в Dockerfile;
|
|
44
|
+
- `dockerfileHasEnvsSubstTemplate(dockerfileContent)` — детектор кроку `envsubst` із шаблоном;
|
|
45
|
+
- `checkTemplateFile(abs, root, passFn, failFn)` — перевірка одного шаблону й сусідніх `*.ini`;
|
|
46
|
+
- `checkDockerfiles(root, ignorePaths, passFn, failFn)` — перевірка Dockerfile'ів проєкту;
|
|
47
|
+
- `checkVscodeNginx(passFn, failFn, cwd)` — делегування у rego-перевірки VSCode-конфігів.
|
|
48
|
+
|
|
49
|
+
## Функції
|
|
50
|
+
|
|
51
|
+
### `findDefaultConfTemplatePaths(root, ignorePaths = [])`
|
|
52
|
+
|
|
53
|
+
**Сигнатура:** `async (root: string, ignorePaths?: string[]) => Promise<string[]>`
|
|
54
|
+
|
|
55
|
+
**Параметри:**
|
|
56
|
+
|
|
57
|
+
- `root` — абсолютний корінь обходу (зазвичай `process.cwd()` репозиторію);
|
|
58
|
+
- `ignorePaths` — список абсолютних шляхів каталогів, повністю виключених з обходу (зчитується з `.cursorignore` / `.gitignore` через `loadCursorIgnorePaths`).
|
|
59
|
+
|
|
60
|
+
**Повертає:** відсортований (`localeCompare`) масив абсолютних шляхів до файлів з іменем `default.conf.template`.
|
|
61
|
+
|
|
62
|
+
**Поведінка:** обходить дерево через `walkDir`, додає тільки шляхи з базовим іменем `default.conf.template`. Будь-який шлях, у якому хоч один сегмент дорівнює `fixtures`, пропускається — це покриває як кореневі `tests/fixtures/`, так і co-located `rules/<rule>/js/<concern>/fixtures/`. Перед порівнянням бекслеші у відносному шляху нормалізуються в `/` (Windows-safe).
|
|
63
|
+
|
|
64
|
+
**Side effects:** виключно читання дерева ФС через `walkDir`.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### `migrateDefaultTplConfFiles(root, ignorePaths = [])`
|
|
69
|
+
|
|
70
|
+
**Сигнатура:** `async (root: string, ignorePaths?: string[]) => Promise<{ renamed: string[], overwritten: string[] }>`
|
|
71
|
+
|
|
72
|
+
**Параметри:** аналогічні `findDefaultConfTemplatePaths`.
|
|
73
|
+
|
|
74
|
+
**Повертає:** об'єкт з двома масивами відносних (від `root`) шляхів колишніх `default.tpl.conf`:
|
|
75
|
+
|
|
76
|
+
- `renamed` — файл був просто перейменований у `default.conf.template`;
|
|
77
|
+
- `overwritten` — поруч уже існував `default.conf.template`, тому його вміст замінено вмістом `default.tpl.conf`, а сам `default.tpl.conf` видалено.
|
|
78
|
+
|
|
79
|
+
**Поведінка:**
|
|
80
|
+
|
|
81
|
+
1. збирає всі `default.tpl.conf` через `walkDir` і сортує їх для детермінованого порядку обробки;
|
|
82
|
+
2. для кожного шляху обчислює цільовий `default.conf.template` у тому ж каталозі (`dirname` + `join`);
|
|
83
|
+
3. якщо ціль існує (`existsSync`) — читає вміст `default.tpl.conf`, записує його в `default.conf.template` (`writeFile` з `utf8`), видаляє джерело (`unlink`);
|
|
84
|
+
4. інакше виконує `rename(old → new)`.
|
|
85
|
+
|
|
86
|
+
**Side effects:** мутація ФС — записи, перейменування, видалення файлів. Усі шляхи в звіті нормалізовані `/`-роздільником.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### `migrateErrorLogOffDirective(root, ignorePaths = [])`
|
|
91
|
+
|
|
92
|
+
**Сигнатура:** `async (root: string, ignorePaths?: string[]) => Promise<string[]>`
|
|
93
|
+
|
|
94
|
+
**Параметри:** аналогічні попереднім міграційним функціям.
|
|
95
|
+
|
|
96
|
+
**Повертає:** відносні шляхи шаблонів, у яких було виконано заміну (для звіту `pass`).
|
|
97
|
+
|
|
98
|
+
**Поведінка:** для кожного `default.conf.template`, знайденого `findDefaultConfTemplatePaths`, читає вміст, виконує заміну `ERROR_LOG_OFF_RE → ERROR_LOG_CANONICAL` (тобто `error_log\s+off\s*;` → `error_log /dev/null crit;`). Якщо текст не змінився — файл не переписується (раннє `continue`); інакше — `writeFile` з `utf8`.
|
|
99
|
+
|
|
100
|
+
**Мотивація:** `error_log off;` у nginx **не** валідна директива — `off` трактується як ім'я файлу (`/etc/nginx/off`), і під `readOnlyRootFilesystem` контейнер падає. `/dev/null` — writable character device і коректно «вимикає» лог.
|
|
101
|
+
|
|
102
|
+
**Side effects:** перезапис вмісту шаблонів на місці.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
### `parseIniVariableNames(iniText)`
|
|
107
|
+
|
|
108
|
+
**Сигнатура:** `(iniText: string) => string[]`
|
|
109
|
+
|
|
110
|
+
**Параметри:**
|
|
111
|
+
|
|
112
|
+
- `iniText` — повний текст INI-файлу.
|
|
113
|
+
|
|
114
|
+
**Повертає:** масив імен ключів у порядку появи (дублікати не дедуплікуються; вони з'являться стільки разів, скільки разів зустрілися).
|
|
115
|
+
|
|
116
|
+
**Поведінка:** розбиває вхід за `LINE_SPLIT_RE` (`\r?\n`), для кожного рядка обрізає пробіли (`trim()`), пропускає порожні й коментарі (`#`, `;`), решту матчить регексом `INI_KEY_RE = /^([A-Za-z_]\w*)\s*=/` і додає першу групу. Секції (`[section]`) ігноруються природним чином, бо не матчаться регексом.
|
|
117
|
+
|
|
118
|
+
**Side effects:** немає, чиста функція.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### `nginxTemplateViolations(content)`
|
|
123
|
+
|
|
124
|
+
**Сигнатура:** `(content: string) => string | null`
|
|
125
|
+
|
|
126
|
+
**Параметри:**
|
|
127
|
+
|
|
128
|
+
- `content` — повний текст `default.conf.template`.
|
|
129
|
+
|
|
130
|
+
**Повертає:** рядок з першим знайденим порушенням або `null`, якщо шаблон валідний.
|
|
131
|
+
|
|
132
|
+
**Поведінка:** проходить упорядкований список правил і повертає повідомлення першого, чий предикат `ok(content)` повернув `false`. Порядок матеріалізовано в коді; перелік правил, що перевіряються (у порядку виконання):
|
|
133
|
+
|
|
134
|
+
1. наявність `server_tokens off`;
|
|
135
|
+
2. наявність `port_in_redirect off`;
|
|
136
|
+
3. наявність `client_max_body_size 0`;
|
|
137
|
+
4. наявність `client_body_buffer_size 512M`;
|
|
138
|
+
5. наявність `listen 8080`;
|
|
139
|
+
6. наявність `server_name _`;
|
|
140
|
+
7. наявність `access_log off`;
|
|
141
|
+
8. наявність `error_log /dev/null crit` (саме така форма; `error_log off` не валідний);
|
|
142
|
+
9. наявність `root /usr/share/nginx/html`;
|
|
143
|
+
10. `location /healthz` повертає healthy (підрядок `healthy` **або** `return\s+200`);
|
|
144
|
+
11. `location` для статики без gzip — мають бути присутні підрядки `gif|jpe?g|png|ico|woff2|xlsx`, `31536000` та `alias /usr/share/nginx/html/`;
|
|
145
|
+
12. `location` для текстової статики з gzip — підрядок `svg|js|css|ttf|map|xml|webmanifest|wasm`;
|
|
146
|
+
13. `gzip_static on` зустрічається **щонайменше двічі** (для двох location-блоків зі стисненням);
|
|
147
|
+
14. використання `$PUBLIC_PATH` у будь-якому location;
|
|
148
|
+
15. наявність `sendfile on`, `sendfile_max_chunk 512k`, `tcp_nopush on`;
|
|
149
|
+
16. наявність `try_files $uri $uri/ /index.html =404`.
|
|
150
|
+
|
|
151
|
+
Після всіх «позитивних» правил додатково перевіряється негативне: якщо регекс `PROXY_LIKE_RE` (`proxy_pass|proxy_redirect|proxy_set_header|proxy_http_version|fastcgi_pass|grpc_pass|uwsgi_pass`) **матчить** вміст, повертається повідомлення про `*_pass` із вказівкою винести логіку в HTTPRoute. У коді є директива `// cspell:ignore fastcgi uwsgi`, щоб ці назви не лаялися спелчекером.
|
|
152
|
+
|
|
153
|
+
**Side effects:** немає.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
### `httpRouteMatchesNginxDefaultTpl(manifest)`
|
|
158
|
+
|
|
159
|
+
**Сигнатура:** `(manifest: unknown) => boolean`
|
|
160
|
+
|
|
161
|
+
**Параметри:**
|
|
162
|
+
|
|
163
|
+
- `manifest` — десеріалізований корінь YAML-документа (об'єкт, мапа, або довільне значення з парсера).
|
|
164
|
+
|
|
165
|
+
**Повертає:** `true`, якщо структура збігається з еталонним прикладом `HTTPRoute` з `nginx-default-tpl.mdc`; інакше `false`.
|
|
166
|
+
|
|
167
|
+
**Поведінка:** ретельно гардить кожен шар від `null`/`undefined`/масивів/не-об'єктів і перевіряє:
|
|
168
|
+
|
|
169
|
+
- `kind === 'HTTPRoute'`;
|
|
170
|
+
- `spec` — об'єкт, не масив;
|
|
171
|
+
- `spec.rules` — масив довжиною ≥ 2;
|
|
172
|
+
- у `rules[0]`:
|
|
173
|
+
- `matches` містить елемент з `path.type === 'Exact'`;
|
|
174
|
+
- `filters` містить елемент з `type === 'RequestRedirect'`, `requestRedirect.scheme === 'https'`, `requestRedirect.path.type === 'ReplaceFullPath'` і `statusCode` `301` (як число або рядок);
|
|
175
|
+
- у `rules[1]`:
|
|
176
|
+
- `matches` містить елемент з `path.type === 'PathPrefix'`;
|
|
177
|
+
- `backendRefs` містить елемент з `port` `8080` (як число або рядок).
|
|
178
|
+
|
|
179
|
+
Підсумок: `hasExact && hasRedirect && hasPrefix && has8080`.
|
|
180
|
+
|
|
181
|
+
**Side effects:** немає. Зараз не викликається з `check()` (зарезервована для тестів і майбутніх перевірок).
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
### `iniKeysMissingInTemplate(keys, template)`
|
|
186
|
+
|
|
187
|
+
**Сигнатура:** `(keys: string[], template: string) => string | null`
|
|
188
|
+
|
|
189
|
+
**Параметри:**
|
|
190
|
+
|
|
191
|
+
- `keys` — масив імен змінних (зазвичай результат `parseIniVariableNames`);
|
|
192
|
+
- `template` — повний текст `default.conf.template`.
|
|
193
|
+
|
|
194
|
+
**Повертає:** повідомлення про першу змінну, для якої в шаблоні немає підрядка `$KEY`; або `null`, якщо всі ключі присутні.
|
|
195
|
+
|
|
196
|
+
**Поведінка:** простий послідовний перебір `keys` з перевіркою `template.includes('$' + key)`. Це контракт `envsubst`: якщо ключ є у values-ini, він має бути використаний у шаблоні (або вилучений з ini).
|
|
197
|
+
|
|
198
|
+
**Side effects:** немає.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### `dockerfileHasGzipStaticPipeline(dockerfileContent)` (внутрішня)
|
|
203
|
+
|
|
204
|
+
**Сигнатура:** `(dockerfileContent: string) => boolean`
|
|
205
|
+
|
|
206
|
+
**Параметри:**
|
|
207
|
+
|
|
208
|
+
- `dockerfileContent` — повний текст одного Dockerfile/Containerfile.
|
|
209
|
+
|
|
210
|
+
**Повертає:** `true`, якщо в тексті одночасно присутні: `find` (через `FIND_CMD_RE`), підрядок `/usr/share/nginx/html`, `gzip` (через `GZIP_CMD_RE`), прапор `-k` і регекс розширень `*.(js|css)` (через `GZIP_EXTENSION_RE`).
|
|
211
|
+
|
|
212
|
+
**Side effects:** немає.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
### `dockerfileHasEnvsSubstTemplate(dockerfileContent)` (внутрішня)
|
|
217
|
+
|
|
218
|
+
**Сигнатура:** `(dockerfileContent: string) => boolean`
|
|
219
|
+
|
|
220
|
+
**Параметри:**
|
|
221
|
+
|
|
222
|
+
- `dockerfileContent` — повний текст Dockerfile/Containerfile.
|
|
223
|
+
|
|
224
|
+
**Повертає:** `true`, якщо текст містить одночасно `envsubst` і `default.conf.template`.
|
|
225
|
+
|
|
226
|
+
**Side effects:** немає.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
### `checkTemplateFile(abs, root, passFn, failFn)` (внутрішня, async)
|
|
231
|
+
|
|
232
|
+
**Сигнатура:** `async (abs: string, root: string, passFn: (msg: string) => void, failFn: (msg: string) => void) => Promise<void>`
|
|
233
|
+
|
|
234
|
+
**Параметри:**
|
|
235
|
+
|
|
236
|
+
- `abs` — абсолютний шлях до `default.conf.template`;
|
|
237
|
+
- `root` — корінь репозиторію (для відносних повідомлень);
|
|
238
|
+
- `passFn`, `failFn` — колбеки `createCheckReporter`.
|
|
239
|
+
|
|
240
|
+
**Поведінка:**
|
|
241
|
+
|
|
242
|
+
1. читає шаблон, викликає `nginxTemplateViolations`, рапортує `pass` або `fail` зі стислим описом порушення;
|
|
243
|
+
2. читає сусідній каталог (`readdir(dirname(abs))`); якщо `readdir` падає (наприклад, нет прав), список INI-файлів вважається порожнім (`try/catch`);
|
|
244
|
+
3. з усіх записів обирає тільки ті, що закінчуються на `.ini`;
|
|
245
|
+
4. якщо INI немає — викликає `failFn` з підказкою додати `values-*.ini` і повертається;
|
|
246
|
+
5. інакше — рапортує `pass` про знайдені ini (з кількістю), і для кожного:
|
|
247
|
+
- читає текст (`readFile utf8`); якщо `readFile` падає — рапортує `fail` з повідомленням помилки (`Error.message` або `String(error)` для не-`Error`);
|
|
248
|
+
- інакше парсить імена через `parseIniVariableNames`, передає у `iniKeysMissingInTemplate(keys, content)`; якщо є невикористаний ключ — `fail` з відносним шляхом ini.
|
|
249
|
+
|
|
250
|
+
**Side effects:** читання файлів і каталогів, виклик колбеків репортера.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
### `checkDockerfiles(root, ignorePaths, passFn, failFn)` (внутрішня, async)
|
|
255
|
+
|
|
256
|
+
**Сигнатура:** `async (root: string, ignorePaths: string[], passFn, failFn) => Promise<void>`
|
|
257
|
+
|
|
258
|
+
**Поведінка:**
|
|
259
|
+
|
|
260
|
+
1. шукає Dockerfile'и через `findDockerfilePaths(root, ignorePaths)` (імпорт з `../../docker/js/lint.mjs`);
|
|
261
|
+
2. якщо їх немає — `fail` з підказкою (бо `default.conf.template` уже знайдено);
|
|
262
|
+
3. читає вміст усіх Dockerfile'ів `Promise.all`;
|
|
263
|
+
4. якщо **хоч один** має gzip-pipeline (`dockerfileHasGzipStaticPipeline`) — `pass`, інакше `fail`;
|
|
264
|
+
5. якщо **хоч один** має envsubst+template (`dockerfileHasEnvsSubstTemplate`) — `pass`, інакше `fail`.
|
|
265
|
+
|
|
266
|
+
**Side effects:** читання Dockerfile'ів, виклик колбеків.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
### `checkVscodeNginx(passFn, failFn, cwd)` (внутрішня, sync)
|
|
271
|
+
|
|
272
|
+
**Сигнатура:** `(passFn, failFn, cwd: string) => void`
|
|
273
|
+
|
|
274
|
+
**Поведінка:** має дві однотипні гілки для `.vscode/extensions.json` і `.vscode/settings.json`:
|
|
275
|
+
|
|
276
|
+
- якщо файл існує — викликає `runConftestBatch` із відповідним `policyDirRel` (`nginx-default-tpl/vscode_extensions` або `…/vscode_settings`) і `namespace` (`nginx_default_tpl.vscode_extensions` / `…vscode_settings`), передає масив з одного файлу. Якщо `violations` порожній — `pass`; інакше для кожного `v` — `failFn(v.message)`;
|
|
277
|
+
- якщо файлу немає — `failFn` з підказкою з `nginx-default-tpl.mdc`.
|
|
278
|
+
|
|
279
|
+
Для `settings.json` при відсутності файлу функція робить ранній `return`, не виконуючи `runConftestBatch` другий раз.
|
|
280
|
+
|
|
281
|
+
**Side effects:** виконання `conftest` як зовнішнього процесу (через `runConftestBatch`), виклик колбеків.
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
### `check(cwd = process.cwd())`
|
|
286
|
+
|
|
287
|
+
**Сигнатура:** `async (cwd?: string) => Promise<number>`
|
|
288
|
+
|
|
289
|
+
**Параметри:**
|
|
290
|
+
|
|
291
|
+
- `cwd` — корінь репозиторію; за замовчанням `process.cwd()`.
|
|
292
|
+
|
|
293
|
+
**Повертає:** код виходу від `reporter.getExitCode()`: `0`, якщо `failFn` жодного разу не викликався (тільки `pass`); `1` — якщо була хоч одна помилка.
|
|
294
|
+
|
|
295
|
+
**Поведінка (потік виконання):**
|
|
296
|
+
|
|
297
|
+
1. створює репортер `createCheckReporter()` і деструктурує `{ pass, fail }`;
|
|
298
|
+
2. читає список ігнорованих шляхів через `loadCursorIgnorePaths(root)`;
|
|
299
|
+
3. виконує міграцію `default.tpl.conf` → `default.conf.template` (`migrateDefaultTplConfFiles`) і репортує `pass` за кожним перейменованим/перезаписаним файлом;
|
|
300
|
+
4. виконує заміну `error_log off;` (`migrateErrorLogOffDirective`) і репортує `pass` за кожним виправленим шаблоном;
|
|
301
|
+
5. шукає `default.conf.template` (`findDefaultConfTemplatePaths`); якщо їх нуль — `pass` про пропуск і ранній `return reporter.getExitCode()`;
|
|
302
|
+
6. репортує `pass` з кількістю знайдених шаблонів;
|
|
303
|
+
7. послідовно (через `for…of` з `await`) перевіряє кожен шаблон через `checkTemplateFile`;
|
|
304
|
+
8. перевіряє Dockerfile'и через `checkDockerfiles`;
|
|
305
|
+
9. перевіряє VSCode-конфіги через `checkVscodeNginx` (синхронно);
|
|
306
|
+
10. повертає `reporter.getExitCode()`.
|
|
307
|
+
|
|
308
|
+
**Side effects:** виклик усіх міграцій (мутація ФС), читання файлів і каталогів, виконання `conftest` через `runConftestBatch`, запис у репортер.
|
|
309
|
+
|
|
310
|
+
## Залежності
|
|
311
|
+
|
|
312
|
+
### Стандартна бібліотека Node.js
|
|
313
|
+
|
|
314
|
+
- `node:fs` — `existsSync` (синхронна перевірка існування файлу для міграції й VSCode-конфігів);
|
|
315
|
+
- `node:fs/promises` — `readdir`, `readFile`, `rename`, `unlink`, `writeFile`;
|
|
316
|
+
- `node:path` — `basename`, `dirname`, `join`, `relative`.
|
|
317
|
+
|
|
318
|
+
### Внутрішні модулі правил
|
|
319
|
+
|
|
320
|
+
- `../../docker/js/lint.mjs` — `findDockerfilePaths(root, ignorePaths)` — пошук Dockerfile'ів у дереві (повторне використання логіки правила `n-docker`);
|
|
321
|
+
- `../../../scripts/lib/check-reporter.mjs` — `createCheckReporter()` — стандартний репортер pass/fail з агрегованим exit-кодом;
|
|
322
|
+
- `../../../scripts/lib/load-cursor-config.mjs` — `loadCursorIgnorePaths(root)` — список абсолютних ігнорованих шляхів з `.cursor` конфігу;
|
|
323
|
+
- `../../../scripts/lib/run-conftest-batch.mjs` — `runConftestBatch({ policyDirRel, namespace, files })` — синхронний запуск `conftest` для пакетної перевірки rego;
|
|
324
|
+
- `../../../scripts/utils/walkDir.mjs` — `walkDir(root, visitor, ignorePaths)` — асинхронний обхід дерева.
|
|
325
|
+
|
|
326
|
+
### Регулярні вирази та константи модуля
|
|
327
|
+
|
|
328
|
+
- `LINE_SPLIT_RE = /\r?\n/u` — універсальний роздільник рядків для INI;
|
|
329
|
+
- `INI_KEY_RE = /^([A-Za-z_]\w*)\s*=/u` — розпізнавання ключа `KEY=` в INI;
|
|
330
|
+
- `RETURN_200_RE = /return\s+200/u` — індикатор успішного `/healthz`;
|
|
331
|
+
- `GZIP_STATIC_ON_RE = /gzip_static\s+on/gu` — підрахунок входжень для перевірки «щонайменше 2»;
|
|
332
|
+
- `PROXY_LIKE_RE = /\b(proxy_pass|proxy_redirect|proxy_set_header|proxy_http_version|fastcgi_pass|grpc_pass|uwsgi_pass)\b/u` — заборонені директиви бекенду;
|
|
333
|
+
- `FIND_CMD_RE = /\bfind\b/u`, `GZIP_CMD_RE = /\bgzip\b/u` — детектори команд у Dockerfile;
|
|
334
|
+
- `GZIP_EXTENSION_RE = /\*\.(?:js|css)/u` — детектор маски розширень gzip-кроку;
|
|
335
|
+
- `ERROR_LOG_OFF_RE = /error_log\s+off\s*;/gu` — глобальний регекс для заміни;
|
|
336
|
+
- `ERROR_LOG_CANONICAL = 'error_log /dev/null crit;'` — канонічна заміна.
|
|
337
|
+
|
|
338
|
+
## Потік виконання / Використання
|
|
339
|
+
|
|
340
|
+
Модуль використовується як одна з перевірок репозитарного лінтера `n-cursor`/правил `.mdc`. Типовий виклик з контекстного скрипту правила:
|
|
341
|
+
|
|
342
|
+
```mjs
|
|
343
|
+
import { check } from './template.mjs'
|
|
344
|
+
|
|
345
|
+
const exitCode = await check(process.cwd())
|
|
346
|
+
process.exit(exitCode)
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Послідовність дій `check(cwd)`:
|
|
350
|
+
|
|
351
|
+
1. **Підготовка:** створення репортера, завантаження ігнор-списку.
|
|
352
|
+
2. **Міграції (мутативна фаза, до перевірок):**
|
|
353
|
+
- застаріле `default.tpl.conf` → `default.conf.template` (rename або overwrite + unlink);
|
|
354
|
+
- невалідне `error_log off;` → `error_log /dev/null crit;`.
|
|
355
|
+
3. **Збір шаблонів:** `findDefaultConfTemplatePaths` (виключає сегменти `fixtures/`).
|
|
356
|
+
4. **Швидкий вихід:** якщо шаблонів нуль — функція повертає `0` як «нічого перевіряти».
|
|
357
|
+
5. **Перевірка кожного шаблону:**
|
|
358
|
+
- валідація директив через `nginxTemplateViolations` (на перше порушення);
|
|
359
|
+
- пошук сусідніх `*.ini`, парс ключів, перевірка `$KEY` у шаблоні через `iniKeysMissingInTemplate`.
|
|
360
|
+
6. **Перевірка Dockerfile'ів:** наявність gzip-кроку для статики й кроку `envsubst` з шаблоном.
|
|
361
|
+
7. **Перевірка VSCode-конфігів:** делегування у rego через `runConftestBatch`.
|
|
362
|
+
8. **Підсумок:** `reporter.getExitCode()` — `0` або `1`.
|
|
363
|
+
|
|
364
|
+
### Контракт з користувачами модуля
|
|
365
|
+
|
|
366
|
+
- Усі публічні функції є чистими або «атомарними» (одна відповідальність), що дозволяє точково використовувати їх у тестах (наприклад, `parseIniVariableNames`, `nginxTemplateViolations`, `iniKeysMissingInTemplate`, `httpRouteMatchesNginxDefaultTpl`).
|
|
367
|
+
- Шляхи в звітах нормалізовані до `/`-роздільника й відносні до `root`.
|
|
368
|
+
- Сортування результатів (`findDefaultConfTemplatePaths`, `migrateDefaultTplConfFiles` через `oldPaths.sort`) гарантує детермінований порядок виводу повідомлень — це важливо для стабільності тестів.
|
|
369
|
+
- Фази «міграції» виконуються **до** валідації, тому застарілі артефакти не призводять до false-negative; натомість користувач бачить `pass` про факт міграції.
|
|
370
|
+
- Шлях VSCode-перевірки залежить від `existsSync` у момент виклику, тобто новостворені файли (у тій самій сесії процесу) будуть враховані.
|
|
371
|
+
|
|
372
|
+
### Особливості й нюанси
|
|
373
|
+
|
|
374
|
+
- `nginxTemplateViolations` повертає **перше** порушення, а не повний список — це свідома стратегія fail-fast (мінімізація шуму в репортері).
|
|
375
|
+
- `httpRouteMatchesNginxDefaultTpl` приймає тип `unknown`, бо очікує безпосередньо результат YAML-парсера; усі гарди — позитивні (object && !Array && !null && !undefined).
|
|
376
|
+
- `parseIniVariableNames` **не** дедуплікує ключі: дубль у ini призведе до повторного перебору в `iniKeysMissingInTemplate`, проте через `template.includes` обидва результати будуть однакові — це не впливає на коректність.
|
|
377
|
+
- `checkTemplateFile` обережно обробляє помилки `readdir` (поглинає винятки, продовжує без INI), але помилки `readFile` для ini рапортує як `fail` із текстом помилки.
|
|
378
|
+
- `migrateErrorLogOffDirective` не запускає файлову операцію, якщо вміст не змінився — оптимізація для ідемпотентних запусків.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# fix.mjs — точка входу правила `npm-module`
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
`fix.mjs` — мінімалістична точка входу (entry-point) для правила з ідентифікатором, що відповідає назві теки, у якій файл розташований (`npm/rules/npm-module/`). Файл виконує дві ролі одночасно:
|
|
6
|
+
|
|
7
|
+
1. **Library mode** — експортує функцію `run(ctx)`, яку викликає оркестратор `npm/scripts/lib/run-standard-rule.mjs` через стандартний імпорт. Це дозволяє іншому коду (наприклад, агрегатору правил `@nitra/cursor`) виконувати правило у складі загального прогону.
|
|
8
|
+
2. **Standalone mode** — якщо файл стартує напряму (наприклад, `bun npm/rules/npm-module/fix.mjs`), він прогорає еквівалент команди `npx @nitra/cursor fix npm-module` через `runRuleCli` із завантаженням конфігу, whitelist та фінальним summary; повертає у процес коректний exit-code для CI/IDE.
|
|
9
|
+
|
|
10
|
+
Логіки самого правила тут немає — файл лише делегує виконання у стандартний раннер `runStandardRule`, який послідовно проганяє чотири фази правила: `applies` → `JS-concerns` → `policy` → `mdc-refs`.
|
|
11
|
+
|
|
12
|
+
## Експорти / API
|
|
13
|
+
|
|
14
|
+
| Назва | Тип | Призначення |
|
|
15
|
+
| ----- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
16
|
+
| `run` | `function (ctx?) => Promise<number>` | Іменований експорт для library-mode. Викликає `runStandardRule` зі шляхом до теки правила та опційним контекстом. Повертає Promise з exit-кодом: `0` — правило не зафіксувало порушень, `1` — є порушення. |
|
|
17
|
+
|
|
18
|
+
Дефолтних експортів немає. Side-effect верхнього рівня: за умови запуску як CLI, наприкінці модуля викликається `process.exit(...)` з кодом, який повернув `runRuleCli`.
|
|
19
|
+
|
|
20
|
+
## Функції (сигнатура / параметри / повертає / side effects)
|
|
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` (закешований обхід дерева файлів), щоб уникнути повторного walk при паралельному прогоні кількох правил.
|
|
30
|
+
- **Повертає** `Promise<number>`:
|
|
31
|
+
- `0` — правило не виявило порушень або не застосовується до поточного workspace.
|
|
32
|
+
- `1` — зафіксовано принаймні одне порушення.
|
|
33
|
+
- **Side effects**
|
|
34
|
+
- Жодних прямих побічних ефектів у самій `run`. Усі побічні ефекти (читання файлів, друк summary, мутація `walkCache`) — у `runStandardRule` та підлеглих фазах правила.
|
|
35
|
+
- **Реалізація**
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`import.meta.dirname` дає абсолютний шлях до теки правила (`.../npm/rules/npm-module/`); цей шлях `runStandardRule` використовує як корінь для пошуку фаз правила (`applies.mjs`, `js/`, `policy.mjs`, `mdc-refs.mjs` тощо).
|
|
42
|
+
|
|
43
|
+
### Standalone-блок (без іменованої функції)
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
if (isRunAsCli(import.meta.url)) {
|
|
47
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- **Умова** — `isRunAsCli(import.meta.url)` повертає `true`, якщо модуль є точкою входу процесу (а не імпортується іншим модулем).
|
|
52
|
+
- **Дія** — `await runRuleCli(import.meta.dirname)` виконує повний CLI-цикл (config-loading, whitelist, прогін правила, summary) і повертає exit-код. Цей код передається у `process.exit`.
|
|
53
|
+
- **Коментар у коді** свідомо вимикає правила лінтера `n/no-process-exit` та `unicorn/no-process-exit`, оскільки standalone-entry-point має повертати exit-код для CI/IDE.
|
|
54
|
+
|
|
55
|
+
## Залежності
|
|
56
|
+
|
|
57
|
+
Прямі (внутрішні) залежності — два модулі зі спільної бібліотеки скриптів проєкту:
|
|
58
|
+
|
|
59
|
+
| Шлях імпорту | Що бере | Призначення |
|
|
60
|
+
| ----------------------------------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
61
|
+
| `../../scripts/lib/run-rule-cli.mjs` | `isRunAsCli`, `runRuleCli` | Допоміжна логіка для роботи у standalone-режимі: визначення, чи модуль є entry-point процесу, і запуск повного CLI-циклу правила, який є еквівалентом `npx @nitra/cursor fix <id>`. |
|
|
62
|
+
| `../../scripts/lib/run-standard-rule.mjs` | `runStandardRule`, тип `RuleContext` (через JSDoc) | Уніфікований раннер чотирифазного правила (`applies → JS-concerns → policy → mdc-refs`). Через нього прогрівається walk-cache і виконуються фази у фіксованому порядку. |
|
|
63
|
+
|
|
64
|
+
Зовнішніх (npm) залежностей файл не має. Він спирається на Node.js / Bun рантайм-можливості:
|
|
65
|
+
|
|
66
|
+
- `import.meta.url` — для визначення, чи модуль запущений як CLI.
|
|
67
|
+
- `import.meta.dirname` — для отримання абсолютного шляху до теки правила.
|
|
68
|
+
- `process.exit` — для повернення exit-коду у standalone-режимі.
|
|
69
|
+
|
|
70
|
+
## Потік виконання / Використання
|
|
71
|
+
|
|
72
|
+
### Сценарій A: Library mode (через оркестратор `@nitra/cursor`)
|
|
73
|
+
|
|
74
|
+
1. Оркестратор імпортує модуль: `const mod = await import('npm/rules/npm-module/fix.mjs')`.
|
|
75
|
+
2. `isRunAsCli(import.meta.url)` повертає `false` (модуль не є entry-point) — standalone-блок ігнорується.
|
|
76
|
+
3. Оркестратор викликає `await mod.run(ctx)`, передаючи спільний `RuleContext` (наприклад, з заздалегідь побудованим `walkCache`).
|
|
77
|
+
4. `run` делегує виконання `runStandardRule(import.meta.dirname, ctx)`, який проганяє чотири фази правила і повертає `0` або `1`.
|
|
78
|
+
5. Оркестратор агрегує exit-коди всіх правил у фінальний код процесу.
|
|
79
|
+
|
|
80
|
+
### Сценарій B: Standalone mode
|
|
81
|
+
|
|
82
|
+
1. Користувач запускає `bun npm/rules/npm-module/fix.mjs` (еквівалент `npx @nitra/cursor fix npm-module`).
|
|
83
|
+
2. Модуль завантажується як entry-point — `isRunAsCli(import.meta.url)` повертає `true`.
|
|
84
|
+
3. Виконується `await runRuleCli(import.meta.dirname)`:
|
|
85
|
+
- завантажується конфіг (`.cursor`/проектні налаштування),
|
|
86
|
+
- застосовується whitelist (якщо є),
|
|
87
|
+
- запускається чотирифазне правило,
|
|
88
|
+
- друкується summary з кількістю порушень.
|
|
89
|
+
4. Повернутий exit-код передається у `process.exit(...)`, що завершує процес. CI/IDE отримує `0` (успіх) або `1` (порушення).
|
|
90
|
+
|
|
91
|
+
### Контракт двох ролей
|
|
92
|
+
|
|
93
|
+
Дизайн «library + standalone в одному файлі» є типовим для правил `@nitra/cursor`:
|
|
94
|
+
|
|
95
|
+
- Функція `run` — публічний API для оркестратора; вона **не** викликає `process.exit` і не друкує summary, лише повертає код.
|
|
96
|
+
- Standalone-блок навпаки відповідає за повноцінний CLI-UX: завантаження конфігу, whitelist, summary, exit-code — усе, що очікується від `fix.mjs` правила, який запускається окремо.
|
|
97
|
+
|
|
98
|
+
Завдяки цьому контракту той самий файл інтегрується і у пакетний прогін правил, і в локальне налагодження одного правила без дублювання логіки.
|