@nitra/cursor 12.8.6 → 12.8.8
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 +12 -0
- package/package.json +1 -1
- package/rules/abie/main.mdc +9 -5
- package/rules/abie/policy/base_deployment_preem/base_deployment_preem.mdc +22 -0
- package/rules/abie/policy/clean_merged_ignore_branches/clean_merged_ignore_branches.mdc +19 -0
- package/rules/abie/policy/health_check_policy/health_check_policy.mdc +17 -0
- package/rules/abie/policy/http_route_base/http_route_base.mdc +9 -0
- package/rules/abie/policy/package_json_shared/package_json_shared.mdc +17 -0
- 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 +17 -95
- package/rules/adr/policy/settings_json/settings_json.mdc +7 -0
- package/rules/adr/policy/settings_local_json/settings_local_json.mdc +7 -0
- 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 +7 -60
- package/rules/bun/policy/bunfig/bunfig.mdc +12 -0
- package/rules/bun/policy/package_json/package_json.mdc +14 -0
- package/rules/capacitor/js/ios_spm.mdc +69 -0
- package/rules/capacitor/js/version.mdc +29 -0
- package/rules/capacitor/main.mdc +6 -22
- package/rules/capacitor/policy/package_json/package_json.mdc +9 -0
- 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 +16 -14
- package/rules/ci4/policy/vscode_extensions/vscode_extensions.mdc +9 -0
- 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 +13 -196
- package/rules/docker/policy/lint_docker_yml/lint_docker_yml.mdc +14 -0
- package/rules/efes/main.mdc +1 -1
- package/rules/efes/policy/package_json_shared/package_json_shared.mdc +30 -0
- 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 +16 -119
- package/rules/ga/policy/clean_ga_workflows/clean_ga_workflows.mdc +18 -0
- package/rules/ga/policy/clean_merged_branch/clean_merged_branch.mdc +22 -0
- package/rules/ga/policy/git_ai/git_ai.mdc +19 -0
- package/rules/ga/policy/lint_ga/lint_ga.mdc +21 -0
- package/rules/ga/policy/vscode_extensions/vscode_extensions.mdc +9 -0
- package/rules/ga/policy/vscode_settings/vscode_settings.mdc +9 -0
- package/rules/ga/policy/workflow_common/workflow_common.mdc +18 -0
- package/rules/ga/policy/zizmor_yml/zizmor_yml.mdc +9 -0
- package/rules/graphql/js/tooling.mdc +13 -0
- package/rules/graphql/js/vscode_extensions.mdc +13 -0
- package/rules/graphql/main.mdc +4 -21
- package/rules/graphql/policy/vscode_extensions/vscode_extensions.mdc +9 -0
- 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 +6 -30
- package/rules/hasura/policy/svc_hl/svc_hl.mdc +15 -0
- 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 +5 -34
- package/rules/image-avif/policy/package_json/package_json.mdc +18 -0
- 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/image-compress/policy/package_json/package_json.mdc +13 -0
- 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 +19 -211
- package/rules/js/policy/jscpd/jscpd.mdc +14 -0
- package/rules/js/policy/lint_js_yml/lint_js_yml.mdc +14 -0
- package/rules/js/policy/package_json/package_json.mdc +15 -0
- package/rules/js/policy/vscode_extensions/vscode_extensions.mdc +11 -0
- 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 +12 -607
- package/rules/js-bun-db/policy/package_json/package_json.mdc +17 -0
- 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 +4 -10
- package/rules/js-bun-redis/policy/package_json/package_json.mdc +11 -0
- 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-mssql/policy/package_json/package_json.mdc +9 -0
- 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 -216
- package/rules/js-run/policy/configmap/configmap.mdc +31 -0
- package/rules/js-run/policy/jsconfig/jsconfig.mdc +25 -0
- package/rules/js-run/policy/package_json/package_json.mdc +38 -0
- 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 +29 -834
- package/rules/k8s/policy/base_kustomization/base_kustomization.mdc +12 -0
- package/rules/k8s/policy/base_manifest/base_manifest.mdc +14 -0
- package/rules/k8s/policy/gateway/gateway.mdc +17 -0
- package/rules/k8s/policy/hasura_configmap/hasura_configmap.mdc +20 -0
- package/rules/k8s/policy/hasura_httproute/hasura_httproute.mdc +16 -0
- package/rules/k8s/policy/hpa_pdb/hpa_pdb.mdc +23 -0
- package/rules/k8s/policy/kustomization/kustomization.mdc +20 -0
- package/rules/k8s/policy/manifest/manifest.mdc +17 -0
- package/rules/k8s/policy/network_policy/network_policy.mdc +22 -0
- package/rules/k8s/policy/svc_hl_yaml/svc_hl_yaml.mdc +13 -0
- package/rules/k8s/policy/svc_yaml/svc_yaml.mdc +12 -0
- 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 +8 -110
- package/rules/nginx-default-tpl/policy/vscode_extensions/vscode_extensions.mdc +11 -0
- package/rules/nginx-default-tpl/policy/vscode_settings/vscode_settings.mdc +15 -0
- 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 -52
- package/rules/npm-module/policy/emit_types_config/emit_types_config.mdc +40 -0
- package/rules/npm-module/policy/npm_package_json/npm_package_json.mdc +50 -0
- package/rules/npm-module/policy/root_package_json/root_package_json.mdc +37 -0
- package/rules/php/js/lint_php_yml.mdc +12 -0
- package/rules/php/js/tooling.mdc +66 -0
- package/rules/php/main.mdc +5 -66
- package/rules/php/policy/lint_php_yml/lint_php_yml.mdc +21 -0
- 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 +7 -32
- package/rules/python/policy/lint_python_yml/lint_python_yml.mdc +12 -0
- package/rules/python/policy/pyproject_toml/pyproject_toml.mdc +13 -0
- 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 +10 -22
- package/rules/rego/policy/vscode_extensions/vscode_extensions.mdc +11 -0
- package/rules/rego/policy/vscode_settings/vscode_settings.mdc +19 -0
- 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/rust/policy/lint_rust_yml/lint_rust_yml.mdc +12 -0
- package/rules/rust/policy/vscode_extensions/vscode_extensions.mdc +9 -0
- 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 -34
- package/rules/security/policy/lint_security_yml/lint_security_yml.mdc +7 -0
- package/rules/security/policy/package_json/package_json.mdc +7 -0
- 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 +12 -251
- package/rules/style/policy/lint_style_yml/lint_style_yml.mdc +13 -0
- package/rules/style/policy/package_json/package_json.mdc +18 -0
- package/rules/style/policy/vscode_extensions/vscode_extensions.mdc +13 -0
- package/rules/style/policy/vscode_settings/vscode_settings.mdc +19 -0
- 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 +6 -78
- package/rules/tauri/policy/vscode_extensions/vscode_extensions.mdc +21 -0
- 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 +16 -184
- package/rules/test/policy/package_json/package_json.mdc +16 -0
- 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 -231
- package/rules/text/policy/cspell/cspell.mdc +34 -0
- package/rules/text/policy/lint_text/lint_text.mdc +19 -0
- package/rules/text/policy/markdownlint/markdownlint.mdc +38 -0
- package/rules/text/policy/oxfmtrc/oxfmtrc.mdc +11 -0
- package/rules/text/policy/package_json/package_json.mdc +33 -0
- package/rules/text/policy/vscode_extensions/vscode_extensions.mdc +13 -0
- package/rules/text/policy/vscode_settings/vscode_settings.mdc +13 -0
- 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 +15 -641
- package/rules/vue/policy/package_json/package_json.mdc +30 -0
- package/scripts/docs/index.md +16 -16
- package/scripts/lib/docs/index.md +36 -36
- package/scripts/utils/docs/index.md +14 -14
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
## Rego-gate: іменування headless та clusterIP Service
|
|
2
|
+
|
|
3
|
+
Rego-пакет: `hasura.svc_hl`
|
|
4
|
+
|
|
5
|
+
Цільові файли: `hasura/k8s/base/svc.yaml`, `hasura/k8s/base/svc-hl.yaml`
|
|
6
|
+
|
|
7
|
+
Перевіряє два правила іменування Kubernetes Service:
|
|
8
|
+
|
|
9
|
+
- **Headless Service** (`spec.clusterIP: None`) — ім'я має закінчуватись на `-h-hl`
|
|
10
|
+
- **ClusterIP Service** (не headless) — ім'я має закінчуватись на `-h`
|
|
11
|
+
|
|
12
|
+
Ресурси з `kind != "Service"` пропускаються без помилок.
|
|
13
|
+
|
|
14
|
+
✓ `db-h-hl` (headless), `db-h` (clusterIP)
|
|
15
|
+
✗ `contract-h` як headless, `db-h-hl` як clusterIP
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## Чотирикроковий пайплайн генерації AVIF-двійників
|
|
2
|
+
|
|
3
|
+
AVIF-двійники (`<name>.<ext>.avif`) генерує **виключно** `npx @nitra/cursor fix image-avif`. Правило `image-compress` відповідає лише за стиснення початкових raster/SVG-файлів через `@nitra/minify-image`, а AVIF лишається окремим правилом. Перевірка виконує чотири кроки в порядку:
|
|
4
|
+
|
|
5
|
+
1. **Pre-scan**: шукає у `.vue`/`.html` хоча б одне raster-посилання, яке потенційно треба переписати на AVIF-двійник (`import x from '...png'` або `<img src="...png" />`). Пакети з opt-out `disable-avif: true` пропускаються. **Якщо жодного raster-посилання не знайдено — `check image-avif` завершується успіхом одразу: ні `npx --avif`, ні rewrite, ні cleanup-сиріт не виконуються** (нічого було б змінювати). Так уникаємо дорогого `npx @nitra/minify-image` у проєктах, де AVIF не вживається.
|
|
6
|
+
2. Запускає `npx @nitra/minify-image --src=. --write --avif` (≥ **3.3.1**) — генерує `<name>.<ext>.avif` поряд з кожним PNG/JPEG/GIF. CLI порівнює sha1 кожного raster-сорсу зі збереженим у `.n-minify-image.tsv` і перезаписує `<source>.avif` при зміні оригіналу.
|
|
7
|
+
3. Сканує `.vue` (а також `.html`) файли в кожному workspace-пакеті (root + workspaces) і автоматично переписує raster-посилання на AVIF-двійник у двох формах:
|
|
8
|
+
- **Імпорт-пов'язані** — `import x from '...png|jpg|jpeg|gif'` (далі `:src="x"` у шаблоні);
|
|
9
|
+
- **Прямі статичні** — `<img src="...png" />` у `<template>` (Vite перетворює такий шлях на asset-імпорт на етапі збірки, тож вимога та сама).
|
|
10
|
+
4. Видаляє AVIF-сироти: ходить по всіх `<...>.avif` у репозиторії; якщо на двійник не лишилось жодного посилання у `.vue`/`.html` — файл видаляється. **AVIF на диску лишається лише там, де заміна реально відбулась** — тому невикористані оригінали не накопичують `.avif`-«хвости». **Зауваж**: cleanup виконується ЛИШЕ якщо pre-scan на кроці 1 знайшов хоча б одне raster-посилання — інакше осиротілі `.avif` залишаються недоторканими (видаляться вже наступним прогоном, коли в `.vue`/`.html` зʼявиться raster для конвертації).
|
|
11
|
+
|
|
12
|
+
```vue title="App.vue (після check image-avif)"
|
|
13
|
+
<script setup>
|
|
14
|
+
import welcomeImage from './assets/welcome.png.avif'
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<img :src="welcomeImage" alt="Welcome" />
|
|
19
|
+
</template>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Реактивне `:src="..."` (з JS-виразом — змінною, тернарником, викликом тощо) **не сканується** — значення обчислюється у рантаймі й шлях туди потрапляє через імпорт або інший резолвинг, який ловить імпорт-перевірка вище. SVG не торкаємо (vector → AVIF безглуздо). Атрибути `data-src=`, `obj.src=` у `<script>` тощо також пропускаються.
|
|
23
|
+
|
|
24
|
+
Якщо raster-посилання у `.vue`/`.html` не вдалось переписати (наприклад, оригіналу немає на диску, тож і `.avif` не згенерувався) — `check image-avif` падає з помилкою на конкретний файл.
|
|
25
|
+
|
|
26
|
+
AVIF-двійники **зберігаємо в git** — це готові артефакти для віддачі браузеру (без них ефект від AVIF втрачається на чистому checkout-і).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
## Опт-аут для конкретного пакета
|
|
2
|
+
|
|
3
|
+
У workspace-пакеті, де AVIF-імпорти небажані (наприклад, мобільний бандл або публічний сайт без гарантованої AVIF-підтримки), додай у `package.json` цього пакета. Заборонений typo `disabled-avif` (канон — `disable-avif`): [package.json.deny.json](./policy/package_json/template/package.json.deny.json).
|
|
4
|
+
|
|
5
|
+
```json title="apps/site/package.json"
|
|
6
|
+
{
|
|
7
|
+
"@nitra/minify-image": {
|
|
8
|
+
"disable-avif": true
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Тоді перевірка пропускає `.vue` файли цього пакета і не видаляє наявні `.avif` всередині як «сироти». У root-`package.json` опт-аут діє лише для файлів кореня (вкладені workspaces перевіряються незалежно — вмикай прапор у кожному пакеті, де треба).
|
|
14
|
+
|
|
15
|
+
### Валідація package.json через conftest (Rego)
|
|
16
|
+
|
|
17
|
+
Rego-пакет `image_avif.package_json` перевіряє коректність конфігурації опт-ауту у `package.json`:
|
|
18
|
+
|
|
19
|
+
- Поле `"@nitra/minify-image"` має бути обʼєктом (якщо задано).
|
|
20
|
+
- Ключ `"disable-avif"` має бути `boolean`.
|
|
21
|
+
- Забороняє typo-ключі (наприклад, `"disabled-avif"`) через deny-template із [`package.json.deny.json`](./policy/package_json/template/package.json.deny.json).
|
|
@@ -5,41 +5,12 @@ globs: "**/*.{png,jpg,jpeg,gif,avif,vue,html}"
|
|
|
5
5
|
alwaysApply: false
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Правило забезпечує, що кожне raster-зображення (`png`, `jpg`, `jpeg`, `gif`), на яке посилаються `.vue`/`.html` файли, має AVIF-двійник і що посилання переписані на нього. Генерація, rewrite і cleanup AVIF-сиріт виконуються автоматично через `npx @nitra/cursor fix image-avif`.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
2. Запускає `npx @nitra/minify-image --src=. --write --avif` (≥ **3.3.1**) — генерує `<name>.<ext>.avif` поряд з кожним PNG/JPEG/GIF. CLI порівнює sha1 кожного raster-сорсу зі збереженим у `.n-minify-image.tsv` і перезаписує `<source>.avif` при зміні оригіналу.
|
|
12
|
-
3. Сканує `.vue` (а також `.html`) файли в кожному workspace-пакеті (root + workspaces) і автоматично переписує raster-посилання на AVIF-двійник у двох формах:
|
|
13
|
-
- **Імпорт-пов'язані** — `import x from '...png|jpg|jpeg|gif'` (далі `:src="x"` у шаблоні);
|
|
14
|
-
- **Прямі статичні** — `<img src="...png" />` у `<template>` (Vite перетворює такий шлях на asset-імпорт на етапі збірки, тож вимога та сама).
|
|
15
|
-
4. Видаляє AVIF-сироти: ходить по всіх `<...>.avif` у репозиторії; якщо на двійник не лишилось жодного посилання у `.vue`/`.html` — файл видаляється. **AVIF на диску лишається лише там, де заміна реально відбулась** — тому невикористані оригінали не накопичують `.avif`-«хвости». **Зауваж**: cleanup виконується ЛИШЕ якщо pre-scan на кроці 1 знайшов хоча б одне raster-посилання — інакше осиротілі `.avif` залишаються недоторканими (видаляться вже наступним прогоном, коли в `.vue`/`.html` зʼявиться raster для конвертації).
|
|
10
|
+
[image-avif-avif_generation](./js/avif_generation.mdc)
|
|
16
11
|
|
|
17
|
-
|
|
18
|
-
<script setup>
|
|
19
|
-
import welcomeImage from './assets/welcome.png.avif'
|
|
20
|
-
</script>
|
|
12
|
+
[image-avif-package_json_optout](./js/package_json_optout.mdc)
|
|
21
13
|
|
|
22
|
-
|
|
23
|
-
<img :src="welcomeImage" alt="Welcome" />
|
|
24
|
-
</template>
|
|
25
|
-
```
|
|
14
|
+
## Швидкий gate через conftest
|
|
26
15
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Якщо raster-посилання у `.vue`/`.html` не вдалось переписати (наприклад, оригіналу немає на диску, тож і `.avif` не згенерувався) — `check image-avif` падає з помилкою на конкретний файл.
|
|
30
|
-
|
|
31
|
-
AVIF-двійники **зберігаємо в git** — це готові артефакти для віддачі браузеру (без них ефект від AVIF втрачається на чистому checkout-і).
|
|
32
|
-
|
|
33
|
-
## Опт-аут для конкретного пакета
|
|
34
|
-
|
|
35
|
-
У workspace-пакеті, де AVIF-імпорти небажані (наприклад, мобільний бандл або публічний сайт без гарантованої AVIF-підтримки), додай у `package.json` цього пакета. Заборонений typo `disabled-avif` (канон — `disable-avif`): [package.json.deny.json](./policy/package_json/template/package.json.deny.json).
|
|
36
|
-
|
|
37
|
-
```json title="apps/site/package.json"
|
|
38
|
-
{
|
|
39
|
-
"@nitra/minify-image": {
|
|
40
|
-
"disable-avif": true
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Тоді перевірка пропускає `.vue` файли цього пакета і не видаляє наявні `.avif` всередині як «сироти». У root-`package.json` опт-аут діє лише для файлів кореня (вкладені workspaces перевіряються незалежно — вмикай прапор у кожному пакеті, де треба).
|
|
16
|
+
[image-avif-package_json](./policy/package_json/package_json.mdc)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
## Rego-gate: структура опт-аут конфігу у package.json
|
|
2
|
+
|
|
3
|
+
Rego-пакет: `image-avif.package_json`
|
|
4
|
+
|
|
5
|
+
Цільові файли: `package.json` (передається через `--input` у conftest).
|
|
6
|
+
|
|
7
|
+
Перевіряє три умови:
|
|
8
|
+
|
|
9
|
+
- **Тип поля** — `"@nitra/minify-image"` має бути обʼєктом (якщо задано); рядок, масив чи boolean → deny.
|
|
10
|
+
- **Тип прапора** — `"@nitra/minify-image.disable-avif"` має бути `boolean`; рядок або число → deny.
|
|
11
|
+
- **Typo-ключі** — забороняє ключі зі списку deny-template [`package.json.deny.json`](./template/package.json.deny.json) (наприклад, `"disabled-avif"` замість `"disable-avif"`).
|
|
12
|
+
|
|
13
|
+
Deny-template: [package.json.deny.json](./template/package.json.deny.json)
|
|
14
|
+
|
|
15
|
+
✓ `{ "@nitra/minify-image": { "disable-avif": true } }`
|
|
16
|
+
✗ `{ "@nitra/minify-image": { "disabled-avif": true } }` — typo-ключ
|
|
17
|
+
✗ `{ "@nitra/minify-image": "disable-avif" }` — поле не є обʼєктом
|
|
18
|
+
✗ `{ "@nitra/minify-image": { "disable-avif": "yes" } }` — не boolean
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
## Заборонені залежності у `package.json`
|
|
2
|
+
|
|
3
|
+
`@nitra/minify-image` не повинен зʼявлятися ні в `dependencies`, ні в `devDependencies` — CLI запускається лише через `npx`. Якщо потрібен явний пін — у самому виклику (`npx @nitra/minify-image@^3 --src=. --write`).
|
|
4
|
+
|
|
5
|
+
Перевіряється через Rego-пакет `image_compress.package_json`: [package.json.deny.json](./policy/package_json/template/package.json.deny.json)
|
|
6
|
+
|
|
7
|
+
Окремий `package.json`-скрипт `lint-image` не потрібен і не перевіряється — єдина точка входу `n-cursor lint image-compress`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## Кеш-файли: що зберігати в git
|
|
2
|
+
|
|
3
|
+
**`.n-minify-image.tsv`** у корені сканованого каталогу — **має бути в git** (source of truth для sha1-перевірок і lifetime savings). У `.gitignore` його не додавай.
|
|
4
|
+
|
|
5
|
+
**`node_modules/.cache/@nitra/minify-image/mtime.tsv`** — локальний fast-path; авто-gitignored через `node_modules/`.
|
|
6
|
+
|
|
7
|
+
Застарілий **`.minify-image-cache.tsv`** у корені (з версій `@nitra/minify-image` < 3.2) — видали після міграції на split-cache:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
git rm --cached .minify-image-cache.tsv 2>/dev/null || true && rm -f .minify-image-cache.tsv
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Також прибери відповідний рядок з `.gitignore`, якщо є.
|
|
@@ -9,18 +9,10 @@ CLI [`@nitra/minify-image`](https://www.npmjs.com/package/@nitra/minify-image) (
|
|
|
9
9
|
|
|
10
10
|
Окремий workflow `lint-image.yml` створювати не треба; якщо потрібен CI-gate для зображень, використовуй `n-cursor lint image-compress --read-only`.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
[image-compress-package_setup](./js/package_setup.mdc)
|
|
13
13
|
|
|
14
|
-
-
|
|
14
|
+
[image-compress-package_json](./js/package_json.mdc)
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
## Швидкий gate через conftest
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- **`.n-minify-image.tsv`** у корені сканованого каталогу — **має бути в git** (source of truth для sha1-перевірок і lifetime savings). У `.gitignore` його не додавай.
|
|
21
|
-
- **`node_modules/.cache/@nitra/minify-image/mtime.tsv`** — локальний fast-path; авто-gitignored через `node_modules/`.
|
|
22
|
-
- Застарілий `.minify-image-cache.tsv` у корені — видали (`git rm --cached`, прибери рядок з `.gitignore`).
|
|
23
|
-
|
|
24
|
-
## Заборонені залежності
|
|
25
|
-
|
|
26
|
-
`@nitra/minify-image` не повинен зʼявлятися ні в `dependencies`, ні в `devDependencies` — CLI запускається лише через `npx`. Якщо потрібен явний пін — у самому виклику (`npx @nitra/minify-image@^3 --src=. --write`).
|
|
18
|
+
[image-compress-package_json](./policy/package_json/package_json.mdc)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## Заборона `@nitra/minify-image` у залежностях (`package.json`)
|
|
2
|
+
|
|
3
|
+
Rego-пакет: `image_compress.package_json`
|
|
4
|
+
|
|
5
|
+
Цільовий файл: `package.json` (єдиний файл проєкту).
|
|
6
|
+
|
|
7
|
+
Перевіряє поля `dependencies` та `devDependencies`: якщо там присутній пакет `@nitra/minify-image` — deny. Список заборонених пакетів надходить через `--data` з канонічного шаблону:
|
|
8
|
+
|
|
9
|
+
[package.json.deny.json](./template/package.json.deny.json)
|
|
10
|
+
|
|
11
|
+
✓ `{}` — порожній `package.json` без залежностей
|
|
12
|
+
✗ `{ "dependencies": { "@nitra/minify-image": "^4.0.1" } }` — заборонено, використовуй `npx`
|
|
13
|
+
✗ `{ "devDependencies": { "@nitra/minify-image": "^4.0.1" } }` — заборонено, використовуй `npx`
|
package/rules/js/docs/index.md
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
## Залежнісна політика (що не додавати)
|
|
2
|
+
|
|
3
|
+
`@e18e/eslint-plugin` окремо не додавай — він уже в залежностях `@nitra/eslint-config` (з **3.8.0**), oxlint підвантажує його з `node_modules`. Пакети `oxlint`/`eslint`/`jscpd`/`knip` теж не додавай у `devDependencies` без потреби монорепо — `bunx` тягне їх ad-hoc.
|
|
4
|
+
|
|
5
|
+
### Заборона `@nitra/as-integrations-fastify`
|
|
6
|
+
|
|
7
|
+
Пакет **`@nitra/as-integrations-fastify`** заборонений у **`dependencies`**, **`peerDependencies`** та в import-specifier-ах. Це чистий републіш upstream, застряглий на peer `@apollo/server: "^4.0.0"`, тож на Apollo 5 `bun install` дає `warn: incorrect peer dependency`. Заміна — upstream **`@as-integrations/fastify`** (**`^3.1.0`**): peer `@apollo/server: "^4.0.0 || ^5.0.0"` + `fastify: "^5.3.0"`.
|
|
8
|
+
|
|
9
|
+
Міграція — лише specifier у трьох місцях: залежність у `package.json`, `import`, та `vi.mock(...)` / `await import(...)` у тестах. **Код не міняється**, експорти ті самі: `default` → `fastifyApollo`, named → `fastifyApolloDrainPlugin`.
|
|
10
|
+
|
|
11
|
+
```javascript title="❌ до"
|
|
12
|
+
import fastifyApollo, { fastifyApolloDrainPlugin } from '@nitra/as-integrations-fastify'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```javascript title="✅ після"
|
|
16
|
+
import fastifyApollo, { fastifyApolloDrainPlugin } from '@as-integrations/fastify'
|
|
17
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
## ESLint flat config — `eslint.config.js`
|
|
2
|
+
|
|
3
|
+
У корені проєкту має бути `eslint.config.js` (або `eslint.config.mjs`) з flat config на основі `getConfig` з `@nitra/eslint-config`.
|
|
4
|
+
|
|
5
|
+
```javascript title="eslint.config.js"
|
|
6
|
+
import { getConfig } from '@nitra/eslint-config'
|
|
7
|
+
|
|
8
|
+
export default [
|
|
9
|
+
{
|
|
10
|
+
ignores: ['**/auto-imports.d.ts']
|
|
11
|
+
},
|
|
12
|
+
...getConfig({
|
|
13
|
+
node: ['npm']
|
|
14
|
+
})
|
|
15
|
+
]
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
У монорепо пакети з Vite (frontend) вкажи в секції `vue`, решту — у секції `node` у виклику `getConfig`.
|
|
19
|
+
|
|
20
|
+
Обов'язкові умови:
|
|
21
|
+
- файл `eslint.config.js` або `eslint.config.mjs` існує;
|
|
22
|
+
- містить виклик `getConfig`;
|
|
23
|
+
- імпортує з `@nitra/eslint-config`;
|
|
24
|
+
- у `ignores` є запис `**/auto-imports.d.ts`.
|
|
25
|
+
|
|
26
|
+
Застарілі конфіги (`.eslintrc`, `.eslintrc.js`, `.eslintrc.json`, `.eslintrc.yml`) — **видали**, вони несумісні з flat config.
|
|
27
|
+
|
|
28
|
+
З версії `@nitra/eslint-config` **3.9.2** `getConfig` вбудовує ignore для `**/adr/**` — ADR-документи не валідуються ESLint, локально цей glob додавати не потрібно.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
## `.vscode/extensions.json` — рекомендовані розширення
|
|
2
|
+
|
|
3
|
+
У `.vscode/extensions.json` поле `recommendations` має містити:
|
|
4
|
+
- `dbaeumer.vscode-eslint`
|
|
5
|
+
- `github.vscode-github-actions`
|
|
6
|
+
- `oxc.oxc-vscode`
|
|
7
|
+
|
|
8
|
+
Канон: [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
## Розширення нових файлів — `.mjs` / `.cjs`, не `.js`
|
|
2
|
+
|
|
3
|
+
**Нові** JS-файли створюй з явним розширенням модуля:
|
|
4
|
+
|
|
5
|
+
- **`.mjs`** — для ESM (типовий випадок);
|
|
6
|
+
- **`.cjs`** — для CommonJS, де він справді потрібен.
|
|
7
|
+
|
|
8
|
+
Голий **`.js`** для нового файлу **заборонено**. Розширення `.js` інтерпретується як ESM чи CJS лише за полем `package.json#type`, тож той самий файл читається по-різному залежно від пакета. Явне `.mjs`/`.cjs` робить тип модуля однозначним **без читання `package.json`** — навіть якщо `type` зміниться або файл перемістять в інший пакет. Це доповнює вимогу `"type": "module"` у `package-json.mdc`: `type` лишається каноном для всього дерева, а розширення нового файлу прибирає залежність від нього.
|
|
9
|
+
|
|
10
|
+
Стосується **backend і frontend** — будь-який новий вихідний файл: `src/`, тести `*.test.*`, `scripts/`, `src/conn/` тощо.
|
|
11
|
+
|
|
12
|
+
**Існуючі `.js` лишаються як є** — масово перейменовувати не треба; це конвенція для нового коду. Автоматичної перевірки тут немає: stateless-скан не відрізнить новий файл від існуючого, тож `.js` нікого не фейлить.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## `for...in` заборонено — рефакторити на `for...of`
|
|
2
|
+
|
|
3
|
+
Конструкція `for (const k in obj)` обходить успадковані ключі прототипу, тому майже завжди тягне `Object.hasOwn(obj, k)`-guard. Заборонена у `@nitra/eslint-config` через `no-restricted-syntax` для `ForInStatement` (з версії **3.8.0**). У каноні oxlint лишається `guard-for-in` як часткова страховка (oxlint не підтримує `no-restricted-syntax`).
|
|
4
|
+
|
|
5
|
+
Замість цього обходь масив напряму, а об'єкт — через `Object.entries` / `Object.keys` / `Object.values`. У такому коді guard за `Object.hasOwn` стає непотрібним і має зникнути разом із `for...in`.
|
|
6
|
+
|
|
7
|
+
```javascript title="❌ до"
|
|
8
|
+
for (const k in obj) {
|
|
9
|
+
if (!Object.hasOwn(obj, k)) continue
|
|
10
|
+
use(k, obj[k])
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
for (const i in arr) {
|
|
14
|
+
use(arr[i])
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```javascript title="✅ після"
|
|
19
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
20
|
+
use(k, v)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (const item of arr) {
|
|
24
|
+
use(item)
|
|
25
|
+
}
|
|
26
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
## `.jscpd.json` — виявлення дублювання коду
|
|
2
|
+
|
|
3
|
+
У корені проєкту має бути `.jscpd.json`. Мінімум: увімкнути облік `.gitignore`, ненульовий код виходу при знаходженні клонів, консольний звіт.
|
|
4
|
+
|
|
5
|
+
```json title=".jscpd.json"
|
|
6
|
+
{
|
|
7
|
+
"gitignore": true,
|
|
8
|
+
"exitCode": 1,
|
|
9
|
+
"reporters": ["console"],
|
|
10
|
+
"minLines": 25,
|
|
11
|
+
"ignore": [".claude/worktrees/**", "**/dist/**", "**/CHANGELOG.md"]
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Канон ключів `.jscpd.json` (`gitignore`, `exitCode`, `reporters`, `minLines`, `ignore` як subset-of): [.jscpd.json.snippet.json](./policy/jscpd/template/.jscpd.json.snippet.json)
|
|
16
|
+
|
|
17
|
+
### Ігнорування `.claude/worktrees/`
|
|
18
|
+
|
|
19
|
+
Каталог `.claude/worktrees/` (робочі копії, які Claude Code створює через **superpowers:using-git-worktrees**) має ігноруватися:
|
|
20
|
+
- додай `.claude/worktrees/` у кореневий `.gitignore` (штатне місце для не-комітних робочих копій);
|
|
21
|
+
- у `.jscpd.json` додай `.claude/worktrees/**` у `ignore` як страховку на випадок запуску без `gitignore: true`.
|
|
22
|
+
|
|
23
|
+
Без цього jscpd сканує паралельну копію репо в worktree і фіксує самозбіги між дзеркальними файлами.
|
|
24
|
+
|
|
25
|
+
```text title=".gitignore (фрагмент)"
|
|
26
|
+
.claude/worktrees/
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Ігнорування `**/CHANGELOG.md`
|
|
30
|
+
|
|
31
|
+
`**/CHANGELOG.md` у каноні `ignore`: release-журнали різних пакетів структурно повторюються (заголовки `## [x.y.z] - YYYY-MM-DD`, секції `### Added / Changed / Fixed` за Keep a Changelog), і `jscpd` за `minLines: 25` фіксує їх як клон — це false positive. Без цього в монорепо легко зловити критичний `bun run lint` на парі CHANGELOG-ів довжиною від ~25 рядків.
|
|
32
|
+
|
|
33
|
+
### Рефакторинг vs конфіг
|
|
34
|
+
|
|
35
|
+
Коли **jscpd** знаходить клони, спочатку зменшуй дублювання кодом, а не конфігом.
|
|
36
|
+
|
|
37
|
+
- **Рефакторинг:** винеси спільні фрагменти в функції, модулі, утиліти, composables/hooks, спільні компоненти або базові типи — залежно від контексту.
|
|
38
|
+
- **Структура:** якщо одна й та сама логіка розмазана між файлами чи пакетами, запропонуй зміну структури (наприклад, спільний модуль, `shared/`, внутрішній пакет у monorepo), щоб була **одна канонічна реалізація** і повторні місця лише викликали її.
|
|
39
|
+
|
|
40
|
+
Розширення `ignore` чи завищення `minLines` лише щоб прибрати звіт — не заміна рефакторингу для справжніх клонів. Якщо збіг **семантично випадковий** (генерований код, формальні шаблони без спільної логіки), після оцінки допустимо точковий `ignore` або зміна порогу — з коротким обґрунтуванням.
|
|
41
|
+
|
|
42
|
+
Перед рефакторингом перевір чи є тести на блоки які підлягають зміні (Bun.test для js, playwright для vue). Якщо тестів немає або вони не покривають — спочатку створи їх, перевір що вони відпрацьовують коректно, потім роби рефакторинг і ще раз запускай тести.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
## `knip.json` — аналіз невикористаних залежностей та експортів
|
|
2
|
+
|
|
3
|
+
Залежнісний аналіз (knip — невикористані залежності/експорти, `knip.json` канон) крос-файловий; запускається автоматично у `lint --full` разом з jscpd.
|
|
4
|
+
|
|
5
|
+
У корені проєкту має бути **`knip.json`**, який стартує з канонічного baseline з пакета `@nitra/cursor` — файл [`npm/rules/js/js/tooling/knip-canonical.json`](./js/tooling/knip-canonical.json). Канон покриває типові false-positives для наших правил:
|
|
6
|
+
|
|
7
|
+
- `entry` зі CLI-конфігами (eslint, stylelint, oxlint, jscpd, markdownlint-cli2, `commitlint`);
|
|
8
|
+
- `project` для `**/*.{js,mjs,cjs,jsx,ts,tsx,mts,cts}`;
|
|
9
|
+
- `ignore` для `**/__fixtures__/**`;
|
|
10
|
+
- `ignoreDependencies` для пакетів, посилання на які є лише в не-JS-конфігах (`@nitra/cspell-dict`, `/@cspell\/dict-.+/`, `graphql`);
|
|
11
|
+
- `ignoreBinaries` для CLI, які канон вимагає викликати через `npx`/`bunx` і яких заборонено додавати в `devDependencies` (`actionlint`, `cspell`, `eslint`, `git-ai`, `jscpd`, `markdownlint-cli2`, `oxfmt`, `oxlint`, `shellcheck`, `uvx`, `v8r`, `zizmor`).
|
|
12
|
+
|
|
13
|
+
Якщо `knip.json` відсутній — `npx @nitra/cursor fix js` копіює канон у корінь проєкту (side effect). Після створення модифікуй файл під свій проєкт як завгодно: перевіряємо лише наявність, зміст подальших змін не валідується.
|
|
14
|
+
|
|
15
|
+
Пакет `knip` окремо в `devDependencies` не додавай — `bunx knip` тягне його ad-hoc, як oxlint/eslint/jscpd.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
## GitHub Actions: `lint-js.yml` — CI-лінт JS
|
|
2
|
+
|
|
3
|
+
Додай workflow для лінту JS у CI:
|
|
4
|
+
|
|
5
|
+
```yaml title=".github/workflows/lint-js.yml"
|
|
6
|
+
name: Lint JS
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
branches:
|
|
11
|
+
- dev
|
|
12
|
+
- main
|
|
13
|
+
paths:
|
|
14
|
+
- '**/*.js'
|
|
15
|
+
- '**/*.mjs'
|
|
16
|
+
- '**/*.cjs'
|
|
17
|
+
- '**/*.jsx'
|
|
18
|
+
- '**/*.ts'
|
|
19
|
+
- '**/*.tsx'
|
|
20
|
+
- '**/*.vue'
|
|
21
|
+
- '**/eslint.config.*'
|
|
22
|
+
|
|
23
|
+
pull_request:
|
|
24
|
+
branches:
|
|
25
|
+
- dev
|
|
26
|
+
- main
|
|
27
|
+
|
|
28
|
+
concurrency:
|
|
29
|
+
group: ${{ github.ref }}-${{ github.workflow }}
|
|
30
|
+
cancel-in-progress: true
|
|
31
|
+
|
|
32
|
+
jobs:
|
|
33
|
+
eslint:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
permissions:
|
|
36
|
+
contents: read
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v6
|
|
39
|
+
with:
|
|
40
|
+
persist-credentials: false
|
|
41
|
+
|
|
42
|
+
- uses: ./.github/actions/setup-bun-deps
|
|
43
|
+
|
|
44
|
+
- name: Eslint
|
|
45
|
+
run: |
|
|
46
|
+
bunx oxlint
|
|
47
|
+
bunx eslint .
|
|
48
|
+
bunx jscpd .
|
|
49
|
+
bunx knip --no-config-hints
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Канон workflow `.github/workflows/lint-js.yml`: [lint-js.yml.snippet.yml](./policy/lint_js_yml/template/lint-js.yml.snippet.yml)
|
|
53
|
+
|
|
54
|
+
Перед `./.github/actions/setup-bun-deps` — `actions/checkout@v6` (з `persist-credentials: false`, див. `ga.mdc`). Composite action: Node 24, Bun, кеш, `bun install --frozen-lockfile`.
|
|
55
|
+
|
|
56
|
+
Один workflow на лінт JS; зайвий `lint.yml` з тими самими кроками (`bunx oxlint`, `bunx eslint`, `jscpd`) — прибери.
|
|
57
|
+
|
|
58
|
+
У CI `--fix` для oxlint/eslint — **заборонено**: CI лише перевіряє, не виправляє.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## `.oxlintrc.json` — канонічна конфігурація oxlint
|
|
2
|
+
|
|
3
|
+
У корені має бути `.oxlintrc.json`, який **збігається з каноном** oxlint з пакета `@nitra/cursor`: файл `npm/rules/js/js/data/tooling/oxlint-canonical.json` (plugins, jsPlugins з `@e18e/eslint-plugin`, categories, повний набір `rules` із канону — додаткові записи в `rules` дозволені; також `settings`, `env`, `globals`).
|
|
4
|
+
|
|
5
|
+
Поле `ignorePatterns` працює як `rules`: канонічні патерни (наразі `**/schema.graphql`, `**/auto-imports.d.ts`) мають бути присутні; додаткові локальні glob-и дозволені.
|
|
6
|
+
|
|
7
|
+
Мінімум для розуміння структури (реальний корінь конфігу має збігатися з каноном повністю):
|
|
8
|
+
|
|
9
|
+
```json title=".oxlintrc.json (фрагмент)"
|
|
10
|
+
{
|
|
11
|
+
"jsPlugins": ["@e18e/eslint-plugin"],
|
|
12
|
+
"rules": {
|
|
13
|
+
"e18e/prefer-includes": "error"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Модуль `@e18e/eslint-plugin` не оголошуй окремо в `package.json` — він уже в залежностях `@nitra/eslint-config` (з **3.8.0**), oxlint підвантажує його з `node_modules`.
|
|
19
|
+
|
|
20
|
+
Канон `oxlint-canonical.json` — source-of-truth, редагується напряму; у споживачі оновлюється копіюванням файлу з репозиторію пакета.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
## `package.json` — модульна система та runtime-вимоги
|
|
2
|
+
|
|
3
|
+
### `"type": "module"` у кожному `package.json`
|
|
4
|
+
|
|
5
|
+
У **кожному** `package.json` проєкту (корінь і всі workspace-пакети) має бути `"type": "module"` — весь код у ESM.
|
|
6
|
+
|
|
7
|
+
### `engines` — мінімальні версії Node і Bun
|
|
8
|
+
|
|
9
|
+
```json title="package.json"
|
|
10
|
+
{
|
|
11
|
+
"type": "module",
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=24",
|
|
14
|
+
"bun": ">=1.3"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@nitra/eslint-config": "^3.10.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Канон `type`, мінімальна `@nitra/eslint-config` (semver-поріг `devDependencies`) і `engines`: [package.json.snippet.json](./policy/package_json/template/package.json.snippet.json).
|
|
23
|
+
|
|
24
|
+
### `@nitra/eslint-config` у `devDependencies`
|
|
25
|
+
|
|
26
|
+
У `devDependencies` кореневого `package.json` має бути `@nitra/eslint-config` — версія не нижче канонічного мінімуму зі snippet (semver-поріг, єдине джерело істини):
|
|
27
|
+
- з **3.8.0** — правило `no-restricted-syntax` забороняє `for...in`;
|
|
28
|
+
- з **3.9.2** — у `getConfig` вбудовано ignore для `**/adr/**`;
|
|
29
|
+
- транзитивно йде `@e18e/eslint-plugin` для oxlint.
|
|
30
|
+
|
|
31
|
+
Окремого `lint-js` скрипта немає — лінт через `n-cursor lint js` (CI — `--read-only`).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
## Тести та покриття
|
|
2
|
+
|
|
3
|
+
### Unit-тести (Bun test)
|
|
4
|
+
|
|
5
|
+
Проєкт має бути покритий unit-тестами (**Bun test**). Код: синтаксис Node **24+**, **top level await** (узгоджено з `engines.node` у `package.json`).
|
|
6
|
+
|
|
7
|
+
### Покриття + мутаційне тестування JS
|
|
8
|
+
|
|
9
|
+
Покриття + мутаційне тестування JS постачаються через `n-cursor coverage` (правило `test.mdc`). Реалізація провайдера — у `npm/rules/js/coverage/coverage.mjs`: `bun test --coverage --coverage-reporter=lcov` + `bunx stryker run`. Stryker конфігурується в `stryker.config.mjs` у JS-корені (single-package або `workspaces[0]`).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
## Структура спільних модулів: `utils/` vs `lib/`
|
|
2
|
+
|
|
3
|
+
Коли спільні методи виносяться з кількох JS-файлів в окремий каталог — назву каталога обирай за призначенням модулів усередині.
|
|
4
|
+
|
|
5
|
+
- **`utils/`** — низькорівневі, чисті, generic helpers без бізнес-логіки і без залежностей від домену проєкту. Те, що могло б жити в окремому npm-пакеті. Приклади: `formatDate()`, `chunk(array, size)`, `retry(fn, opts)`, `deepMerge()`, `parseDuration('5m')`, `sleep(ms)`.
|
|
6
|
+
|
|
7
|
+
- **`lib/`** — внутрішні модулі / підсистеми проєкту, які знають про домен. Більші за `utils/`, часто з власним state, конфігом або side effects. Приклади: `lib/gmail-client.js`, `lib/circuit-breaker.js`, `lib/skill-loader.js`, `lib/safety/dry-run.js`.
|
|
8
|
+
|
|
9
|
+
Швидкий тест: якщо файл завтра можна опублікувати окремим npm-пакетом без переписування — це `utils/`; якщо він тримає domain-state, читає конфіг проєкту або викликає зовнішні сервіси/файли — це `lib/`. Не плутай із чужими каталогами на кшталт `shared/` чи `common/`: канонічні назви — лише `utils/` і `lib/`.
|
|
10
|
+
|
|
11
|
+
### Автоматична перевірка імпортів у `utils/`
|
|
12
|
+
|
|
13
|
+
`npx @nitra/cursor fix js` (концерн `utils_imports`) обходить кожен `utils/`-каталог у воркспейсах і падає, якщо знаходить relative-імпорт з `..` (вихід за межі каталогу) у будь-якому не-тестовому файлі. Це механічне втілення правила «utils не знає про домен»: тільки same-dir (`./X`), bare-пакети та `node:*` дозволені; cross-rule, конфіги проєкту чи sibling-utils — fail.
|
|
14
|
+
|
|
15
|
+
Якщо потрібна domain-залежність — перенеси файл у `lib/`.
|