@nitra/cursor 12.8.6 → 12.8.7
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/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/rules/adr/js/hooks.mdc +32 -0
- package/rules/adr/js/madr_format.mdc +96 -0
- package/rules/adr/js/settings_policy.mdc +34 -0
- package/rules/adr/main.mdc +13 -95
- package/rules/bun/js/bunfig.mdc +12 -0
- package/rules/bun/js/layout.mdc +60 -0
- package/rules/bun/js/lint.mdc +9 -0
- package/rules/bun/js/package_json.mdc +19 -0
- package/rules/bun/main.mdc +9 -61
- package/rules/capacitor/js/ios_spm.mdc +69 -0
- package/rules/capacitor/js/version.mdc +29 -0
- package/rules/capacitor/main.mdc +8 -22
- package/rules/changelog/js/agent-workflow.mdc +15 -0
- package/rules/changelog/js/changelog-format.mdc +33 -0
- package/rules/changelog/js/comparison-models.mdc +40 -0
- package/rules/changelog/main.mdc +4 -98
- package/rules/ci4/js/marksman_config.mdc +31 -0
- package/rules/ci4/js/vscode_extensions.mdc +33 -0
- package/rules/ci4/main.mdc +14 -14
- package/rules/docker/js/compile.mdc +44 -0
- package/rules/docker/js/hadolint.mdc +50 -0
- package/rules/docker/js/mirror.mdc +13 -0
- package/rules/docker/js/multistage.mdc +13 -0
- package/rules/docker/js/native-addon.mdc +43 -0
- package/rules/docker/js/nginx-tag.mdc +7 -0
- package/rules/docker/js/nginx-user.mdc +37 -0
- package/rules/docker/js/non-root.mdc +39 -0
- package/rules/docker/main.mdc +15 -196
- package/rules/ga/js/lint_toolchain.mdc +15 -0
- package/rules/ga/js/required_workflows.mdc +35 -0
- package/rules/ga/js/vscode.mdc +17 -0
- package/rules/ga/js/workflow_common.mdc +108 -0
- package/rules/ga/js/workflows.mdc +32 -0
- package/rules/ga/js/zizmor.mdc +7 -0
- package/rules/ga/main.mdc +17 -125
- package/rules/graphql/js/tooling.mdc +13 -0
- package/rules/graphql/js/vscode_extensions.mdc +13 -0
- package/rules/graphql/main.mdc +3 -22
- package/rules/hasura/js/internal_urls.mdc +27 -0
- package/rules/hasura/js/migrations.mdc +13 -0
- package/rules/hasura/js/svc_hl.mdc +17 -0
- package/rules/hasura/main.mdc +8 -30
- package/rules/image-avif/js/avif_generation.mdc +26 -0
- package/rules/image-avif/js/package_json_optout.mdc +21 -0
- package/rules/image-avif/main.mdc +7 -34
- package/rules/image-compress/js/package_json.mdc +7 -0
- package/rules/image-compress/js/package_setup.mdc +13 -0
- package/rules/image-compress/main.mdc +4 -12
- package/rules/js/docs/index.md +3 -3
- package/rules/js/js/dep-policy.mdc +17 -0
- package/rules/js/js/eslint-config.mdc +28 -0
- package/rules/js/js/extensions.mdc +8 -0
- package/rules/js/js/file-extensions.mdc +12 -0
- package/rules/js/js/for-in.mdc +26 -0
- package/rules/js/js/jscpd.mdc +42 -0
- package/rules/js/js/knip.mdc +15 -0
- package/rules/js/js/lint-js-workflow.mdc +58 -0
- package/rules/js/js/oxlintrc.mdc +20 -0
- package/rules/js/js/package-json.mdc +31 -0
- package/rules/js/js/tests.mdc +9 -0
- package/rules/js/js/utils-lib-structure.mdc +15 -0
- package/rules/js/main.mdc +21 -214
- package/rules/js-bun-db/js/bun-sql-migration.mdc +15 -0
- package/rules/js-bun-db/js/connection.mdc +42 -0
- package/rules/js-bun-db/js/pg-format-identifiers.mdc +102 -0
- package/rules/js-bun-db/js/pg-format-shim.mdc +99 -0
- package/rules/js-bun-db/js/pg-leftover.mdc +27 -0
- package/rules/js-bun-db/js/pg-listen-notify.mdc +51 -0
- package/rules/js-bun-db/js/query-safety.mdc +117 -0
- package/rules/js-bun-db/js/sql-array.mdc +88 -0
- package/rules/js-bun-db/js/unsafe.mdc +65 -0
- package/rules/js-bun-db/main.mdc +15 -605
- package/rules/js-bun-redis/js/imports.mdc +47 -0
- package/rules/js-bun-redis/js/package_json.mdc +44 -0
- package/rules/js-bun-redis/main.mdc +3 -11
- package/rules/js-mssql/js/mssql-in-list.mdc +38 -0
- package/rules/js-mssql/js/mssql-pool.mdc +56 -0
- package/rules/js-mssql/js/mssql-query-template.mdc +33 -0
- package/rules/js-mssql/js/mssql-tvp.mdc +75 -0
- package/rules/js-mssql/js/mssql-version.mdc +7 -0
- package/rules/js-mssql/main.mdc +10 -198
- package/rules/js-run/js/check-env.mdc +35 -0
- package/rules/js-run/js/conn-aliases.mdc +109 -0
- package/rules/js-run/js/jsconfig.mdc +20 -0
- package/rules/js-run/js/otel-configmap.mdc +6 -0
- package/rules/js-run/js/pino.mdc +6 -0
- package/rules/js-run/js/project-structure.mdc +11 -0
- package/rules/js-run/js/runtime.mdc +14 -0
- package/rules/js-run/js/scope.mdc +11 -0
- package/rules/js-run/js/settimeout.mdc +11 -0
- package/rules/js-run/js/temporal.mdc +5 -0
- package/rules/js-run/main.mdc +16 -218
- package/rules/k8s/js/configmap.mdc +41 -0
- package/rules/k8s/js/deployment_resources.mdc +49 -0
- package/rules/k8s/js/hasura_httproute.mdc +91 -0
- package/rules/k8s/js/hpa_apiversion.mdc +27 -0
- package/rules/k8s/js/ingress_gateway.mdc +16 -0
- package/rules/k8s/js/kustomize_structure.mdc +144 -0
- package/rules/k8s/js/lint_k8s.mdc +72 -0
- package/rules/k8s/js/multidoc_yaml.mdc +5 -0
- package/rules/k8s/js/network_policy.mdc +136 -0
- package/rules/k8s/js/schema_modeline.mdc +57 -0
- package/rules/k8s/js/service.mdc +44 -0
- package/rules/k8s/js/topology_hpa_pdb.mdc +181 -0
- package/rules/k8s/main.mdc +30 -843
- package/rules/nginx-default-tpl/js/dockerfile.mdc +36 -0
- package/rules/nginx-default-tpl/js/http-route.mdc +41 -0
- package/rules/nginx-default-tpl/js/ini-keys.mdc +21 -0
- package/rules/nginx-default-tpl/js/template-structure.mdc +86 -0
- package/rules/nginx-default-tpl/js/vscode.mdc +37 -0
- package/rules/nginx-default-tpl/main.mdc +6 -112
- package/rules/npm-module/js/docs/index.md +5 -5
- package/rules/npm-module/js/docs/rule_meta.md +6 -6
- package/rules/npm-module/js/docs/skill_meta.md +8 -8
- package/rules/npm-module/js/header_doc_pointer.mdc +18 -0
- package/rules/npm-module/js/package_structure.mdc +62 -0
- package/rules/npm-module/js/rule_meta.mdc +11 -0
- package/rules/npm-module/js/skill_meta.mdc +11 -0
- package/rules/npm-module/main.mdc +10 -55
- package/rules/php/js/lint_php_yml.mdc +12 -0
- package/rules/php/js/tooling.mdc +66 -0
- package/rules/php/main.mdc +7 -66
- package/rules/python/js/lint_python_yml.mdc +23 -0
- package/rules/python/js/pyproject_toml.mdc +32 -0
- package/rules/python/js/tooling.mdc +23 -0
- package/rules/python/main.mdc +9 -33
- package/rules/rego/js/rego-lint.mdc +31 -0
- package/rules/rego/js/vscode_extensions.mdc +11 -0
- package/rules/rego/js/vscode_settings.mdc +13 -0
- package/rules/rego/main.mdc +8 -24
- package/rules/rust/js/coverage.mdc +28 -0
- package/rules/rust/js/lint.mdc +22 -0
- package/rules/rust/js/tauri_composition.mdc +8 -0
- package/rules/rust/js/vscode_extensions.mdc +12 -0
- package/rules/rust/main.mdc +8 -38
- package/rules/security/js/rego_policies.mdc +15 -0
- package/rules/security/js/sample_secret.mdc +19 -0
- package/rules/security/js/trufflehog.mdc +21 -0
- package/rules/security/main.mdc +7 -35
- package/rules/style/js/admin-table.mdc +88 -0
- package/rules/style/js/colors.mdc +21 -0
- package/rules/style/js/gap.mdc +22 -0
- package/rules/style/js/quasar-fixes.mdc +32 -0
- package/rules/style/js/quasar.mdc +7 -0
- package/rules/style/js/tooling.mdc +85 -0
- package/rules/style/main.mdc +13 -253
- package/rules/tauri/js/cargo_mutants_config.mdc +39 -0
- package/rules/tauri/js/tool_surface.mdc +21 -0
- package/rules/tauri/js/tooling.mdc +25 -0
- package/rules/tauri/main.mdc +8 -78
- package/rules/test/js/cargo_mutants_config.mdc +18 -0
- package/rules/test/js/docs/index.md +7 -7
- package/rules/test/js/location.mdc +52 -0
- package/rules/test/js/no-console-store-restore.mdc +11 -0
- package/rules/test/js/no-process-chdir.mdc +15 -0
- package/rules/test/js/no-relative-fs-path.mdc +22 -0
- package/rules/test/js/sandbox-aware-test.mdc +28 -0
- package/rules/test/js/stryker_config.mdc +26 -0
- package/rules/test/js/vitest-config-pool-forks.mdc +33 -0
- package/rules/test/main.mdc +18 -184
- package/rules/text/js/ci-lint-text.mdc +15 -0
- package/rules/text/js/cspell.mdc +81 -0
- package/rules/text/js/dotenv-linter.mdc +16 -0
- package/rules/text/js/forbidden-prettier.mdc +13 -0
- package/rules/text/js/markdownlint.mdc +25 -0
- package/rules/text/js/oxfmt.mdc +35 -0
- package/rules/text/js/package-json.mdc +26 -0
- package/rules/text/js/shellcheck.mdc +18 -0
- package/rules/text/js/v8r.mdc +23 -0
- package/rules/text/js/vscode.mdc +86 -0
- package/rules/text/main.mdc +20 -237
- package/rules/vue/js/composition-api.mdc +82 -0
- package/rules/vue/js/nheader-layout.mdc +171 -0
- package/rules/vue/js/node-imports.mdc +25 -0
- package/rules/vue/js/quasar-ui.mdc +32 -0
- package/rules/vue/js/structure.mdc +101 -0
- package/rules/vue/js/testing.mdc +32 -0
- package/rules/vue/js/tfm-translations.mdc +26 -0
- package/rules/vue/js/vite-config.mdc +126 -0
- package/rules/vue/js/vite-env.mdc +55 -0
- package/rules/vue/js/vue-imports.mdc +25 -0
- package/rules/vue/main.mdc +16 -640
- package/scripts/docs/index.md +16 -16
- package/scripts/lib/docs/index.md +36 -36
- package/scripts/utils/docs/index.md +14 -14
package/rules/test/main.mdc
CHANGED
|
@@ -5,206 +5,40 @@ globs: "**/{.n-cursor.json,package.json,Cargo.toml,stryker.config.mjs,vitest.con
|
|
|
5
5
|
alwaysApply: false
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Правило **test** керує розміщенням тестових файлів, безпекою ізоляції тестів (заборона `process.chdir`, відносних шляхів у FS, ручного store/restore console), налаштуванням Vitest/Stryker baseline та конфігурацією cargo-mutants для Rust.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
[test-location](./js/location.mdc)
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
rules/foo/js/bar/
|
|
14
|
-
├── check.mjs ← JS-джерело
|
|
15
|
-
└── tests/
|
|
16
|
-
└── check.test.mjs ← тест check.mjs
|
|
17
|
-
```
|
|
12
|
+
[test-no-process-chdir](./js/no-process-chdir.mdc)
|
|
18
13
|
|
|
19
|
-
|
|
14
|
+
[test-no-relative-fs-path](./js/no-relative-fs-path.mdc)
|
|
20
15
|
|
|
21
|
-
-
|
|
22
|
-
- Один погляд на дерево показує, що піде у npm tarball (все, крім `tests/` і `__fixtures__/` — їх ловить `package.json#files` через негативні globs).
|
|
23
|
-
- Якщо тест переростає у мульти-файловий — він уже у власному каталозі, без хаотичного розкидання.
|
|
16
|
+
[test-no-console-store-restore](./js/no-console-store-restore.mdc)
|
|
24
17
|
|
|
25
|
-
|
|
18
|
+
[test-sandbox-aware-test](./js/sandbox-aware-test.mdc)
|
|
26
19
|
|
|
27
|
-
|
|
28
|
-
rules/foo/policy/package_json/
|
|
29
|
-
├── package_json.rego ← Rego-правило
|
|
30
|
-
├── package_json_test.rego ← Rego unit-тести (поряд, не у tests/)
|
|
31
|
-
└── target.json
|
|
32
|
-
```
|
|
20
|
+
[test-vitest-config-pool-forks](./js/vitest-config-pool-forks.mdc)
|
|
33
21
|
|
|
34
|
-
|
|
22
|
+
[test-stryker-config](./js/stryker_config.mdc)
|
|
35
23
|
|
|
36
|
-
-
|
|
37
|
-
- **Fixtures**: `__fixtures__/` (для shared) і `fixtures/` (для rule-specific) також живуть **усередині `tests/`**: `tests/__fixtures__/...` і `tests/fixtures/...`.
|
|
38
|
-
- **Test helpers** (`test-helpers.mjs`) можуть лишатися у `scripts/utils/` як shared infra — конвенція стосується файлів `*.test.mjs`.
|
|
39
|
-
|
|
40
|
-
**Гарантії tarball-чистоти** через `package.json#files`:
|
|
41
|
-
|
|
42
|
-
```json
|
|
43
|
-
"files": [
|
|
44
|
-
"...",
|
|
45
|
-
"!**/*.test.mjs",
|
|
46
|
-
"!**/*_test.rego",
|
|
47
|
-
"!**/__fixtures__/**",
|
|
48
|
-
"!**/fixtures/**",
|
|
49
|
-
"!**/test-helpers.mjs"
|
|
50
|
-
]
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Recursive globs ловлять файли всередині `tests/` так само, як ловили б їх біля джерела.
|
|
54
|
-
|
|
55
|
-
## Що перевіряє правило
|
|
56
|
-
|
|
57
|
-
`npx @nitra/cursor fix test` (concern `location`) проходить деревом пакета й перевіряє: **кожен `*.test.mjs` файл лежить усередині каталогу з ім'ям `tests`** (точне співпадіння басенейму батьківської директорії). Файли поза цим каталогом репортуються з порадою куди їх перенести.
|
|
58
|
-
|
|
59
|
-
`*_test.rego` перевіркою **не охоплюються** — вони не переміщуються.
|
|
60
|
-
|
|
61
|
-
Пропускаються: `node_modules`, `.git`, `dist`, `build`, `.venv`, `venv`, шляхи з `.n-cursor.json:ignore`.
|
|
62
|
-
|
|
63
|
-
## Заборона `process.chdir` у тестах
|
|
64
|
-
|
|
65
|
-
`process.chdir(dir)` — **process-wide** мутація. Vitest за замовчуванням ставить `pool: 'threads'`, і всі workers ділять один процес: паралельний test file може перехопити cwd сусіда посеред FS- або `git`-операції. У реальному інциденті це призвело до того, що `git init`+`git commit` із tmp-фікстури потрапив у реальний робочий репозиторій і створив rogue commit з автором `test <test@test>`, знищивши `npm/package.json` / `CHANGELOG.md`.
|
|
66
|
-
|
|
67
|
-
Тому:
|
|
68
|
-
|
|
69
|
-
- **У тестах заборонено** `process.chdir(...)` напряму та будь-які хелпери, що його викликають (історичний `withTmpCwd` видалений у `1.28.0`).
|
|
70
|
-
- Канон: `withTmpDir(async dir => { ... })` зі `scripts/utils/test-helpers.mjs` — створює tmpdir і передає **абсолютний** `dir` у callback **без** `process.chdir`.
|
|
71
|
-
- Усі FS-операції у тесті — через `join(dir, …)` і `writeJson(join(dir, …), …)` / `ensureDir(join(dir, …))` (хелпери валідують `isAbsolute`).
|
|
72
|
-
- Усі child-процеси — `execFile(bin, args, { cwd: dir })`, `spawnSync(bin, args, { cwd: dir })`.
|
|
73
|
-
- Concern-функції правил — `await check(dir)`, `await applies(dir)`, `await fix(dir)`; усі production функції приймають перший параметр `cwd = process.cwd()` (default зберігає CLI-сумісність).
|
|
74
|
-
- `vitest.config.mjs` (або legacy `vitest.config.js`) додатково ставить `pool: 'forks'` як defense-in-depth: навіть якщо хтось пропустить правило вище, fork-ізоляція не дасть race у production tree.
|
|
75
|
-
|
|
76
|
-
Це **обов'язково** і для тестів пакета `@nitra/cursor`, і для кожного проєкту-споживача. Триплет перевірок:
|
|
77
|
-
|
|
78
|
-
- **`no-process-chdir`** (`rules/test/js/no-process-chdir.mjs`) — сканує `**/*.test.{js,mjs}` і падає з ❌ на будь-яке вживання `process.chdir(`.
|
|
79
|
-
- **`no-relative-fs-path`** (`rules/test/js/no-relative-fs-path.mjs`) — AST-сканер (`oxc-parser`): знаходить виклики FS-функцій із `node:fs`/`node:fs/promises` (`writeFile`, `copyFile`, `mkdir`, `readFile`, `existsSync`, `rename`, `symlink`, `cp`, … включно з `*Sync`-варіантами та `writeJson`/`ensureDir`-хелперами), де path-аргумент — це **string literal** без префікса `/`, `\`, `file:`, `http(s):`, `data:`, чи Windows-disk-letter `C:\`. Виклики `copyFile`/`rename`/`symlink`/`link`/`cp` перевіряють обидва path-аргументи. Виклики з обчисленим path (`join(dir, …)`, змінна, template-literal з виразом) пропускаються. Виловив би інцидент v1.28.0 у `tests/check-rule-fixtures.test.mjs` (`copyFile(src, 'default.conf.template')` → файл у production tree).
|
|
80
|
-
- **`vitest-config-pool-forks`** (`rules/test/js/vitest-config-pool-forks.mjs`) — substring-перевірка `pool: 'forks'` у `vitest.config.mjs` (або legacy `vitest.config.js`; `.mjs` пріоритетніший). Defense-in-depth.
|
|
81
|
-
|
|
82
|
-
Canonical `vitest.config.mjs` (для довідки — `pool: 'forks'` + `include` + `coverage`) — у `rules/test/js/data/vitest_config/vitest.config.baseline.js` (концерн `stryker_config` копіює його у кожен JS-root; нові файли — `.mjs`, наявний `vitest.config.js` лишається валідним і не дублюється).
|
|
83
|
-
|
|
84
|
-
## Console mocking у тестах
|
|
85
|
-
|
|
86
|
-
`console.log` / `console.error` / `console.warn` — **process-wide** мутації, рівно як `process.cwd()`. Якщо тест перехоплює їх через `const orig = console.log; console.log = (...) => …; try { … } finally { console.log = orig }`, паралельний test файл у `pool: 'forks'` (різний процес) ізольований, але в межах **одного** процесу всі тести у файлі ділять єдиний `console`-об'єкт. Якщо try/finally не виконається (наприклад, асинхронний помилка повз `await`), restore не спрацює і наступні тести втрачають вивід.
|
|
87
|
-
|
|
88
|
-
Тому:
|
|
89
|
-
|
|
90
|
-
- **Канон**: `vi.spyOn(console, 'log').mockReturnValue()` (та аналоги для `error`/`warn`/`info`) + `afterEach(() => vi.restoreAllMocks())`. Vitest сам слідкує за scope mock-у і гарантовано відновлює оригінал між тестами, навіть якщо тест кинув виняток.
|
|
91
|
-
- **Заборонено**: ручний store/restore (`const orig = console.log; console.log = stub`). Виняток — коли тест **необхідно** перехопити вивід **до** того, як завантажиться модуль із top-level-логуванням; у цьому випадку фіксуй pattern explicit-коментарем.
|
|
92
|
-
- Якщо потрібен лог зі stub-ом — `const logs = []; vi.spyOn(console, 'log').mockImplementation((...args) => logs.push(args.join(' ')))`.
|
|
93
|
-
|
|
94
|
-
Перевірка — концерн `no-console-store-restore` (`rules/test/js/no-console-store-restore.mjs`): AST-сканер, який ловить присвоєння `console.<method> = …` у `*.test.{js,mjs}`.
|
|
95
|
-
|
|
96
|
-
## Sandbox-aware тести (Stryker)
|
|
97
|
-
|
|
98
|
-
Stryker за замовчуванням копіює репо у sandbox-каталог (`reports/stryker/.tmp/sandbox-XXX/`), щоб AST-патчити мутантів без зачеплення робочого дерева. У sandbox **немає `.git/`**, і будь-який тест, що звертається до **реального** git-дерева через `import.meta.url + N≥3 рівнів вгору` (типу `const REPO_ROOT = join(import.meta.dirname, '..', '..', '..', '..', '..')`) фактично адресує sandbox-корінь, а не справжній репо. Це ламає `git rev-parse`/`git ls-files`/перевірки `.n-cursor.json`/CHANGELOG-співставлення — Stryker dry-run падає, мутаційний прогон не стартує, `mutation.json` лишається stale.
|
|
99
|
-
|
|
100
|
-
**Канон**: тест не повинен залежати від реального git-дерева через `import.meta.url + N≥3 рівнів вгору`. Якщо тест перевіряє git-логіку — ізолюй **повністю**:
|
|
101
|
-
|
|
102
|
-
```js
|
|
103
|
-
await withTmpDir(async dir => {
|
|
104
|
-
execFileSync('git', ['init', '-q', '--initial-branch=main'], { cwd: dir })
|
|
105
|
-
await writeFile(join(dir, 'a.sh'), '#!/bin/sh\n', 'utf8')
|
|
106
|
-
execFileSync('git', ['add', '-A'], { cwd: dir })
|
|
107
|
-
expect(listShellScriptPaths(dir)).toEqual(['a.sh'])
|
|
108
|
-
})
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
Приклад у репо: `rules/text/lint/tests/run-shellcheck.test.mjs` — тест `listShellScriptPaths всередині git-репо`.
|
|
112
|
-
|
|
113
|
-
**Виняток** — top-level smoke-аудит тести, що **за визначенням** перевіряють інваріанти **живого** репо (структура файлів, узгодженість CHANGELOG з `package.json`, кожне правило має fixture). Приклади: `tests/integration-repo-checks.test.mjs`, `tests/check-rule-fixtures.test.mjs`. Такі тести **не дають додаткового coverage у unit-сенсі** — концерни, які вони перевіряють, уже покриті per-rule unit-тестами (`rules/<rule>/js/tests/`). Захищаємо їх `test.skipIf(env.STRYKER_MUTATOR_WORKER)` від запуску всередині Stryker-sandbox:
|
|
114
|
-
|
|
115
|
-
```js
|
|
116
|
-
import { env } from 'node:process'
|
|
117
|
-
|
|
118
|
-
test.skipIf(env.STRYKER_MUTATOR_WORKER)('узгоджені з поточним деревом', async () => {
|
|
119
|
-
// …перевірки на REPO_ROOT
|
|
120
|
-
})
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Перевірка — концерн `sandbox-aware-test` (`rules/test/js/sandbox-aware-test.mjs`): сканер `*.test.{js,mjs}`, який знаходить тести з `import.meta.url`-deep-relative навігацією (4+ `..`-рівнів) і вимагає або `withTmpDir` у їхніх `test`-блоках, або `test.skipIf(env.STRYKER_MUTATOR_WORKER)`.
|
|
124
|
-
|
|
125
|
-
## `child_process` у тестах: явний `cwd`
|
|
126
|
-
|
|
127
|
-
Уже зазначено в `## Заборона process.chdir у тестах`. Підсилення: **кожен** виклик `execFile`/`execFileSync`/`spawn`/`spawnSync` у `*.test.{js,mjs}` приймає або **явний `{ cwd: dir }`** (де `dir` — змінна, не `process.cwd()`), або працює з **абсолютними шляхами** до бінарників/fixture-файлів (наприклад `spawnSync('node', [absoluteFixturePath])`). Це гарантує, що мутаційний test-flow у `pool: 'forks'` не страждає від implicit-`process.cwd()`-stride між тестами в одному воркері.
|
|
24
|
+
[test-cargo-mutants-config](./js/cargo_mutants_config.mdc)
|
|
128
25
|
|
|
129
26
|
## Покриття + мутаційне тестування
|
|
130
27
|
|
|
131
|
-
Канонічна команда — `n-cursor coverage`: збирає метрики покриття (`vitest run --coverage`, `cargo llvm-cov` тощо) і мутаційного тестування (Stryker з vitest-runner + `coverageAnalysis: 'perTest'`, `cargo-mutants`) з усіх активних провайдерів у `.n-cursor.json#rules` і пише `COVERAGE.md` у корінь проєкту.
|
|
28
|
+
Канонічна команда — `n-cursor coverage`: збирає метрики покриття (`vitest run --coverage`, `cargo llvm-cov` тощо) і мутаційного тестування (Stryker з vitest-runner + `coverageAnalysis: 'perTest'`, `cargo-mutants`) з усіх активних провайдерів у `.n-cursor.json#rules` і пише `COVERAGE.md` у корінь проєкту.
|
|
132
29
|
|
|
133
|
-
**Scoped-режим `--changed`:** `n-cursor coverage --changed` звужує scope до файлів, змінених від git merge-base поточної гілки з `main` або `origin/main
|
|
30
|
+
**Scoped-режим `--changed`:** `n-cursor coverage --changed` звужує scope до файлів, змінених від git merge-base поточної гілки з `main` або `origin/main`. У changed-режимі `COVERAGE.md` **не** перезаписується і LLM-класифікація не запускається.
|
|
134
31
|
|
|
135
|
-
Провайдери живуть у `npm/rules/<rule>/coverage/coverage.mjs
|
|
32
|
+
Провайдери живуть у `npm/rules/<rule>/coverage/coverage.mjs`. Оркестратор — у `npm/rules/test/coverage/coverage.mjs`.
|
|
136
33
|
|
|
137
|
-
У `package.json` (корінь) має бути `scripts.coverage` із викликом `n-cursor coverage
|
|
138
|
-
|
|
139
|
-
Канон `scripts.coverage` (substring requirement): [package.json.contains.json](./policy/package_json/template/package.json.contains.json)
|
|
34
|
+
У `package.json` (корінь) має бути `scripts.coverage` із викликом `n-cursor coverage`.
|
|
140
35
|
|
|
141
36
|
### Multi-workspace iteration
|
|
142
37
|
|
|
143
|
-
У monorepo `n-cursor coverage` ітерує усі workspaces з власним `package.json`
|
|
144
|
-
|
|
145
|
-
## Налаштування mutation-testing
|
|
146
|
-
|
|
147
|
-
Якщо у `.n-cursor.json#rules` присутнє правило `js` — правило `test` створює canonical baseline `stryker.config.mjs` + `vitest.config.mjs` у **кожному** JS-root проєкту: у кожному workspace з власним `package.json` (або в корені для single-package). У monorepo з `workspaces: ['app', 'scripts']` отримаєте `app/stryker.config.mjs` + `app/vitest.config.mjs` і `scripts/stryker.config.mjs` + `scripts/vitest.config.mjs`. Якщо у JS-root уже лежить legacy `vitest.config.js` — він лишається валідним, новий `.mjs` поряд не створюється, а `vitest.configFile` у скопійованому `stryker.config.mjs` приводиться до фактичного імені.
|
|
148
|
-
|
|
149
|
-
Канон Stryker config (Vitest runner + perTest): [stryker.config.baseline.mjs](./js/data/stryker_config/stryker.config.baseline.mjs)
|
|
150
|
-
|
|
151
|
-
### Vue SFC (`<script setup>` macros)
|
|
152
|
-
|
|
153
|
-
Якщо у JS-root знайдено бодай один `.vue` під `src/` (skip `node_modules`/`dist`/`reports`) — концерн ставить **vue-варіант** baseline ([`stryker.config.vue.baseline.mjs`](./js/data/stryker_config/stryker.config.vue.baseline.mjs)) замість звичайного і додатково копіює локальний Stryker `Ignore`-плагін [`stryker-vue-macros-ignorer.mjs`](./js/data/stryker_config/stryker-vue-macros-ignorer.mjs) поряд із конфігом.
|
|
154
|
-
|
|
155
|
-
Плагін реєструється як `plugins: ['@stryker-mutator/vitest-runner', './stryker-vue-macros-ignorer.mjs']` + `ignorers: ['vue-macros']` і виключає з мутацій виклики `<script setup>`-макросів: **`defineProps`**, **`defineEmits`**, **`defineModel`**, **`defineSlots`**, **`defineExpose`**, **`defineOptions`**. Без плагіна Stryker огортає аргументи макроса у coverage-тернарник (`stryMutAct_9fa48(...) ? {} : (stryCov_9fa48(...), {...})`), а `@vue/compiler-sfc` падає з `defineProps() in <script setup> cannot reference locally declared variables` — макроси мають бути статично-аналізованими на етапі compile-sfc. Альтернатива з boilerplate `// Stryker disable next-line` у кожному SFC не масштабується.
|
|
156
|
-
|
|
157
|
-
JS-root без `.vue` отримує дефолтний baseline без `plugins`/`ignorers` (backward-compatible). Обидва файли копіюються idempotent — наявний `stryker.config.mjs` / `stryker-vue-macros-ignorer.mjs` не перетирається.
|
|
158
|
-
|
|
159
|
-
**Augment існуючого config.** Якщо у Vue JS-root `stryker.config.mjs` **уже лежить** (наприклад, після апгрейду з версії без Vue-підтримки — `ensureBaselineFile` його idempotent-skip-ить і baseline-секцій `plugins`/`ignorers` він мовчки не отримує, а Stryker падає у dry-run з `defineProps()` error), концерн `stryker_config` точково вставляє у наявний файл `plugins: [..., './stryker-vue-macros-ignorer.mjs']` і `ignorers: ['vue-macros']`, зберігши решту полів і коментарів. Редагування — string-splice за AST-аналізом (oxc-parser — лише для пошуку offsets, не для re-serialize), тож форматування й коментарі не переписуються; idempotent — повторний `fix test` не дублює entries. Якщо `plugins`/`ignorers` уже частково є — добавляються лише відсутні entries. Якщо `export default` — **не** object-literal (factory/функція/змінна) або масиви динамічні (spread/computed), augment пропускається з вимогою додати плагін вручну згідно [`stryker.config.vue.baseline.mjs`](./js/data/stryker_config/stryker.config.vue.baseline.mjs).
|
|
160
|
-
|
|
161
|
-
### Vitest baseline та `package.json#scripts`
|
|
162
|
-
|
|
163
|
-
Поряд зі Stryker концерн `stryker_config` без дублювання копіює `vitest.config.mjs` (тільки якщо немає ні `.mjs`, ні legacy `.js`). Canonical: [vitest.config.baseline.js](./js/data/vitest_config/vitest.config.baseline.js) — `environment: 'node'`, `coverage.provider: 'v8'` з lcov+text-summary репортами, `include: ['**/*.test.{js,mjs}', 'tests/**/*.test.{js,mjs}']` (підхоплює обидві розкладки — тести у `tests/`-піддиректоріях і top-level integration suites у `<root>/tests/`).
|
|
164
|
-
|
|
165
|
-
У `package.json#scripts` має бути `"test": "vitest run"` (canonical contains-substring `vitest` — допустимо `vitest run` та інші локальні розширення); опційно — `"test:watch": "vitest"`.
|
|
166
|
-
|
|
167
|
-
Канон scripts: [package.json.contains.json](./policy/package_json/template/package.json.contains.json)
|
|
168
|
-
|
|
169
|
-
### Frontend-варіант (Vue/Vite + happy-dom)
|
|
170
|
-
|
|
171
|
-
Для проєктів зі своїм `vite.config.js` `vitest.config.mjs` має повторно використовувати vite-плагіни та aliases і перемкнути `environment` на `'happy-dom'` (або `'jsdom'`):
|
|
172
|
-
|
|
173
|
-
```js
|
|
174
|
-
import { defineConfig, mergeConfig } from 'vitest/config'
|
|
175
|
-
import viteConfig from './vite.config.js'
|
|
176
|
-
|
|
177
|
-
export default mergeConfig(viteConfig, defineConfig({
|
|
178
|
-
test: {
|
|
179
|
-
include: ['**/*.test.{js,mjs}', 'tests/**/*.test.{js,mjs}'],
|
|
180
|
-
environment: 'happy-dom',
|
|
181
|
-
coverage: { provider: 'v8', reporter: ['lcov', 'text-summary'] }
|
|
182
|
-
}
|
|
183
|
-
}))
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
Концерн ставить **node-варіант** baseline; перехід на frontend — ручна модифікація, після якої концерн уже не перетирає (idempotent).
|
|
187
|
-
|
|
188
|
-
Аналогічно, якщо `rust` присутнє в `rules` — створюється `.cargo/mutants.toml` у каталозі **кожного** Cargo.toml-маніфесту: кореневий `Cargo.toml`, `<workspace>/src-tauri/Cargo.toml` (Tauri-патерн) і `<workspace>/Cargo.toml` (flat workspace).
|
|
189
|
-
|
|
190
|
-
Канон cargo-mutants config: [mutants.toml.baseline](./js/data/cargo_mutants_config/mutants.toml.baseline)
|
|
191
|
-
|
|
192
|
-
Customization (mutate patterns, exclude rules, timeout) — відповідальність проєкту-споживача; концерни лише забезпечують наявність файлу як стартового baseline в кожному з виявлених workspace-каталогів.
|
|
193
|
-
|
|
194
|
-
### Універсальний baseline і framework-specific tuning
|
|
195
|
-
|
|
196
|
-
Правило `test` відповідає лише за **універсальний baseline** mutation-testing:
|
|
197
|
-
|
|
198
|
-
- створює `.cargo/mutants.toml` для Rust crates (порожній, з коментарем);
|
|
199
|
-
- не робить припущень про framework/app shell;
|
|
200
|
-
- не виключає platform glue, generated wrappers або binary entrypoints;
|
|
201
|
-
- framework-specific tuning (`--lib`/`--tests`, `exclude_globs` для app-shell і platform bridge файлів) належить відповідним правилам (`tauri`, `capacitor` тощо).
|
|
202
|
-
|
|
203
|
-
Якщо інше правило спеціалізує mutation-behavior — воно зобов'язане **доповнювати** існуючий `.cargo/mutants.toml` без дублювання (додавати лише відсутні ключі) і **не перетирати** ручні налаштування. Послідовний запуск `npx @nitra/cursor fix test` після `fix tauri` не має скидати tauri-tuning, і навпаки — повторний `fix tauri` не дублює секції.
|
|
204
|
-
|
|
205
|
-
Додатково: коли `js` enabled, концерн `stryker_config` без дублювання додає у кореневий `.gitignore` тест-патерни:
|
|
38
|
+
У monorepo `n-cursor coverage` ітерує усі workspaces з власним `package.json` і агрегує метрики lcov + Stryker у єдиний `JS`-рядок `COVERAGE.md`. Workspace без тестів пропускається без помилки.
|
|
206
39
|
|
|
207
|
-
|
|
208
|
-
- `**/coverage/` — весь output vitest v8 coverage (`lcov.info` + HTML `lcov-report/`). Ефемерний: регенерується кожним прогоном, фінальні метрики живуть у `COVERAGE.md`. Gitignore не заважає `n-cursor coverage` читати `lcov.info` у тому ж прогоні.
|
|
40
|
+
## Швидкий gate через conftest
|
|
209
41
|
|
|
210
|
-
|
|
42
|
+
| Namespace | Що перевіряє |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `test.package_json` | `scripts.coverage` містить `n-cursor coverage`; `scripts.test` містить `vitest` |
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
## CI: workflow lint-text
|
|
2
|
+
|
|
3
|
+
Додай workflow `.github/workflows/lint-text.yml`:
|
|
4
|
+
|
|
5
|
+
- Канон: [lint-text.yml.snippet.yml](./policy/lint_text/template/lint-text.yml.snippet.yml)
|
|
6
|
+
|
|
7
|
+
Перед **`./.github/actions/setup-bun-deps`** — **`actions/checkout@v6`** (див. **ga.mdc**). Після composite — кроки **`Install shellcheck`** (apt) і **`Install dotenv-linter`** (curl), бо `n-cursor lint text` вимагає обидва бінарники в PATH; на ubuntu-latest shellcheck часто вже є, dotenv-linter — ні. Composite: Node 24, Bun, кеш, `bun install --frozen-lockfile`.
|
|
8
|
+
|
|
9
|
+
Не дублюй окремий workflow з тими самими кроками cspell/markdownlint.
|
|
10
|
+
|
|
11
|
+
Ключовий крок CI: **`n-cursor lint text --read-only`** (нуль мутацій — авто-фікс вимкнено).
|
|
12
|
+
|
|
13
|
+
## Перевірка
|
|
14
|
+
|
|
15
|
+
`npx @nitra/cursor fix text` (охоплює oxfmt, cspell, shellcheck, markdownlint, v8r і CI для `n-cursor lint text`). Зокрема падає, якщо у корені є `.prettierignore`, `.prettierrc*`, `prettier.config.*` або у `package.json#scripts` зустрічається команда з `prettier` — Prettier повністю заборонено, форматування лише через **oxfmt**.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
## cspell: перевірка правопису
|
|
2
|
+
|
|
3
|
+
У корені проєкту має бути `.cspell.json` і залежності для cspell у кореневому `package.json` (зазвичай `devDependencies`).
|
|
4
|
+
|
|
5
|
+
**`package.json`:** у `devDependencies` має бути **`@nitra/cspell-dict`** (**`^2.0.0`** або новіший у лінії 2.x) — з **2.0.0** у пакет транзитивно входять типові **`@cspell/dict-*`**, тому **не** додавай їх окремо в корінь. **`markdownlint-cli2`** запускається rule-адаптером через **`bunx markdownlint-cli2`**, не додавай пакет до devDependencies. **`v8r`** лише через **`bunx v8r`**, не в devDependencies. Окремий пакет **`markdownlint`** не потрібний.
|
|
6
|
+
|
|
7
|
+
```json title="package.json"
|
|
8
|
+
{
|
|
9
|
+
"devDependencies": {
|
|
10
|
+
"@nitra/cspell-dict": "^2.1.0"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**`.cspell.json`**, `version: "0.2"`, **`language`**, **`import`** з `@nitra/cspell-dict`, **`ignorePaths`**, **`words`** лише для назв/термінів, коли не виправити текстом.
|
|
16
|
+
|
|
17
|
+
Базово (англійська + українська + корпоративний словник):
|
|
18
|
+
|
|
19
|
+
```json title=".cspell.json"
|
|
20
|
+
{
|
|
21
|
+
"version": "0.2",
|
|
22
|
+
"language": "nitra",
|
|
23
|
+
"ignorePaths": ["**/node_modules/**", "**/vscode-extension/**", "**/.git/**", ".vscode", "report", "*.svg", "**/k8s/**/*.yaml", "docs/adr/**"],
|
|
24
|
+
"import": ["@nitra/cspell-dict/cspell-ext.json"],
|
|
25
|
+
"words": []
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Канон базових ключів `.cspell.json` (`version`, `ignorePaths`): [.cspell.json.snippet.json](./policy/cspell/template/.cspell.json.snippet.json). Обовʼязковий запис у `import`: [.cspell.json.contains.json](./policy/cspell/template/.cspell.json.contains.json). Заборонено імпортувати окремі `@cspell/dict-*` у `.cspell.json`: [.cspell.json.deny.json](./policy/cspell/template/.cspell.json.deny.json).
|
|
30
|
+
|
|
31
|
+
`docs/adr/**` у канонічному `ignorePaths` — машинно-генеровані MADR-документи (драфти `capture-decisions.sh` + clean-ADR-и після `normalize-decisions.sh`). cspell-перевірка там безглузда: чернетка стирається наступним прогоном хук-ланцюжка, а будь-яка ручна правка правопису перезаписується. Локальні розширення `ignorePaths` дозволені — це лише мінімум.
|
|
32
|
+
|
|
33
|
+
### Проєкт з українською та російською (і суміжні мови)
|
|
34
|
+
|
|
35
|
+
У **`@nitra/cspell-dict`** від **2.0.0** уже зібрані залежності на **`@cspell/dict-uk-ua`**, **`@cspell/dict-ru_ru`** та інші типові словники — **не** дублюй їх у кореневому `package.json` і **не** імпортуй **`@cspell/dict-*/cspell-ext.json`** у `.cspell.json`.
|
|
36
|
+
|
|
37
|
+
**1. Залежності** — лише корпоративний пакет:
|
|
38
|
+
|
|
39
|
+
```json title="package.json"
|
|
40
|
+
{
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@nitra/cspell-dict": "^2.1.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Встановлення: `bun add -d @nitra/cspell-dict@^2.0.0`.
|
|
48
|
+
|
|
49
|
+
**2. `.cspell.json`** — у полі **`language`** додай потрібні коди (наприклад **`uk`**, **`ru-ru`** разом з **`en`** та **`nitra`**). У **`import`** залиш лише **`@nitra/cspell-dict/cspell-ext.json`**:
|
|
50
|
+
|
|
51
|
+
```json title=".cspell.json"
|
|
52
|
+
{
|
|
53
|
+
"version": "0.2",
|
|
54
|
+
"language": "en,uk,ru-ru,nitra",
|
|
55
|
+
"ignorePaths": ["**/node_modules/**", "**/vscode-extension/**", "**/.git/**", ".vscode", "report", "*.svg", "**/k8s/**/*.yaml", "docs/adr/**"],
|
|
56
|
+
"import": ["@nitra/cspell-dict/cspell-ext.json"],
|
|
57
|
+
"words": []
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Підлаштуй **`language`** під проєкт. Порядок у **`import`** може впливати на пріоритет словників — тримай корпоративний **`@nitra/cspell-dict`** першим, якщо додаєш інші розширення (рідко).
|
|
62
|
+
|
|
63
|
+
**Український апостроф:** у словах не використовуй прямий символ `'` (U+0027); потрібен типографський апостроф `'` (U+2019). Якщо після цього cspell досі підсвічує слово як невідоме — додай його до масиву `words` у `.cspell.json`.
|
|
64
|
+
|
|
65
|
+
### Локальні виключення (cspell `words`)
|
|
66
|
+
|
|
67
|
+
Коли **cspell** підсвічує слово, спочатку **виправ текст**, а не розширюй словник:
|
|
68
|
+
|
|
69
|
+
- виправ **друкарські помилки** та неправильні форми;
|
|
70
|
+
- **перефразуй коректною українською** (або англійською, залежно від контексту файлу): заміни кальки й випадкові склади на звичні формулювання зі словників;
|
|
71
|
+
- заміни **жаргон**, якщо є природний еквівалент у тому ж стилі документації (наприклад, у коментарях пиши «функція зворотного виклику» замість розмовного запозичення з англійського `callback`).
|
|
72
|
+
|
|
73
|
+
У секцію `words` у `.cspell.json` додавай записи **лише якщо переписати коректно неможливо** або **недоречно**: власні назви, стабільні технічні терміни без усталеного перекладу в проєкті, ідентифікатори зовнішніх API тощо.
|
|
74
|
+
|
|
75
|
+
### Інші мови
|
|
76
|
+
|
|
77
|
+
Якщо потрібна мова вже є в залежностях **`@nitra/cspell-dict`** — додай лише код у **`language`**, без окремих **`@cspell/dict-*`** у споживачі. Якщо мови немає в корпоративному пакеті — розширюй **`@nitra/cspell-dict`**, а не підключай **`@cspell/dict-*`** у корені репозиторію-споживача. Огляд upstream-словників: [streetsidesoftware/cspell-dicts](https://github.com/streetsidesoftware/cspell-dicts).
|
|
78
|
+
|
|
79
|
+
### cspell-fix: класифікація невідомих слів через omlx
|
|
80
|
+
|
|
81
|
+
cspell не має нативного `--fix`. Fix-режим **класифікує** знахідки через локальну LLM (`N_LOCAL_MIN_MODEL`): detect → omlx-класифікація distinct-слів (bounded JSON-вихід) → валідні слова авто-дописуються у `.cspell.json#words` (sorted/dedup) → ймовірні одруки лишаються списком на ревʼю (НЕ авто-виправляються). Максимум 80 distinct-слів за прогін.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
## dotenv-linter: перевірка `.env`-файлів
|
|
2
|
+
|
|
3
|
+
**dotenv-linter** — швидкий лінтер для `.env`-файлів (перевіряє: LowercaseKey, DuplicatedKey, IncorrectDelimiter, UnorderedKey тощо). Інструмент має бути в **`PATH`** і **не** додається в `dependencies` / `devDependencies`.
|
|
4
|
+
|
|
5
|
+
Якщо `dotenv-linter` відсутній — встанови локально:
|
|
6
|
+
- **macOS:** `brew install dotenv-linter`
|
|
7
|
+
- **Linux:** `curl -sSfL https://git.io/JLbXn | sh -s -- -b /usr/local/bin`
|
|
8
|
+
- **cargo:** `cargo install dotenv-linter`
|
|
9
|
+
|
|
10
|
+
**Запуск:** після shellcheck CLI виконує **`runDotenvLinter()`** з пакета — рекурсивно по `.env*`-файлах проєкту через **`dotenv-linter fix -r --no-backup --quiet . --exclude node_modules --exclude .envrc`**, далі такий самий **`check`** для фінальної перевірки.
|
|
11
|
+
|
|
12
|
+
Виключення: `node_modules` і `.envrc` (direnv shell-синтаксис, не key=value формат). `.bak`-файли інструмент ігнорує самостійно. Якщо `.env*`-файлів немає, dotenv-linter повертає 0 («Nothing to check»).
|
|
13
|
+
|
|
14
|
+
Авто-фікс — один прогін `dotenv-linter fix` (інструмент сам застосовує усі виправлення без потреби в diff/patch, на відміну від shellcheck). Після цього — фінальний `dotenv-linter check`; будь-яке залишкове порушення — ненульовий код виходу.
|
|
15
|
+
|
|
16
|
+
У CI — режим `--read-only` (нуль мутацій): авто-фікс пропускається, лише `check`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## Заборона Prettier
|
|
2
|
+
|
|
3
|
+
Prettier **повністю заборонено** — форматування лише через **oxfmt**.
|
|
4
|
+
|
|
5
|
+
Також потрібно прибрати, якщо є в проєкті, модуль **`@nitra/prettier-config`**, **prettier** та всі виклики prettier і налаштування для нього. Канон заборонених top-level/`dependencies`/`devDependencies` (prettier, `@nitra/prettier-config`, `markdownlint-cli2`): [package.json.deny.json](./policy/package_json/template/package.json.deny.json)
|
|
6
|
+
|
|
7
|
+
Перевірка `npx @nitra/cursor fix text` падає, якщо у корені є `.prettierignore`, `.prettierrc*`, `prettier.config.*` або у `package.json#scripts` зустрічається команда з `prettier`.
|
|
8
|
+
|
|
9
|
+
Заборонені файли конфігурації Prettier у корені:
|
|
10
|
+
- `.prettierignore`, `.prettierrc`, `.prettierrc.json`, `.prettierrc.jsonc`, `.prettierrc.json5`
|
|
11
|
+
- `.prettierrc.yaml`, `.prettierrc.yml`, `.prettierrc.toml`
|
|
12
|
+
- `.prettierrc.js`, `.prettierrc.cjs`, `.prettierrc.mjs`, `.prettierrc.ts`, `.prettierrc.cts`, `.prettierrc.mts`
|
|
13
|
+
- `prettier.config.js`, `prettier.config.cjs`, `prettier.config.mjs`, `prettier.config.ts`, `prettier.config.cts`, `prettier.config.mts`
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
## markdownlint: перевірка Markdown
|
|
2
|
+
|
|
3
|
+
**`markdownlint-cli2`** запускається rule-адаптером через **`bunx markdownlint-cli2`** — не додавай пакет до `devDependencies`. VSCode-розширення: `DavidAnson.vscode-markdownlint`.
|
|
4
|
+
|
|
5
|
+
У корені проєкту має бути `.markdownlint-cli2.jsonc`:
|
|
6
|
+
|
|
7
|
+
```json title=".markdownlint-cli2.jsonc"
|
|
8
|
+
{
|
|
9
|
+
"gitignore": true,
|
|
10
|
+
"config": {
|
|
11
|
+
"default": true,
|
|
12
|
+
"MD013": false,
|
|
13
|
+
"MD024": {
|
|
14
|
+
"siblings_only": true
|
|
15
|
+
},
|
|
16
|
+
"MD029": false,
|
|
17
|
+
"MD040": false,
|
|
18
|
+
"MD041": false
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Канон: [.markdownlint-cli2.jsonc.snippet.jsonc](./policy/markdownlint/template/.markdownlint-cli2.jsonc.snippet.jsonc)
|
|
24
|
+
|
|
25
|
+
**MD041** off навмисно (`.mdc` з frontmatter). Деталі — [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
## oxfmt: форматування коду
|
|
2
|
+
|
|
3
|
+
**oxfmt** — єдиний канонічний форматер для проєкту. Prettier повністю заборонено.
|
|
4
|
+
|
|
5
|
+
У корені проєкту має бути файл `.oxfmtrc.json` з правилами форматування:
|
|
6
|
+
|
|
7
|
+
```json title=".oxfmtrc.json"
|
|
8
|
+
{
|
|
9
|
+
"ignorePatterns": ["**/hasura/metadata/**", "**/schema.graphql", "**/auto-imports.d.ts"],
|
|
10
|
+
"arrowParens": "avoid",
|
|
11
|
+
"printWidth": 120,
|
|
12
|
+
"bracketSpacing": true,
|
|
13
|
+
"bracketSameLine": true,
|
|
14
|
+
"embeddedLanguageFormatting": "auto",
|
|
15
|
+
"endOfLine": "lf",
|
|
16
|
+
"htmlWhitespaceSensitivity": "css",
|
|
17
|
+
"insertPragma": false,
|
|
18
|
+
"jsxSingleQuote": true,
|
|
19
|
+
"proseWrap": "preserve",
|
|
20
|
+
"quoteProps": "as-needed",
|
|
21
|
+
"requirePragma": false,
|
|
22
|
+
"semi": false,
|
|
23
|
+
"singleQuote": true,
|
|
24
|
+
"tabWidth": 2,
|
|
25
|
+
"trailingComma": "none",
|
|
26
|
+
"useTabs": false,
|
|
27
|
+
"vueIndentScriptAndStyle": false
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Канон мінімального набору ключів і `ignorePatterns`: [.oxfmtrc.json.snippet.json](./policy/oxfmtrc/template/.oxfmtrc.json.snippet.json)
|
|
32
|
+
|
|
33
|
+
Поле **`ignorePatterns`** обовʼязкове: у масиві мають бути **`**/hasura/metadata/**`**, **`**/schema.graphql`** і **`**/auto-imports.d.ts`**; інші glob-и додавай за потреби (згенеровані каталоги тощо) — канон задає мінімум, локальні розширення дозволені.
|
|
34
|
+
|
|
35
|
+
Обовʼязкові ключі (наявність без точного значення): `arrowParens`, `printWidth`, `bracketSpacing`, `bracketSameLine`, `semi`, `singleQuote`, `tabWidth`, `trailingComma`, `useTabs`.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## package.json: вимоги текстового стека
|
|
2
|
+
|
|
3
|
+
**`package.json`** у `devDependencies` має бути **`@nitra/cspell-dict`** (**`^2.0.0`** або новіший у лінії 2.x). Окремий `package.json`-скрипт `lint-text` не потрібен: запуск іде через **`n-cursor lint text`**.
|
|
4
|
+
|
|
5
|
+
```json title="package.json"
|
|
6
|
+
{
|
|
7
|
+
"devDependencies": {
|
|
8
|
+
"@nitra/cspell-dict": "^2.1.0"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Заборонено** у `dependencies` / `devDependencies`:
|
|
14
|
+
- `prettier`, `@nitra/prettier-config` — Prettier повністю заборонено
|
|
15
|
+
- `markdownlint-cli2` — запускається через `bunx markdownlint-cli2`
|
|
16
|
+
- окремі `@cspell/dict-*` пакети — входять транзитивно через `@nitra/cspell-dict`
|
|
17
|
+
|
|
18
|
+
**Заборонено** у `scripts.*`: команди, що містять виклик `prettier` (будь-який runner: `bunx prettier`, `npx prettier`, `prettier --write` тощо) — використовуй oxfmt.
|
|
19
|
+
|
|
20
|
+
Канон заборонених пакетів: [package.json.deny.json](./policy/package_json/template/package.json.deny.json)
|
|
21
|
+
|
|
22
|
+
Завжди пиши **JSDoc** до функцій та методів.
|
|
23
|
+
|
|
24
|
+
## Найменування каталогів
|
|
25
|
+
|
|
26
|
+
kebab-case
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
## shellcheck: перевірка shell-скриптів
|
|
2
|
+
|
|
3
|
+
**shellcheck** — інструмент лише в **`PATH`**, **не** додавай до `dependencies` / `devDependencies`. VSCode-розширення: `timonwong.shellcheck`.
|
|
4
|
+
|
|
5
|
+
Якщо `shellcheck` відсутній — встанови локально:
|
|
6
|
+
- **macOS:** `brew install shellcheck`
|
|
7
|
+
- **Debian/Ubuntu:** `sudo apt-get install -y shellcheck`
|
|
8
|
+
- **Arch:** `sudo pacman -S shellcheck`
|
|
9
|
+
|
|
10
|
+
Потрібен також **`patch`** у `PATH` (на macOS зазвичай уже є; на Linux: `sudo apt-get install -y patch`).
|
|
11
|
+
|
|
12
|
+
Канонічний запуск — **`n-cursor lint text`**: після **`cspell .`** виконується **`runShellcheckText()`** з пакета — циклічно **`shellcheck -f diff`** і **`patch -p1`** для авто-виправлень (до 32 ітерацій на файл), потім повний прогін **`shellcheck`** по всіх tracked `*.sh` (у git) або по `**/*.sh` без `node_modules`.
|
|
13
|
+
|
|
14
|
+
Список файлів: у git-робочому дереві — `git ls-files` з pathspec `:(glob)` для всіх tracked `*.sh`; інакше — `globSync` з виключенням `node_modules`. Якщо скриптів не знайдено — вихід 0.
|
|
15
|
+
|
|
16
|
+
ShellCheck не має прапорця `--fix`; для виправлень використовується формат виводу `diff` і застосування патчу через `patch -p1`.
|
|
17
|
+
|
|
18
|
+
У CI — той самий `n-cursor lint text --read-only` (нуль мутацій): авто-фікс не запускається, лише фінальна перевірка всіх `*.sh`.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## v8r: валідація JSON/YAML/TOML за Schema Store
|
|
2
|
+
|
|
3
|
+
**`v8r`** — інструмент валідації файлів за схемами з [Schema Store](https://www.schemastore.org/). Використовуй лише через **`bunx v8r`**, не в `devDependencies`.
|
|
4
|
+
|
|
5
|
+
### .v8rignore
|
|
6
|
+
|
|
7
|
+
У корені проєкту має бути **`.v8rignore`**: **v8r** шукає схему в Schema Store; для JSON без запису там перевірка завершується помилкою. Мінімум виключи **`.vscode/extensions.json`** і **`.vscode/settings.json`** (немає стабільної схеми в каталозі).
|
|
8
|
+
|
|
9
|
+
```text title=".v8rignore"
|
|
10
|
+
.vscode/extensions.json
|
|
11
|
+
.vscode/settings.json
|
|
12
|
+
.git/**
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Не розширюй ignore, щоб приховати проблеми там, де схема є. За потреби додай **`.git/**`** (службові JSON під `.git`) та **`npm/schemas/n-cursor.json`** у репозиторії `@nitra/cursor`, якщо для цього файлу немає стабільної схеми в ланцюжку v8r.
|
|
16
|
+
|
|
17
|
+
### Запуск через обгортку n-cursor
|
|
18
|
+
|
|
19
|
+
У v8r **немає** прапорця тихого режиму; rule-адаптер **`n-cursor lint text`** на четвертому кроці викликає **`runV8rWithGlobs()`** з пакета (реалізація — `npm/rules/text/js/run-v8r.mjs`): під капотом послідовні **`bunx v8r`** для кожного типу (**json**, **json5**, **yml**, **yaml**, **toml**), бо один процес v8r з кількома глобами падає з **98**, якщо хоч один glob порожній, і тоді інші розширення не перевіряються. Вивід при кодах **0** і **98** не показується. Каталог схем **`schemas/v8r-catalog.json`** пакета `@nitra/cursor` обгортка підставляє в v8r сама.
|
|
20
|
+
|
|
21
|
+
### Запуск без обгортки
|
|
22
|
+
|
|
23
|
+
**v8r:** без обгортки — чотири виклики `(bunx v8r "<glob>" || [ $? -eq 98 ])` для **`**/*.json`**, **`**/*.yml`**, **`**/*.yaml`**, **`**/*.toml`** (за потреби окремо **json5**). Враховує `.gitignore`. Для **`**/*.json`** опційно додай **власний каталог схем** [`--catalogs`](https://chris48s.github.io/v8r/usage-examples/#using-a-custom-catlog) (наприклад `-c npm/schemas/v8r-catalog.json` у репозиторії пакета `@nitra/cursor` або `-c https://unpkg.com/@nitra/cursor/schemas/v8r-catalog.json`), щоб перевіряти **`.n-cursor.json`** за схемою з [unpkg](https://unpkg.com/@nitra/cursor/schemas/n-cursor.json).
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
## VSCode: розширення та налаштування
|
|
2
|
+
|
|
3
|
+
У проєкті мають бути `.vscode/extensions.json` і `.vscode/settings.json` з канонічними значеннями.
|
|
4
|
+
|
|
5
|
+
### Розширення (`.vscode/extensions.json`)
|
|
6
|
+
|
|
7
|
+
```json title=".vscode/extensions.json"
|
|
8
|
+
{
|
|
9
|
+
"recommendations": [
|
|
10
|
+
"dbaeumer.vscode-eslint",
|
|
11
|
+
"github.vscode-github-actions",
|
|
12
|
+
"oxc.oxc-vscode",
|
|
13
|
+
"DavidAnson.vscode-markdownlint",
|
|
14
|
+
"timonwong.shellcheck",
|
|
15
|
+
"redhat.vscode-yaml",
|
|
16
|
+
"irongeek.vscode-env"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Канон `recommendations` (substring requirement): [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
|
|
22
|
+
|
|
23
|
+
Обовʼязкові розширення: `DavidAnson.vscode-markdownlint`, `oxc.oxc-vscode`, `timonwong.shellcheck`.
|
|
24
|
+
|
|
25
|
+
### Налаштування (`.vscode/settings.json`)
|
|
26
|
+
|
|
27
|
+
```json title=".vscode/settings.json"
|
|
28
|
+
{
|
|
29
|
+
"files.associations": {
|
|
30
|
+
"*.env.*": "env",
|
|
31
|
+
"*.env": "env"
|
|
32
|
+
},
|
|
33
|
+
"editor.formatOnSave": true,
|
|
34
|
+
"[css]": {
|
|
35
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
36
|
+
},
|
|
37
|
+
"[graphql]": {
|
|
38
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
39
|
+
},
|
|
40
|
+
"[handlebars]": {
|
|
41
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
42
|
+
},
|
|
43
|
+
"[html]": {
|
|
44
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
45
|
+
},
|
|
46
|
+
"[javascript]": {
|
|
47
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
48
|
+
},
|
|
49
|
+
"[json]": {
|
|
50
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
51
|
+
},
|
|
52
|
+
"[json5]": {
|
|
53
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
54
|
+
},
|
|
55
|
+
"[jsonc]": {
|
|
56
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
57
|
+
},
|
|
58
|
+
"[less]": {
|
|
59
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
60
|
+
},
|
|
61
|
+
"[markdown]": {
|
|
62
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
63
|
+
},
|
|
64
|
+
"[mdx]": {
|
|
65
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
66
|
+
},
|
|
67
|
+
"[scss]": {
|
|
68
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
69
|
+
},
|
|
70
|
+
"[toml]": {
|
|
71
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
72
|
+
},
|
|
73
|
+
"[typescript]": {
|
|
74
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
75
|
+
},
|
|
76
|
+
"[vue]": {
|
|
77
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
78
|
+
},
|
|
79
|
+
"[yaml]": {
|
|
80
|
+
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
81
|
+
},
|
|
82
|
+
"oxc.path.oxfmt": "/opt/homebrew/bin/oxfmt"
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Канон `editor.formatOnSave` + `editor.defaultFormatter` для основних мов: [settings.json.snippet.json](./policy/vscode_settings/template/settings.json.snippet.json)
|