@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,195 @@
|
|
|
1
|
+
# vitest.config.baseline.js
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Файл `vitest.config.baseline.js` — це **еталонна (baseline) конфігурація Vitest**, яка використовується як test-data fixture у правилі `test` пакета `npm/rules`. Він задає мінімальний, але достатній набір опцій Vitest, що відповідає внутрішнім конвенціям проєкту: розкладку тестових файлів, exclude-патерни для штучних artefact-директорій (наприклад, sandbox-копій Stryker), середовище виконання та модель ізоляції паралельних воркерів.
|
|
6
|
+
|
|
7
|
+
Файл одночасно виконує дві ролі:
|
|
8
|
+
|
|
9
|
+
1. **Робоча конфігурація Vitest** — повністю валідний модуль, який можна передати в `vitest run` чи `vitest --config <path>`. Vitest імпортує `default`-export і використовує його як свої налаштування.
|
|
10
|
+
2. **Канонічний приклад (baseline) для перевірок rule `test`** — зберігається у `data/vitest_config/`, де `data/` — стандартна тека fixture-даних для правил у `npm/rules`. Інші конфіги в цьому проєкті (або їхні фрагменти) можуть порівнюватися з цим baseline, щоб гарантувати єдиний стиль.
|
|
11
|
+
|
|
12
|
+
Ключове технічне рішення, зафіксоване у файлі через коментарі, — використання `pool: 'forks'` замість дефолтного `pool: 'threads'` для уникнення гонок навколо `process.chdir()` у тестових фікстурах (детально див. ## Функції / Конфігурація).
|
|
13
|
+
|
|
14
|
+
## Експорти / API
|
|
15
|
+
|
|
16
|
+
Модуль має один **default export** — об'єкт-конфігурація Vitest, обгорнутий у `defineConfig()`:
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
export default defineConfig({
|
|
20
|
+
test: {
|
|
21
|
+
/* ... */
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
| Експорт | Тип | Призначення |
|
|
27
|
+
| --------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
|
+
| `default` | `UserConfig` (тип Vitest) | Об'єкт конфігурації для команди `vitest`. Підхоплюється автоматично, якщо файл лежить за дефолтним ім'ям `vitest.config.{js,mjs,ts}` у корені проєкту, або задається явно через `--config`. |
|
|
29
|
+
|
|
30
|
+
Іменованих експортів немає.
|
|
31
|
+
|
|
32
|
+
### Структура default-export
|
|
33
|
+
|
|
34
|
+
Об'єкт містить єдиний верхньорівневий ключ `test`, всередині якого:
|
|
35
|
+
|
|
36
|
+
| Ключ | Тип | Значення | Призначення |
|
|
37
|
+
| ------------------ | ---------- | --------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
38
|
+
| `test.include` | `string[]` | `['**/*.test.{js,mjs}', 'tests/**/*.test.{js,mjs}']` | Glob-патерни файлів, які Vitest вважає тестами. |
|
|
39
|
+
| `test.exclude` | `string[]` | `['**/node_modules/**', '**/dist/**', '**/reports/stryker/**']` | Glob-патерни, виключені зі сканування. |
|
|
40
|
+
| `test.environment` | `string` | `'node'` | Тестове середовище — Node.js (без jsdom/happy-dom). |
|
|
41
|
+
| `test.pool` | `string` | `'forks'` | Модель ізоляції воркерів — окремі процеси на тест-файл. |
|
|
42
|
+
| `test.coverage` | `object` | `{ provider: 'v8', reporter: ['lcov', 'text-summary'] }` | Налаштування покриття: V8-провайдер, репортери `lcov` + `text-summary`. |
|
|
43
|
+
|
|
44
|
+
## Функції
|
|
45
|
+
|
|
46
|
+
Файл **не містить власних функцій** — це чисто декларативний конфігураційний модуль. Єдиний виклик — `defineConfig(...)`, імпортований з `vitest/config`.
|
|
47
|
+
|
|
48
|
+
### `defineConfig(config)`
|
|
49
|
+
|
|
50
|
+
| Атрибут | Значення |
|
|
51
|
+
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
|
|
52
|
+
| Походження | Імпортується з пакета `vitest/config` (named export). |
|
|
53
|
+
| Сигнатура | `defineConfig(config: UserConfig | UserConfigFn): UserConfig` |
|
|
54
|
+
| Параметри | `config` — об'єкт конфігурації Vitest або функція, що повертає такий об'єкт (підтримується async-варіант). У цьому файлі передається статичний літерал. |
|
|
55
|
+
| Повертає | Той самий об'єкт (identity для runtime), але з повною TypeScript-типізацією — без `defineConfig` IDE не підказувала б ключі. |
|
|
56
|
+
| Side effects | Немає. Функція — idempotent type-helper. |
|
|
57
|
+
|
|
58
|
+
### Конфігурація — поле за полем
|
|
59
|
+
|
|
60
|
+
**`test.include`**
|
|
61
|
+
|
|
62
|
+
- Значення: `['**/*.test.{js,mjs}', 'tests/**/*.test.{js,mjs}']`.
|
|
63
|
+
- Семантика: підхоплюються **обидві основні розкладки** тестів, прийняті в монорепо:
|
|
64
|
+
- Тести **поряд із кодом** у піддиректоріях `tests/` (test.mdc-конвенція пакета `npm/rules` — кожне правило тримає свої тести у `<rule>/tests/`).
|
|
65
|
+
- Top-level **integration suites** у `<root>/tests/`.
|
|
66
|
+
- Glob-патерн `**/*.test.{js,mjs}` рекурсивно охоплює обидва випадки; другий патерн `tests/**/*.test.{js,mjs}` залишений як надлишково-явна декларація для читабельності та узгодженості з документацією.
|
|
67
|
+
|
|
68
|
+
**`test.exclude`**
|
|
69
|
+
|
|
70
|
+
- Значення: `['**/node_modules/**', '**/dist/**', '**/reports/stryker/**']`.
|
|
71
|
+
- Перші два патерни — стандартні для Node-проєктів (vendor-залежності та build-артефакти).
|
|
72
|
+
- **Критичний патерн** `**/reports/stryker/**` виключає sandbox-копії тестів, які Stryker (mutation testing) залишає у `reports/stryker/.tmp/` після incremental- або aborted-runs. Без цього exclude команда `vitest run --coverage` підхоплює ці копії і вони **фейляться**, бо стартують поза реальним repo root. Детальна мотивація фіксована inline-коментарем.
|
|
73
|
+
|
|
74
|
+
**`test.environment`**
|
|
75
|
+
|
|
76
|
+
- Значення: `'node'`.
|
|
77
|
+
- Тести виконуються у звичайному Node.js-середовищі: без DOM-стабів, без `window`/`document`. Це коректний вибір для CLI- та rule-логіки, що становить основну масу коду в `npm/rules`.
|
|
78
|
+
|
|
79
|
+
**`test.pool`**
|
|
80
|
+
|
|
81
|
+
- Значення: `'forks'` (замість дефолтного `'threads'`).
|
|
82
|
+
- **Defense-in-depth ізоляція процесів** між тест-файлами. Мотивація:
|
|
83
|
+
- У дефолтному `pool: 'threads'` усі воркери ділять **один спільний процес Node.js** → кожен `process.chdir(dir)` всередині фікстури **перехоплює cwd сусіда** посеред його FS- або `git`-операції.
|
|
84
|
+
- Зафіксований реальний інцидент: `git init` + `git commit` із tmp-фікстури **потрапив у реальний робочий репозиторій**, бо cwd змінився в момент виклику Git.
|
|
85
|
+
- `pool: 'forks'` дає кожному файлу окремий процес → `process.chdir` локальний, ізоляція гарантована.
|
|
86
|
+
- Канонічний патерн тестів, прийнятий у проєкті, — `withTmpDir(async dir => ...)` (зафіксований у `test.mdc`).
|
|
87
|
+
|
|
88
|
+
**`test.coverage`**
|
|
89
|
+
|
|
90
|
+
- Значення: `{ provider: 'v8', reporter: ['lcov', 'text-summary'] }`.
|
|
91
|
+
- `provider: 'v8'` — нативне покриття від V8 engine (без інструментації Istanbul) — швидше та точніше для сучасного Node.
|
|
92
|
+
- `reporter`:
|
|
93
|
+
- `'lcov'` — машиночитний формат (`lcov.info`) для імпорту в CI / SonarQube / Codecov.
|
|
94
|
+
- `'text-summary'` — компактний текстовий звіт у stdout для локального запуску й логів CI.
|
|
95
|
+
|
|
96
|
+
### Side effects модуля
|
|
97
|
+
|
|
98
|
+
Side effects відсутні. Файл — pure declarative module: на import-time не відбувається мутації глобального стану, відкриття дескрипторів чи мережевих з'єднань. Vitest сам інтерпретує об'єкт у власному lifecycle.
|
|
99
|
+
|
|
100
|
+
## Залежності
|
|
101
|
+
|
|
102
|
+
### Зовнішні (npm)
|
|
103
|
+
|
|
104
|
+
| Пакет | Імпорт | Призначення |
|
|
105
|
+
| --------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
106
|
+
| `vitest/config` | `import { defineConfig } from 'vitest/config'` | Сабпакет основного пакета `vitest`. Експортує `defineConfig` — type-helper, що дає TypeScript-/IDE-підказки для об'єкта конфігурації. Має бути доступним як devDependency. |
|
|
107
|
+
|
|
108
|
+
### Внутрішні залежності проєкту
|
|
109
|
+
|
|
110
|
+
Жодних internal-імпортів — файл є кінцевим листком тестового data-fixture.
|
|
111
|
+
|
|
112
|
+
### Транзитивні / runtime
|
|
113
|
+
|
|
114
|
+
На етапі реального тестового виконання задіюються (поза цим файлом):
|
|
115
|
+
|
|
116
|
+
- `vitest` — раннер, який споживає об'єкт конфігурації.
|
|
117
|
+
- `@vitest/coverage-v8` — окремий пакет coverage-провайдера V8, потрібен, якщо запускати з `--coverage` (іноді постачається як peer-dependency).
|
|
118
|
+
|
|
119
|
+
## Потік виконання / Використання
|
|
120
|
+
|
|
121
|
+
### Як baseline-fixture для правила `test`
|
|
122
|
+
|
|
123
|
+
Файл лежить у `npm/rules/test/js/data/vitest_config/` — типовій теці `data/`, де правила пакета `npm/rules` зберігають **референсні приклади (fixtures)** для:
|
|
124
|
+
|
|
125
|
+
- snapshot-порівнянь з конфігами користувацьких проєктів,
|
|
126
|
+
- pattern-matching у перевірках (наприклад, чи містить чужий `vitest.config.js` необхідні ключі),
|
|
127
|
+
- авто-генерації / migration-кроків (rule може запропонувати оновити чужий конфіг до baseline).
|
|
128
|
+
|
|
129
|
+
Конкретний механізм споживання baseline залежить від реалізації check-функцій правила (`check-*.mjs`), але типова схема така:
|
|
130
|
+
|
|
131
|
+
1. Rule отримує цільовий `vitest.config.{js,mjs,ts}` проєкту.
|
|
132
|
+
2. Парсить його (через AST або динамічний import у sandbox) та зіставляє з baseline.
|
|
133
|
+
3. Емітить попередження / автофікс, якщо ключові поля (`pool`, `exclude`, `coverage.provider`) відхиляються від baseline.
|
|
134
|
+
|
|
135
|
+
### Як робоча конфігурація Vitest
|
|
136
|
+
|
|
137
|
+
Файл повністю придатний до прямого використання Vitest-CLI:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
vitest run --config npm/rules/test/js/data/vitest_config/vitest.config.baseline.js
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
або копіюванням у корінь споживчого проєкту під ім'ям `vitest.config.js`.
|
|
144
|
+
|
|
145
|
+
Lifecycle:
|
|
146
|
+
|
|
147
|
+
1. Vitest CLI завантажує модуль через ESM-loader Node.
|
|
148
|
+
2. `defineConfig` повертає той самий об'єкт; default-export потрапляє у Vitest internals.
|
|
149
|
+
3. Vitest застосовує `test.include`/`test.exclude` для discovery, потім ініціалізує `forks`-pool, для кожного знайденого файлу спавнить child-process з `environment: 'node'`.
|
|
150
|
+
4. Якщо запуск із `--coverage` — після проходу збирається V8-coverage і пишеться `lcov.info` + текстовий summary.
|
|
151
|
+
|
|
152
|
+
### Інваріанти, що треба зберігати при правках
|
|
153
|
+
|
|
154
|
+
- **Не змінювати `pool: 'forks'` на `'threads'`** без явного перегляду всіх тестів на `process.chdir` — це регрес з ризиком пошкодження реального репозиторію.
|
|
155
|
+
- **Не видаляти `**/reports/stryker/**`з`exclude`** — інакше coverage-run починає підхоплювати mutation-sandbox-копії.
|
|
156
|
+
- **Не додавати `test.environment: 'jsdom'`** без потреби — це повільніше і неактуально для текстової/CLI-логіки `npm/rules`.
|
|
157
|
+
|
|
158
|
+
## Rebuild Test
|
|
159
|
+
|
|
160
|
+
Файл — конфігурація без власної логіки, тому ребілд-тест зводиться до перевірки структурної відповідності default-export очікуваним полям. Орієнтовний smoke-тест на Vitest (псевдокод):
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
import { describe, it, expect } from 'vitest'
|
|
164
|
+
import config from './vitest.config.baseline.js'
|
|
165
|
+
|
|
166
|
+
describe('vitest.config.baseline.js', () => {
|
|
167
|
+
it('експортує об`єкт із секцією test', () => {
|
|
168
|
+
expect(config).toBeTypeOf('object')
|
|
169
|
+
expect(config.test).toBeTypeOf('object')
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('include охоплює дві канонічні розкладки', () => {
|
|
173
|
+
expect(config.test.include).toEqual(['**/*.test.{js,mjs}', 'tests/**/*.test.{js,mjs}'])
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('exclude містить stryker-sandbox', () => {
|
|
177
|
+
expect(config.test.exclude).toContain('**/reports/stryker/**')
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('environment === node', () => {
|
|
181
|
+
expect(config.test.environment).toBe('node')
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('pool === forks (захист від chdir-гонок)', () => {
|
|
185
|
+
expect(config.test.pool).toBe('forks')
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('coverage: v8 + lcov + text-summary', () => {
|
|
189
|
+
expect(config.test.coverage.provider).toBe('v8')
|
|
190
|
+
expect(config.test.coverage.reporter).toEqual(['lcov', 'text-summary'])
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Цього достатньо, щоб зафіксувати baseline як invariant — будь-яка спроба змінити критичні поля без узгодження впаде на smoke-тесті.
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# cargo_mutants_config.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Файл реалізує концерн `cargo_mutants_config` правила `test` (відповідає правилу
|
|
6
|
+
`test.mdc`) для пакета `@nitra/cursor`. Призначення концерну — забезпечити, що
|
|
7
|
+
у кожному Cargo-крейті проєкту присутній файл `.cargo/mutants.toml` із
|
|
8
|
+
канонічним baseline-вмістом, який потрібен інструменту
|
|
9
|
+
[`cargo-mutants`](https://mutants.rs/) для запуску мутаційного тестування Rust.
|
|
10
|
+
|
|
11
|
+
Логіка self-gating: концерн виконується тільки якщо в `.n-cursor.json` правило
|
|
12
|
+
`rust` присутнє у списку `rules` і не присутнє у списку `disable-rules`. Якщо
|
|
13
|
+
правило `rust` вимкнене — функція мовчки повертає успіх. Якщо правило
|
|
14
|
+
ввімкнене, але в проєкті ще немає жодного `Cargo.toml` (наприклад, манифест
|
|
15
|
+
з'явиться пізніше) — функція теж мовчки повертає успіх, не вважаючи це
|
|
16
|
+
порушенням.
|
|
17
|
+
|
|
18
|
+
У разі, якщо canonical baseline-файл `data/cargo_mutants_config/mutants.toml.baseline`
|
|
19
|
+
відсутній у дистрибутиві `@nitra/cursor` (наприклад, через зламану інсталяцію),
|
|
20
|
+
концерн фейлить із вказівкою перевстановити пакет.
|
|
21
|
+
|
|
22
|
+
Для кожного знайденого `Cargo.toml` (cwd, всі workspace-члени, включно з
|
|
23
|
+
Tauri-патерном `src-tauri/Cargo.toml`) концерн перевіряє наявність файлу
|
|
24
|
+
`.cargo/mutants.toml` у відповідному каталозі манифеста. Якщо файл існує —
|
|
25
|
+
позначає його як pass і йде далі. Якщо не існує — створює каталог `.cargo/`
|
|
26
|
+
(`recursive: true`) та копіює canonical baseline у `.cargo/mutants.toml`.
|
|
27
|
+
|
|
28
|
+
Baseline-файл — порожній з коментарем; у `cargo-mutants` працюють робочі
|
|
29
|
+
defaults, тож фактичні налаштування не потрібні.
|
|
30
|
+
|
|
31
|
+
## Експорти / API
|
|
32
|
+
|
|
33
|
+
| Експорт | Тип | Опис |
|
|
34
|
+
| ------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
35
|
+
| `check` | `async function(cwd?: string): Promise<number>` | Основна точка входу концерну. Повертає exit-код: `0` — OK або silently skipped, `1` — порушення. |
|
|
36
|
+
|
|
37
|
+
Інших експортів модуль не має. Константи `HERE` та `BASELINE_PATH` —
|
|
38
|
+
внутрішні, не експортуються.
|
|
39
|
+
|
|
40
|
+
## Функції
|
|
41
|
+
|
|
42
|
+
### `check(cwd = process.cwd())`
|
|
43
|
+
|
|
44
|
+
Асинхронна функція, що виконує перевірку та (за потреби) копіювання canonical
|
|
45
|
+
baseline `.cargo/mutants.toml` у кожен Cargo-крейт проєкту.
|
|
46
|
+
|
|
47
|
+
**Сигнатура:**
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
export async function check(cwd = process.cwd()): Promise<number>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Параметри:**
|
|
54
|
+
|
|
55
|
+
- `cwd` — `string`, необов'язковий. Корінь проєкту, у якому шукати
|
|
56
|
+
`.n-cursor.json` та `Cargo.toml`. За замовчуванням — `process.cwd()`
|
|
57
|
+
(підтримка CLI-сценарію виклику).
|
|
58
|
+
|
|
59
|
+
**Повертає:**
|
|
60
|
+
|
|
61
|
+
- `Promise<number>` — exit-код, який повертає `reporter.getExitCode()`:
|
|
62
|
+
- `0` — концерн пройшов успішно (включно з випадками silently skip:
|
|
63
|
+
rust не enabled або немає жодного Cargo.toml).
|
|
64
|
+
- `1` — concer зафейлив (наприклад, відсутній canonical baseline у
|
|
65
|
+
дистрибутиві `@nitra/cursor`).
|
|
66
|
+
|
|
67
|
+
**Алгоритм:**
|
|
68
|
+
|
|
69
|
+
1. Створює `reporter` через `createCheckReporter()`.
|
|
70
|
+
2. Читає `.n-cursor.json` через `readNCursorConfigLite(cwd)`, отримує
|
|
71
|
+
`config.rules` і `config.disableRules`.
|
|
72
|
+
3. Self-gate: якщо `rust` не у `config.rules` або є у `config.disableRules` —
|
|
73
|
+
повертає `reporter.getExitCode()` без жодних повідомлень.
|
|
74
|
+
4. Резолвить усі `Cargo.toml` у проєкті через
|
|
75
|
+
`resolveAllCargoManifests(cwd)` (cwd, workspaces, Tauri-патерн).
|
|
76
|
+
5. Якщо манифестів немає — silently skip, повертає `reporter.getExitCode()`.
|
|
77
|
+
6. Перевіряє існування canonical baseline за шляхом `BASELINE_PATH`. Якщо
|
|
78
|
+
файлу немає — `reporter.fail()` із рекомендацією перевстановити
|
|
79
|
+
`@nitra/cursor` і повертає `reporter.getExitCode()`.
|
|
80
|
+
7. Ітерує по кожному `manifestPath`:
|
|
81
|
+
- Обчислює `cargoDir = dirname(manifestPath)`.
|
|
82
|
+
- Обчислює `target = join(cargoDir, '.cargo', 'mutants.toml')`.
|
|
83
|
+
- Якщо `target` існує — `reporter.pass()` з повідомленням
|
|
84
|
+
`.cargo/mutants.toml існує (<relative-path>)`, переходить до наступного
|
|
85
|
+
манифеста.
|
|
86
|
+
- Інакше — створює каталог `dirname(target)` (тобто `.cargo/`) із
|
|
87
|
+
прапорцем `{ recursive: true }`, копіює `BASELINE_PATH` у `target`,
|
|
88
|
+
повідомляє `reporter.pass()` з повідомленням
|
|
89
|
+
`.cargo/mutants.toml створено з canonical baseline (<relative-path>) (test.mdc)`.
|
|
90
|
+
8. Повертає `reporter.getExitCode()`.
|
|
91
|
+
|
|
92
|
+
**Side effects:**
|
|
93
|
+
|
|
94
|
+
- Читання файлової системи: `.n-cursor.json` (через
|
|
95
|
+
`readNCursorConfigLite`), `Cargo.toml` (через `resolveAllCargoManifests`),
|
|
96
|
+
перевірка існування `BASELINE_PATH` і `target` (через `existsSync`).
|
|
97
|
+
- Запис у файлову систему:
|
|
98
|
+
- Створення каталогу `.cargo/` у кожному cargo-крейті, де ще нема
|
|
99
|
+
`.cargo/mutants.toml` (`mkdir` з `recursive: true`).
|
|
100
|
+
- Копіювання `BASELINE_PATH` у `.cargo/mutants.toml` (`copyFile`).
|
|
101
|
+
- Виклики reporter (`pass` / `fail`) — у stdout/stderr формат залежить
|
|
102
|
+
від реалізації `createCheckReporter`.
|
|
103
|
+
|
|
104
|
+
## Залежності
|
|
105
|
+
|
|
106
|
+
### Стандартна бібліотека Node.js
|
|
107
|
+
|
|
108
|
+
- `node:fs` — `existsSync`.
|
|
109
|
+
- `node:fs/promises` — `copyFile`, `mkdir`.
|
|
110
|
+
- `node:path` — `dirname`, `join`, `relative`.
|
|
111
|
+
- `node:url` — `fileURLToPath` (для обчислення абсолютного шляху до
|
|
112
|
+
baseline через `import.meta.url`).
|
|
113
|
+
|
|
114
|
+
### Внутрішні модулі проєкту
|
|
115
|
+
|
|
116
|
+
- `../../../scripts/lib/check-reporter.mjs` — `createCheckReporter()`,
|
|
117
|
+
фабрика репортера; об'єкт із методами `pass(msg)`, `fail(msg)`,
|
|
118
|
+
`getExitCode()`.
|
|
119
|
+
- `../../../scripts/lib/read-n-cursor-config-lite.mjs` —
|
|
120
|
+
`readNCursorConfigLite(cwd)`, читає `.n-cursor.json` і повертає об'єкт із
|
|
121
|
+
полями `rules: string[]` і `disableRules: string[]`.
|
|
122
|
+
- `../../../scripts/utils/resolve-cargo-manifest.mjs` —
|
|
123
|
+
`resolveAllCargoManifests(cwd)`, повертає масив абсолютних шляхів до всіх
|
|
124
|
+
`Cargo.toml` у проєкті (cwd, workspace-члени, Tauri-патерн
|
|
125
|
+
`src-tauri/Cargo.toml`).
|
|
126
|
+
|
|
127
|
+
### Файлові ресурси
|
|
128
|
+
|
|
129
|
+
- `data/cargo_mutants_config/mutants.toml.baseline` (відносно теки модуля,
|
|
130
|
+
обчислюється через `HERE`) — canonical baseline `.cargo/mutants.toml`.
|
|
131
|
+
Має бути частиною дистрибутиву `@nitra/cursor`. Порожній файл з
|
|
132
|
+
коментарем, `cargo-mutants` використовує робочі defaults.
|
|
133
|
+
|
|
134
|
+
## Потік виконання / Використання
|
|
135
|
+
|
|
136
|
+
### Як викликається
|
|
137
|
+
|
|
138
|
+
Модуль експортує `check(cwd)`, яку диспетчер правила `test` (через
|
|
139
|
+
`test.mdc` / контрактний раннер `@nitra/cursor`) викликає під час перевірки
|
|
140
|
+
правил у проєкті. Можлива також CLI-сумісність — функція приймає
|
|
141
|
+
необов'язковий `cwd` і за замовчуванням використовує `process.cwd()`.
|
|
142
|
+
|
|
143
|
+
### Типовий сценарій
|
|
144
|
+
|
|
145
|
+
1. Користувач додає у `.n-cursor.json` правило `rust` (поза `disable-rules`).
|
|
146
|
+
2. Раннер виконує концерн `cargo_mutants_config` (через `check(cwd)`).
|
|
147
|
+
3. Концерн читає конфіг, проходить self-gate.
|
|
148
|
+
4. Концерн через `resolveAllCargoManifests` знаходить усі `Cargo.toml`
|
|
149
|
+
(cwd, workspace-члени, `src-tauri/Cargo.toml` для Tauri-проєктів).
|
|
150
|
+
5. Для кожного манифеста, що не має `.cargo/mutants.toml`, файл
|
|
151
|
+
створюється з canonical baseline; для тих, що вже мають — pass.
|
|
152
|
+
6. Раннер отримує exit-код `0` або `1` і агрегує його з рештою концернів.
|
|
153
|
+
|
|
154
|
+
### Сценарії silently skip
|
|
155
|
+
|
|
156
|
+
- `rust` не у `rules` — концерн не релевантний, скіп.
|
|
157
|
+
- `rust` у `disable-rules` — користувач свідомо вимкнув, скіп.
|
|
158
|
+
- Жодного `Cargo.toml` у проєкті — манифест ще не створено, скіп
|
|
159
|
+
(не помилка).
|
|
160
|
+
|
|
161
|
+
### Сценарій порушення (`exit 1`)
|
|
162
|
+
|
|
163
|
+
- canonical baseline `data/cargo_mutants_config/mutants.toml.baseline`
|
|
164
|
+
відсутній у дистрибутиві `@nitra/cursor` — інсталяція пакета зламана,
|
|
165
|
+
потрібно перевстановити.
|
|
166
|
+
|
|
167
|
+
### Внутрішні константи
|
|
168
|
+
|
|
169
|
+
- `HERE = dirname(fileURLToPath(import.meta.url))` — абсолютний шлях до
|
|
170
|
+
теки, де лежить цей `.mjs`.
|
|
171
|
+
- `BASELINE_PATH = join(HERE, 'data', 'cargo_mutants_config', 'mutants.toml.baseline')` —
|
|
172
|
+
абсолютний шлях до canonical baseline. Шлях відраховується від
|
|
173
|
+
розташування самого модуля, тому стабільний незалежно від `cwd`.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# location.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль реалізує перевірку правила `test.mdc` щодо **розміщення тестових файлів** у JS-кодовій базі.
|
|
6
|
+
|
|
7
|
+
Конвенція: усі файли з суфіксом `.test.mjs` повинні лежати у каталозі `tests/`, що розташований поряд із джерельним файлом. Тобто для джерела `dir/foo.mjs` правильне розташування тесту — `dir/tests/foo.test.mjs`, а не `dir/foo.test.mjs`.
|
|
8
|
+
|
|
9
|
+
Особливості:
|
|
10
|
+
|
|
11
|
+
- Виключено `*_test.rego` — Rego unit-тести, за конвенцією OPA community, лежать поряд із полісі.
|
|
12
|
+
- Обхід дерева через `walkDir` автоматично пропускає `node_modules`, `.git`, `dist`, `build`, `.venv`, `venv`.
|
|
13
|
+
- Додатково ігноруються шляхи з `.n-cursor.json:ignore` (через `loadCursorIgnorePaths`).
|
|
14
|
+
|
|
15
|
+
Файл експортує асинхронну функцію `check`, яка призначена для запуску з кореня репозиторію (зазвичай через runner правил у `npm/rules/`). Повертає exit-код (0 — успіх, 1 — порушення), формуючи звіт через `createCheckReporter`.
|
|
16
|
+
|
|
17
|
+
## Експорти / API
|
|
18
|
+
|
|
19
|
+
| Експорт | Тип | Опис |
|
|
20
|
+
| ------- | ---------------------------------------- | ------------------------------------------------------------ |
|
|
21
|
+
| `check` | `(cwdParam?: string) => Promise<number>` | Запуск перевірки розміщення `*.test.mjs`. Повертає exit-код. |
|
|
22
|
+
|
|
23
|
+
Внутрішні (не експортовані) допоміжні функції:
|
|
24
|
+
|
|
25
|
+
- `isTestFile(absPath: string): boolean`
|
|
26
|
+
- `isInsideTestsDir(absPath: string): boolean`
|
|
27
|
+
|
|
28
|
+
Внутрішня константа:
|
|
29
|
+
|
|
30
|
+
- `TESTS_DIR_NAME = 'tests'` — канонічна назва каталогу для тестів.
|
|
31
|
+
|
|
32
|
+
## Функції
|
|
33
|
+
|
|
34
|
+
### `isTestFile(absPath)`
|
|
35
|
+
|
|
36
|
+
- **Сигнатура:** `function isTestFile(absPath: string): boolean`
|
|
37
|
+
- **Параметри:**
|
|
38
|
+
- `absPath` — абсолютний (або відносний — функція не залежить від форми) шлях до файла.
|
|
39
|
+
- **Повертає:** `true`, якщо `basename(absPath)` закінчується на `.test.mjs`, інакше `false`.
|
|
40
|
+
- **Side effects:** немає; функція чиста.
|
|
41
|
+
- **Використання:** фільтрує лише JS-тести; інші розширення (`.test.ts`, `_test.rego` тощо) ігноруються.
|
|
42
|
+
|
|
43
|
+
### `isInsideTestsDir(absPath)`
|
|
44
|
+
|
|
45
|
+
- **Сигнатура:** `function isInsideTestsDir(absPath: string): boolean`
|
|
46
|
+
- **Параметри:**
|
|
47
|
+
- `absPath` — шлях до тестового файла.
|
|
48
|
+
- **Повертає:** `true`, якщо басенейм безпосередньої батьківської директорії дорівнює `tests` (значення `TESTS_DIR_NAME`).
|
|
49
|
+
- **Side effects:** немає; функція чиста.
|
|
50
|
+
- **Зауваження:** перевіряється саме безпосередній батько (`basename(dirname(absPath))`), а не наявність `tests/` будь-де у шляху. Тобто `pkg/tests/sub/foo.test.mjs` буде вважатися **поза** `tests/` (батько — `sub`, а не `tests`).
|
|
51
|
+
|
|
52
|
+
### `check(cwdParam = process.cwd())`
|
|
53
|
+
|
|
54
|
+
- **Сигнатура:** `export async function check(cwdParam?: string): Promise<number>`
|
|
55
|
+
- **Параметри:**
|
|
56
|
+
- `cwdParam` _(опціонально)_ — корінь репозиторію, з якого починається обхід. За замовчуванням — `process.cwd()`.
|
|
57
|
+
- **Повертає:** `Promise<number>` — exit-код від `reporter.getExitCode()`. За домовленістю з `createCheckReporter`: `0` — порушень немає, `1` — є порушення.
|
|
58
|
+
- **Side effects:**
|
|
59
|
+
- Читання файлової системи: обхід дерева від `cwd` через `walkDir`.
|
|
60
|
+
- Читання конфігурації: `.n-cursor.json` через `loadCursorIgnorePaths`.
|
|
61
|
+
- Запис у `stdout` / `stderr`: повідомлення про `pass` / `fail` через `createCheckReporter` (формат повідомлень визначається репортером).
|
|
62
|
+
- **Алгоритм:**
|
|
63
|
+
1. Створити репортер: `const reporter = createCheckReporter()`, дістати `pass` і `fail`.
|
|
64
|
+
2. Завантажити список ігнорованих шляхів: `ignorePaths = await loadCursorIgnorePaths(cwd)`.
|
|
65
|
+
3. Ініціалізувати лічильник `totalTests = 0` та масив `offenders: string[]`.
|
|
66
|
+
4. Обійти дерево `walkDir(cwd, visitor, ignorePaths)`. Для кожного файла:
|
|
67
|
+
- Якщо `!isTestFile(absPath)` — пропустити.
|
|
68
|
+
- Інакше `totalTests++`.
|
|
69
|
+
- Якщо `!isInsideTestsDir(absPath)` — додати **відносний** до `cwd` шлях у `offenders` (через `relative(cwd, absPath)`).
|
|
70
|
+
5. Якщо `offenders.length === 0` — викликати `pass('Всі ${totalTests} файлів *.test.mjs у каталозі tests/ (test.mdc)')` і повернути `reporter.getExitCode()`.
|
|
71
|
+
6. Інакше — для кожного `offenderPath`:
|
|
72
|
+
- Обчислити `parentDir = dirname(offenderPath)` і `base = basename(offenderPath)`.
|
|
73
|
+
- Викликати `fail` з повідомленням-підказкою: тест має лежати у `tests/` — рекомендований шлях `${parentDir}/tests/${base}` (з посиланням на правило `test.mdc`).
|
|
74
|
+
7. Повернути `reporter.getExitCode()`.
|
|
75
|
+
|
|
76
|
+
## Залежності
|
|
77
|
+
|
|
78
|
+
### Стандартна бібліотека Node.js
|
|
79
|
+
|
|
80
|
+
- `node:path` — функції `basename`, `dirname`, `relative`.
|
|
81
|
+
|
|
82
|
+
### Внутрішні модулі репозиторію (відносні шляхи від `npm/rules/test/js/location.mjs`)
|
|
83
|
+
|
|
84
|
+
- `../../../scripts/lib/check-reporter.mjs` — фабрика репортерів `createCheckReporter()`, що повертає об'єкт з методами `pass(msg)`, `fail(msg)` та `getExitCode()`.
|
|
85
|
+
- `../../../scripts/lib/load-cursor-config.mjs` — `loadCursorIgnorePaths(cwd)`: повертає список глобів/шляхів, які слід пропустити при обході (зчитується з `.n-cursor.json`, ключ `ignore`).
|
|
86
|
+
- `../../../scripts/utils/walkDir.mjs` — асинхронний рекурсивний обхід дерева: `walkDir(rootDir, visitor, ignorePaths)`; за замовчуванням пропускає `node_modules`, `.git`, `dist`, `build`, `.venv`, `venv`.
|
|
87
|
+
|
|
88
|
+
### Зовнішні правила (концептуальні залежності)
|
|
89
|
+
|
|
90
|
+
- `test.mdc` — правило, що формалізує конвенцію розміщення тестів і на яке посилаються повідомлення `pass`/`fail`.
|
|
91
|
+
|
|
92
|
+
## Потік виконання / Використання
|
|
93
|
+
|
|
94
|
+
### Типове використання як перевірка правила
|
|
95
|
+
|
|
96
|
+
Файл є частиною системи перевірок (`npm/rules/test/js/`). Зазвичай викликається runner'ом, який знає про expor `check`:
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
import { check } from './location.mjs'
|
|
100
|
+
|
|
101
|
+
process.exit(await check())
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
або з явним коренем:
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
const code = await check('/path/to/repo')
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Послідовність виконання
|
|
111
|
+
|
|
112
|
+
1. Виклик `check()` (опційно з `cwdParam`).
|
|
113
|
+
2. Створення репортера → завантаження `ignorePaths`.
|
|
114
|
+
3. Однопрохідний обхід дерева від `cwd`:
|
|
115
|
+
- на кожному кроці фільтр `isTestFile`,
|
|
116
|
+
- інкремент `totalTests`,
|
|
117
|
+
- перевірка `isInsideTestsDir`, накопичення `offenders`.
|
|
118
|
+
4. Звіт:
|
|
119
|
+
- якщо `offenders.length === 0` — один `pass` з підсумком `totalTests`;
|
|
120
|
+
- інакше — `fail` для кожного порушника з підказкою куди перенести.
|
|
121
|
+
5. Повернення exit-коду (`0` або `1`).
|
|
122
|
+
|
|
123
|
+
### Граничні випадки
|
|
124
|
+
|
|
125
|
+
- **Немає жодного `*.test.mjs`:** `totalTests === 0`, `offenders === []` — буде успішний `pass('Всі 0 файлів *.test.mjs у каталозі tests/ (test.mdc)')`.
|
|
126
|
+
- **Тест у вкладеному каталозі `tests/`:** наприклад, `dir/tests/sub/foo.test.mjs` — буде вважатися порушенням, бо безпосередній батько — `sub`, а не `tests`.
|
|
127
|
+
- **Файли з частковим суфіксом:** `foo.test.mjs.bak` — не вважається тестом (не закінчується на `.test.mjs`).
|
|
128
|
+
- **Шляхи з `.n-cursor.json:ignore`** не відвідуються `walkDir` і не впливають на лічильники.
|
|
129
|
+
|
|
130
|
+
### Rebuild Test
|
|
131
|
+
|
|
132
|
+
Для верифікації коректності документації — мисленний rebuild:
|
|
133
|
+
|
|
134
|
+
- Дано: дерево з файлами `a/foo.test.mjs`, `a/tests/bar.test.mjs`, `b/baz.test.mjs`.
|
|
135
|
+
- Очікувано: `totalTests === 3`; `offenders === ['a/foo.test.mjs', 'b/baz.test.mjs']`; два виклики `fail`; exit-код `1`.
|
|
136
|
+
- Повідомлення `fail` для `a/foo.test.mjs` міститимуть підказку: `a/foo.test.mjs: тест має лежати у tests/ — перенеси у a/tests/foo.test.mjs (test.mdc)`.
|