@nitra/cursor 1.9.14 → 1.9.17

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.
Files changed (25) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/package.json +1 -1
  3. package/policy/graphql/vscode_extensions/vscode_extensions.rego +20 -0
  4. package/policy/graphql/vscode_extensions/vscode_extensions_test.rego +34 -0
  5. package/policy/image_avif/package_json/package_json.rego +61 -0
  6. package/policy/image_avif/package_json/package_json_test.rego +69 -0
  7. package/policy/js_run/jsconfig/jsconfig_test.rego +88 -0
  8. package/policy/nginx_default_tpl/vscode_extensions/vscode_extensions.rego +16 -0
  9. package/policy/nginx_default_tpl/vscode_extensions/vscode_extensions_test.rego +30 -0
  10. package/policy/nginx_default_tpl/vscode_settings/vscode_settings.rego +36 -0
  11. package/policy/nginx_default_tpl/vscode_settings/vscode_settings_test.rego +53 -0
  12. package/policy/style_lint/vscode_extensions/vscode_extensions.rego +23 -0
  13. package/policy/style_lint/vscode_extensions/vscode_extensions_test.rego +39 -0
  14. package/policy/style_lint/vscode_settings/vscode_settings.rego +24 -0
  15. package/policy/style_lint/vscode_settings/vscode_settings_test.rego +49 -0
  16. package/policy/text/vscode_extensions/vscode_extensions.rego +36 -0
  17. package/policy/text/vscode_extensions/vscode_extensions_test.rego +51 -0
  18. package/policy/text/vscode_settings/vscode_settings.rego +56 -0
  19. package/policy/text/vscode_settings/vscode_settings_test.rego +85 -0
  20. package/scripts/check-graphql.mjs +18 -26
  21. package/scripts/check-js-run.mjs +18 -7
  22. package/scripts/check-nginx-default-tpl.mjs +28 -18
  23. package/scripts/check-style-lint.mjs +11 -33
  24. package/scripts/check-text.mjs +8 -66
  25. package/scripts/lint-conftest.mjs +27 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,62 @@
4
4
 
5
5
  Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
6
6
 
7
+ ## [1.9.17] - 2026-05-13
8
+
9
+ ### Added
10
+
11
+ - **2 нові rego-полісі для text.mdc VSCode-канону** (мігровано з `check-text.mjs`):
12
+ - `text.vscode_extensions` — `recommendations` має містити три розширення: `DavidAnson.vscode-markdownlint`, `oxc.oxc-vscode`, `timonwong.shellcheck`. Шаблон повідомлень + множина `recommendations_set` (винесена поза `deny`, щоб не порушити `performance/non-loop-expression`).
13
+ - `text.vscode_settings` — `editor.formatOnSave: true` плюс шість мов-блоків (`[javascript]`/`[typescript]`/`[json]`/`[vue]`/`[css]`/`[html]`) з `editor.defaultFormatter: "oxc.oxc-vscode"`. Окремі deny для «не object» і «неправильний defaultFormatter». Канон задає мінімум — додаткові lang-блоки дозволені.
14
+ - **18 нових rego-тестів** (7 для `vscode_extensions` + 11 для `vscode_settings`): happy path, додаткові поля, відсутність кожного розширення, відсутність `formatOnSave`, неправильний defaultFormatter, відсутні lang-блоки. `conftest verify` — **252/252 pass** (+18).
15
+ - **`lint-conftest.mjs` TARGETS — два нові entry для text:** `text.vscode_extensions` (`single: '.vscode/extensions.json'`) і `text.vscode_settings` (`single: '.vscode/settings.json'`), обидва з `rule: 'text'`. Глобально активуються для всіх проєктів з `text` у `.n-cursor.json:rules`.
16
+
17
+ ### Removed
18
+
19
+ - **`check-text.mjs::checkVscodeTextExtensions` / `checkVscodeTextSettings` / `checkVscodeText`** — три JS-функції видалено разом з викликом `await checkVscodeText(pass, fail)` у `check()`. Зміст delegated у rego (`text.vscode_extensions` + `text.vscode_settings`).
20
+
21
+ ### Changed
22
+
23
+ - **`check-text.mjs::checkTextConfigsExistence` — розширено двома записами:** тепер вимагає FS-існування `.vscode/extensions.json` і `.vscode/settings.json` поряд з `.oxfmtrc.json` / `.cspell.json` / `.markdownlint-cli2.jsonc`. lint-conftest з rego skip-ить неіснуючі файли, тому FS-existence лишається в JS — це працює як «єдина точка контролю наявності файлу + delegated content-валідація у rego».
24
+
25
+ ## [1.9.16] - 2026-05-13
26
+
27
+ ### Added
28
+
29
+ - **5 нових rego-полісі для `.vscode/extensions.json` / `.vscode/settings.json`** (мігровано канон з .mdc у rego, прибрано JS-дублі):
30
+ - `style_lint.vscode_extensions` — `recommendations` має містити `stylelint.vscode-stylelint` (style-lint.mdc).
31
+ - `style_lint.vscode_settings` — `css.validate` / `scss.validate` / `less.validate: false`; `editor.codeActionsOnSave` свідомо не enforced (smell-test, мдс показує як рекомендацію).
32
+ - `graphql.vscode_extensions` — `recommendations` має містити `graphql.vscode-graphql` (graphql.mdc). НЕ реєструється глобально у `lint-conftest` TARGETS — правило conditional на наявність `gql\`…\``у джерелах; викликається з`check-graphql.mjs`через`runConftestBatch` після gql-scan.
33
+ - `nginx_default_tpl.vscode_extensions` — `recommendations` має містити `ahmadalli.vscode-nginx-conf` (nginx-default-tpl.mdc).
34
+ - `nginx_default_tpl.vscode_settings` — `editor.formatOnSave: true` і `[nginx].editor.defaultFormatter: "ahmadalli.vscode-nginx-conf"`. Обидва nginx-полісі викликаються з `check-nginx-default-tpl.mjs` через `runConftestBatch` лише після виявлення `default.conf.template`.
35
+ - **28 нових тестів** до пʼяти полісі: 5 (style_lint.vscode_extensions) + 6 (style_lint.vscode_settings) + 5 (graphql.vscode_extensions) + 5 (nginx_default_tpl.vscode_extensions) + 7 (nginx_default_tpl.vscode_settings). `conftest verify` — **234/234 pass** (+28).
36
+
37
+ ### Removed
38
+
39
+ - **`check-style-lint.mjs::checkVscodeStylelint`** — функція повністю видалена; зміст delegated у `style_lint.vscode_extensions` і `style_lint.vscode_settings`. JSDoc-преамбулу оновлено.
40
+ - **`check-graphql.mjs::checkExtensionsRecommendation` — JS-копія тіла перевірки видалена:** функція тепер є тонкою обгорткою над `runConftestBatch`, делегує `graphql.vscode_extensions`. Зник дубль JSON-парсингу й порівняння `recommendations`.
41
+ - **`check-nginx-default-tpl.mjs::checkVscodeNginx` — JS-копія тіла перевірки видалена:** функція тепер делегує `nginx_default_tpl.vscode_extensions` і `nginx_default_tpl.vscode_settings` через `runConftestBatch`. Зник дубль перевірок `editor.formatOnSave` і `[nginx].editor.defaultFormatter` у JS.
42
+
43
+ ### Changed
44
+
45
+ - **`lint-conftest.mjs` TARGETS — два нові глобальні entry для style-lint:** `style_lint.vscode_extensions` (`single: .vscode/extensions.json`) і `style_lint.vscode_settings` (`single: .vscode/settings.json`), обидва з `rule: 'style-lint'`. Не-style-lint проєкти не зачіпають (filter по `activeRules` з `.n-cursor.json`).
46
+ - **graphql/nginx — НЕ реєструються глобально у `lint-conftest`:** правила conditional на per-package умовах, які lint-conftest не вміє виразити (`gql\`…\``у джерелах для graphql; наявність`default.conf.template`для nginx). Plan B: rego-authoritative + JS-orchestrator з`runConftestBatch`.
47
+
48
+ ## [1.9.15] - 2026-05-13
49
+
50
+ ### Added
51
+
52
+ - **`npm/policy/js_run/jsconfig/jsconfig_test.rego` — 12 нових тестів для канону `jsconfig.json`:** rego-полісі `js_run.jsconfig` (canonical compilerOptions — `lib: ["esnext"]`, `module/moduleResolution: NodeNext`, `target: esnext`, `checkJs: false`, `include: ["src/**/*"]`) існувала, але не мала тестів і не запускалась на реальних файлах. Додано happy path + 11 негативних кейсів через `json.patch`-фікстури.
53
+ - **`npm/policy/image_avif/package_json/` — структурна валідація опт-аут конфігу:** новий rego-пакет `image_avif.package_json` з 3 deny-правилами для `package.json`: значення `"@nitra/minify-image"` має бути обʼєктом (якщо присутнє), `disable-avif` має бути boolean (якщо присутнє), захист від typo `disabled-avif`. Поле опційне — більшість проєктів його не мають, deny спрацьовує лише на нелегітимну форму (typo або wrong type, що тихо ламає опт-аут). +11 тестів. Зареєстровано у `lint-conftest.mjs` TARGETS з `walk` по всіх `package.json` (з фільтром `rule: "image-avif"`).
54
+
55
+ ### Changed
56
+
57
+ - **`check-js-run.mjs::checkBackendJsconfigWhenSrcPresent` — структуру `jsconfig.json` тепер валідує rego через `runConftestBatch`:** замість FS-existence-only + посилання на `lint-conftest` (яке насправді не запускалось — rego не була зареєстрована глобально), JS тепер викликає rego-пакет `js_run.jsconfig` через `runConftestBatch` після того, як визначить, що пакет — backend (без `vite` у `devDependencies`) з каталогом `src/`. Це Plan B: Rego-authoritative + JS-orchestrator. Глобальна реєстрація `js_run.jsconfig` у `lint-conftest.mjs` свідомо не додавалась — rule стосується лише workspace-пакетів певної форми, що lint-conftest filter (`activeRules` на рівні репо) не вміє виразити.
58
+
59
+ ### Not done (Phase 1.5 — пізніше)
60
+
61
+ - **`rego.mdc`, `tauri.mdc`** — rego-полісі для канонічних `.vscode/extensions.json` / `.vscode/settings.json` потрібен JS-orchestrator. Ці правила conditional (rego — glob `**/*.rego`, tauri — лише Tauri-проєкти), тож запускати rego безумовно на кожний `.vscode/extensions.json` дало б false-positive порушення для всіх не-rego/не-tauri проєктів. Чисте розширення rego-полісі без `check-<rule>.mjs`-orchestrator-а тут не закриває правило.
62
+
7
63
  ## [1.9.14] - 2026-05-13
8
64
 
9
65
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.9.14",
3
+ "version": "1.9.17",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -0,0 +1,20 @@
1
+ # Перевірка `.vscode/extensions.json` для graphql (graphql.mdc).
2
+ #
3
+ # Викликається з `check-graphql.mjs` через `runConftestBatch` лише ПІСЛЯ того,
4
+ # як JS виявив `gql\`…\`` tagged template literal у джерелах (умовне правило).
5
+ # Тому в `lint-conftest.mjs` TARGETS глобально не реєструється — інакше були б
6
+ # false-positive порушення на проєктах без gql.
7
+ #
8
+ # Canonical (graphql.mdc):
9
+ # { "recommendations": ["graphql.vscode-graphql"] }
10
+ #
11
+ # Канон задає мінімум; інші записи (від markdownlint/oxc/...) дозволені.
12
+ package graphql.vscode_extensions
13
+
14
+ import rego.v1
15
+
16
+ deny contains msg if {
17
+ recs := object.get(input, "recommendations", [])
18
+ not "graphql.vscode-graphql" in {r | some r in recs}
19
+ msg := ".vscode/extensions.json: додай у recommendations \"graphql.vscode-graphql\" (graphql.mdc)"
20
+ }
@@ -0,0 +1,34 @@
1
+ # Тести для `graphql.vscode_extensions`. Запуск:
2
+ # conftest verify -p npm/policy/graphql/vscode_extensions
3
+ package graphql.vscode_extensions_test
4
+
5
+ import rego.v1
6
+
7
+ import data.graphql.vscode_extensions
8
+
9
+ test_allow_with_required_extension if {
10
+ cfg := {"recommendations": ["graphql.vscode-graphql"]}
11
+ count(vscode_extensions.deny) == 0 with input as cfg
12
+ }
13
+
14
+ test_allow_with_additional_extensions if {
15
+ cfg := {"recommendations": [
16
+ "dbaeumer.vscode-eslint",
17
+ "graphql.vscode-graphql",
18
+ "oxc.oxc-vscode",
19
+ ]}
20
+ count(vscode_extensions.deny) == 0 with input as cfg
21
+ }
22
+
23
+ test_deny_missing_extension if {
24
+ cfg := {"recommendations": ["dbaeumer.vscode-eslint"]}
25
+ count(vscode_extensions.deny) > 0 with input as cfg
26
+ }
27
+
28
+ test_deny_empty_recommendations if {
29
+ count(vscode_extensions.deny) > 0 with input as {"recommendations": []}
30
+ }
31
+
32
+ test_deny_no_recommendations_field if {
33
+ count(vscode_extensions.deny) > 0 with input as {}
34
+ }
@@ -0,0 +1,61 @@
1
+ # Структурна перевірка опт-аут конфігу для image-avif у `package.json` (image-avif.mdc).
2
+ #
3
+ # Запуск (локально):
4
+ # conftest test <pkg>/package.json -p npm/policy/image_avif/package_json \
5
+ # --namespace image_avif.package_json
6
+ #
7
+ # Канонічна форма опт-ауту з image-avif.mdc:
8
+ # { "@nitra/minify-image": { "disable-avif": true } }
9
+ #
10
+ # Поле опційне — більшість проєктів його не мають. Полісі deny лише, якщо поле
11
+ # присутнє, але має нелегітимну форму: типовий typo (`disabled-avif`) або
12
+ # неправильний тип (`"disable-avif": "yes"`). Без цієї перевірки помилкове
13
+ # написання тихо повертає AVIF-генерацію всередину пакета, де її хотіли вимкнути.
14
+ #
15
+ # FS / behavior (запуск `npx @nitra/minify-image`, walk `.vue`, видалення AVIF-сиріт) — у JS.
16
+ #
17
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
18
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`.
19
+ package image_avif.package_json
20
+
21
+ import rego.v1
22
+
23
+ minify_image_field := "@nitra/minify-image"
24
+
25
+ # ── deny: значення поля має бути обʼєктом, якщо присутнє ──────────────────
26
+
27
+ deny contains msg if {
28
+ value := object.get(input, minify_image_field, null)
29
+ value != null
30
+ not is_object(value)
31
+ msg := sprintf(
32
+ "package.json: поле \"@nitra/minify-image\" має бути обʼєктом (зараз: %v) (image-avif.mdc)",
33
+ [value],
34
+ )
35
+ }
36
+
37
+ # ── deny: відомі ключі мають правильний тип ──────────────────────────────
38
+
39
+ deny contains msg if {
40
+ cfg := object.get(input, minify_image_field, {})
41
+ is_object(cfg)
42
+ value := object.get(cfg, "disable-avif", null)
43
+ value != null
44
+ not is_boolean(value)
45
+ msg := sprintf(
46
+ "package.json: \"@nitra/minify-image.disable-avif\" має бути boolean (зараз: %v) (image-avif.mdc)",
47
+ [value],
48
+ )
49
+ }
50
+
51
+ # ── deny: захист від typo `disabled-avif` ────────────────────────────────
52
+
53
+ deny contains msg if {
54
+ cfg := object.get(input, minify_image_field, {})
55
+ is_object(cfg)
56
+ "disabled-avif" in object.keys(cfg)
57
+ msg := concat(" ", [
58
+ "package.json: ключ \"@nitra/minify-image.disabled-avif\" виглядає як typo —",
59
+ "канонічна назва \"disable-avif\" (image-avif.mdc)",
60
+ ])
61
+ }
@@ -0,0 +1,69 @@
1
+ # Тести для `image_avif.package_json`. Запуск:
2
+ # conftest verify -p npm/policy/image_avif/package_json
3
+ package image_avif.package_json_test
4
+
5
+ import rego.v1
6
+
7
+ import data.image_avif.package_json
8
+
9
+ # ── happy path ────────────────────────────────────────────────────────────
10
+
11
+ test_allow_no_field if {
12
+ count(package_json.deny) == 0 with input as {"name": "x"}
13
+ }
14
+
15
+ test_allow_canonical_opt_out if {
16
+ pkg := {"name": "x", "@nitra/minify-image": {"disable-avif": true}}
17
+ count(package_json.deny) == 0 with input as pkg
18
+ }
19
+
20
+ test_allow_disable_avif_false if {
21
+ pkg := {"name": "x", "@nitra/minify-image": {"disable-avif": false}}
22
+ count(package_json.deny) == 0 with input as pkg
23
+ }
24
+
25
+ test_allow_empty_config if {
26
+ pkg := {"name": "x", "@nitra/minify-image": {}}
27
+ count(package_json.deny) == 0 with input as pkg
28
+ }
29
+
30
+ test_allow_other_keys_inside if {
31
+ pkg := {"name": "x", "@nitra/minify-image": {"disable-avif": true, "future-flag": "y"}}
32
+ count(package_json.deny) == 0 with input as pkg
33
+ }
34
+
35
+ # ── deny: тип поля ───────────────────────────────────────────────────────
36
+
37
+ test_deny_field_is_string if {
38
+ pkg := {"name": "x", "@nitra/minify-image": "disable-avif"}
39
+ count(package_json.deny) > 0 with input as pkg
40
+ }
41
+
42
+ test_deny_field_is_array if {
43
+ pkg := {"name": "x", "@nitra/minify-image": ["disable-avif"]}
44
+ count(package_json.deny) > 0 with input as pkg
45
+ }
46
+
47
+ test_deny_field_is_boolean if {
48
+ pkg := {"name": "x", "@nitra/minify-image": true}
49
+ count(package_json.deny) > 0 with input as pkg
50
+ }
51
+
52
+ # ── deny: тип disable-avif ──────────────────────────────────────────────
53
+
54
+ test_deny_disable_avif_string if {
55
+ pkg := {"name": "x", "@nitra/minify-image": {"disable-avif": "yes"}}
56
+ count(package_json.deny) > 0 with input as pkg
57
+ }
58
+
59
+ test_deny_disable_avif_number if {
60
+ pkg := {"name": "x", "@nitra/minify-image": {"disable-avif": 1}}
61
+ count(package_json.deny) > 0 with input as pkg
62
+ }
63
+
64
+ # ── deny: typo disabled-avif ────────────────────────────────────────────
65
+
66
+ test_deny_typo_disabled_avif if {
67
+ pkg := {"name": "x", "@nitra/minify-image": {"disabled-avif": true}}
68
+ count(package_json.deny) > 0 with input as pkg
69
+ }
@@ -0,0 +1,88 @@
1
+ # Тести для `js_run.jsconfig`. Запуск:
2
+ # conftest verify -p npm/policy/js_run/jsconfig
3
+ package js_run.jsconfig_test
4
+
5
+ import rego.v1
6
+
7
+ import data.js_run.jsconfig
8
+
9
+ valid_cfg := {
10
+ "compilerOptions": {
11
+ "lib": ["esnext"],
12
+ "module": "NodeNext",
13
+ "moduleResolution": "NodeNext",
14
+ "target": "esnext",
15
+ "checkJs": false,
16
+ },
17
+ "include": ["src/**/*"],
18
+ }
19
+
20
+ # ── happy path ────────────────────────────────────────────────────────────
21
+
22
+ test_allow_canonical if {
23
+ count(jsconfig.deny) == 0 with input as valid_cfg
24
+ }
25
+
26
+ # ── compilerOptions.lib ───────────────────────────────────────────────────
27
+
28
+ test_deny_lib_not_array if {
29
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/compilerOptions/lib", "value": "esnext"}])
30
+ count(jsconfig.deny) > 0 with input as cfg
31
+ }
32
+
33
+ test_deny_lib_wrong_value if {
34
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/compilerOptions/lib", "value": ["es2022"]}])
35
+ count(jsconfig.deny) > 0 with input as cfg
36
+ }
37
+
38
+ test_deny_lib_missing if {
39
+ cfg := json.patch(valid_cfg, [{"op": "remove", "path": "/compilerOptions/lib"}])
40
+ count(jsconfig.deny) > 0 with input as cfg
41
+ }
42
+
43
+ # ── compilerOptions.module / moduleResolution / target / checkJs ──────────
44
+
45
+ test_deny_module_not_nodenext if {
46
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/compilerOptions/module", "value": "esnext"}])
47
+ count(jsconfig.deny) > 0 with input as cfg
48
+ }
49
+
50
+ test_deny_module_resolution_not_nodenext if {
51
+ cfg := json.patch(
52
+ valid_cfg,
53
+ [{"op": "replace", "path": "/compilerOptions/moduleResolution", "value": "node"}],
54
+ )
55
+ count(jsconfig.deny) > 0 with input as cfg
56
+ }
57
+
58
+ test_deny_target_not_esnext if {
59
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/compilerOptions/target", "value": "es2022"}])
60
+ count(jsconfig.deny) > 0 with input as cfg
61
+ }
62
+
63
+ test_deny_check_js_true if {
64
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/compilerOptions/checkJs", "value": true}])
65
+ count(jsconfig.deny) > 0 with input as cfg
66
+ }
67
+
68
+ test_deny_check_js_missing if {
69
+ cfg := json.patch(valid_cfg, [{"op": "remove", "path": "/compilerOptions/checkJs"}])
70
+ count(jsconfig.deny) > 0 with input as cfg
71
+ }
72
+
73
+ # ── include ──────────────────────────────────────────────────────────────
74
+
75
+ test_deny_include_not_array if {
76
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/include", "value": "src/**/*"}])
77
+ count(jsconfig.deny) > 0 with input as cfg
78
+ }
79
+
80
+ test_deny_include_wrong_value if {
81
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/include", "value": ["lib/**/*"]}])
82
+ count(jsconfig.deny) > 0 with input as cfg
83
+ }
84
+
85
+ test_deny_include_missing if {
86
+ cfg := json.patch(valid_cfg, [{"op": "remove", "path": "/include"}])
87
+ count(jsconfig.deny) > 0 with input as cfg
88
+ }
@@ -0,0 +1,16 @@
1
+ # Перевірка `.vscode/extensions.json` для nginx-default-tpl (nginx-default-tpl.mdc).
2
+ #
3
+ # Викликається з `check-nginx-default-tpl.mjs` через `runConftestBatch` лише
4
+ # ПІСЛЯ того, як JS виявив `default.conf.template` у дереві (умовне правило).
5
+ # Глобально у `lint-conftest.mjs` TARGETS не реєструється.
6
+ #
7
+ # Canonical: `recommendations` має містити `ahmadalli.vscode-nginx-conf`.
8
+ package nginx_default_tpl.vscode_extensions
9
+
10
+ import rego.v1
11
+
12
+ deny contains msg if {
13
+ recs := object.get(input, "recommendations", [])
14
+ not "ahmadalli.vscode-nginx-conf" in {r | some r in recs}
15
+ msg := ".vscode/extensions.json: recommendations має містити \"ahmadalli.vscode-nginx-conf\" (nginx-default-tpl.mdc)"
16
+ }
@@ -0,0 +1,30 @@
1
+ # Тести для `nginx_default_tpl.vscode_extensions`. Запуск:
2
+ # conftest verify -p npm/policy/nginx_default_tpl/vscode_extensions
3
+ package nginx_default_tpl.vscode_extensions_test
4
+
5
+ import rego.v1
6
+
7
+ import data.nginx_default_tpl.vscode_extensions
8
+
9
+ test_allow_with_required_extension if {
10
+ cfg := {"recommendations": ["ahmadalli.vscode-nginx-conf"]}
11
+ count(vscode_extensions.deny) == 0 with input as cfg
12
+ }
13
+
14
+ test_allow_with_additional_extensions if {
15
+ cfg := {"recommendations": ["dbaeumer.vscode-eslint", "ahmadalli.vscode-nginx-conf"]}
16
+ count(vscode_extensions.deny) == 0 with input as cfg
17
+ }
18
+
19
+ test_deny_missing_extension if {
20
+ cfg := {"recommendations": ["dbaeumer.vscode-eslint"]}
21
+ count(vscode_extensions.deny) > 0 with input as cfg
22
+ }
23
+
24
+ test_deny_empty_recommendations if {
25
+ count(vscode_extensions.deny) > 0 with input as {"recommendations": []}
26
+ }
27
+
28
+ test_deny_no_recommendations_field if {
29
+ count(vscode_extensions.deny) > 0 with input as {}
30
+ }
@@ -0,0 +1,36 @@
1
+ # Перевірка `.vscode/settings.json` для nginx-default-tpl (nginx-default-tpl.mdc).
2
+ #
3
+ # Викликається з `check-nginx-default-tpl.mjs` через `runConftestBatch` лише
4
+ # ПІСЛЯ того, як JS виявив `default.conf.template`. Глобально у `lint-conftest`
5
+ # не реєструється.
6
+ #
7
+ # Canonical:
8
+ # { "editor.formatOnSave": true,
9
+ # "[nginx]": { "editor.defaultFormatter": "ahmadalli.vscode-nginx-conf" } }
10
+ package nginx_default_tpl.vscode_settings
11
+
12
+ import rego.v1
13
+
14
+ deny contains msg if {
15
+ object.get(input, "editor.formatOnSave", null) != true
16
+ msg := ".vscode/settings.json: \"editor.formatOnSave\" має бути true (nginx-default-tpl.mdc)"
17
+ }
18
+
19
+ deny contains msg if {
20
+ nginx_block := object.get(input, "[nginx]", {})
21
+ not is_object(nginx_block)
22
+ msg := concat(" ", [
23
+ ".vscode/settings.json: \"[nginx]\" має бути обʼєктом з",
24
+ "\"editor.defaultFormatter\": \"ahmadalli.vscode-nginx-conf\" (nginx-default-tpl.mdc)",
25
+ ])
26
+ }
27
+
28
+ deny contains msg if {
29
+ nginx_block := object.get(input, "[nginx]", {})
30
+ is_object(nginx_block)
31
+ object.get(nginx_block, "editor.defaultFormatter", null) != "ahmadalli.vscode-nginx-conf"
32
+ msg := concat(" ", [
33
+ ".vscode/settings.json: \"[nginx].editor.defaultFormatter\" має бути",
34
+ "\"ahmadalli.vscode-nginx-conf\" (nginx-default-tpl.mdc)",
35
+ ])
36
+ }
@@ -0,0 +1,53 @@
1
+ # Тести для `nginx_default_tpl.vscode_settings`. Запуск:
2
+ # conftest verify -p npm/policy/nginx_default_tpl/vscode_settings
3
+ package nginx_default_tpl.vscode_settings_test
4
+
5
+ import rego.v1
6
+
7
+ import data.nginx_default_tpl.vscode_settings
8
+
9
+ valid_cfg := {
10
+ "editor.formatOnSave": true,
11
+ "[nginx]": {"editor.defaultFormatter": "ahmadalli.vscode-nginx-conf"},
12
+ }
13
+
14
+ test_allow_canonical if {
15
+ count(vscode_settings.deny) == 0 with input as valid_cfg
16
+ }
17
+
18
+ test_allow_with_additional_keys if {
19
+ cfg := json.patch(valid_cfg, [{
20
+ "op": "add",
21
+ "path": "/[javascript]",
22
+ "value": {"editor.defaultFormatter": "oxc.oxc-vscode"},
23
+ }])
24
+ count(vscode_settings.deny) == 0 with input as cfg
25
+ }
26
+
27
+ test_deny_format_on_save_false if {
28
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/editor.formatOnSave", "value": false}])
29
+ count(vscode_settings.deny) > 0 with input as cfg
30
+ }
31
+
32
+ test_deny_format_on_save_missing if {
33
+ cfg := json.patch(valid_cfg, [{"op": "remove", "path": "/editor.formatOnSave"}])
34
+ count(vscode_settings.deny) > 0 with input as cfg
35
+ }
36
+
37
+ test_deny_nginx_block_missing if {
38
+ cfg := json.patch(valid_cfg, [{"op": "remove", "path": "/[nginx]"}])
39
+ count(vscode_settings.deny) > 0 with input as cfg
40
+ }
41
+
42
+ test_deny_nginx_block_wrong_type if {
43
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/[nginx]", "value": "ahmadalli.vscode-nginx-conf"}])
44
+ count(vscode_settings.deny) > 0 with input as cfg
45
+ }
46
+
47
+ test_deny_nginx_wrong_formatter if {
48
+ cfg := json.patch(
49
+ valid_cfg,
50
+ [{"op": "replace", "path": "/[nginx]/editor.defaultFormatter", "value": "ms-vscode.cpptools"}],
51
+ )
52
+ count(vscode_settings.deny) > 0 with input as cfg
53
+ }
@@ -0,0 +1,23 @@
1
+ # Перевірка `.vscode/extensions.json` для style-lint (style-lint.mdc).
2
+ #
3
+ # Запуск (локально):
4
+ # conftest test .vscode/extensions.json -p npm/policy/style_lint/vscode_extensions \
5
+ # --namespace style_lint.vscode_extensions
6
+ #
7
+ # Canonical (style-lint.mdc):
8
+ # { "recommendations": ["stylelint.vscode-stylelint"] }
9
+ #
10
+ # Канон задає мінімум — `recommendations` має МІСТИТИ `stylelint.vscode-stylelint`;
11
+ # додаткові записи (від інших правил — markdownlint, oxc тощо) дозволені.
12
+ #
13
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
14
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`.
15
+ package style_lint.vscode_extensions
16
+
17
+ import rego.v1
18
+
19
+ deny contains msg if {
20
+ recs := object.get(input, "recommendations", [])
21
+ not "stylelint.vscode-stylelint" in {r | some r in recs}
22
+ msg := ".vscode/extensions.json: recommendations має містити \"stylelint.vscode-stylelint\" (style-lint.mdc)"
23
+ }
@@ -0,0 +1,39 @@
1
+ # Тести для `style_lint.vscode_extensions`. Запуск:
2
+ # conftest verify -p npm/policy/style_lint/vscode_extensions
3
+ package style_lint.vscode_extensions_test
4
+
5
+ import rego.v1
6
+
7
+ import data.style_lint.vscode_extensions
8
+
9
+ # ── happy path ────────────────────────────────────────────────────────────
10
+
11
+ test_allow_with_required_extension if {
12
+ cfg := {"recommendations": ["stylelint.vscode-stylelint"]}
13
+ count(vscode_extensions.deny) == 0 with input as cfg
14
+ }
15
+
16
+ test_allow_with_additional_extensions if {
17
+ cfg := {"recommendations": [
18
+ "dbaeumer.vscode-eslint",
19
+ "stylelint.vscode-stylelint",
20
+ "oxc.oxc-vscode",
21
+ "DavidAnson.vscode-markdownlint",
22
+ ]}
23
+ count(vscode_extensions.deny) == 0 with input as cfg
24
+ }
25
+
26
+ # ── deny ──────────────────────────────────────────────────────────────────
27
+
28
+ test_deny_missing_extension if {
29
+ cfg := {"recommendations": ["dbaeumer.vscode-eslint"]}
30
+ count(vscode_extensions.deny) > 0 with input as cfg
31
+ }
32
+
33
+ test_deny_empty_recommendations if {
34
+ count(vscode_extensions.deny) > 0 with input as {"recommendations": []}
35
+ }
36
+
37
+ test_deny_no_recommendations_field if {
38
+ count(vscode_extensions.deny) > 0 with input as {}
39
+ }
@@ -0,0 +1,24 @@
1
+ # Перевірка `.vscode/settings.json` для style-lint (style-lint.mdc).
2
+ #
3
+ # Запуск (локально):
4
+ # conftest test .vscode/settings.json -p npm/policy/style_lint/vscode_settings \
5
+ # --namespace style_lint.vscode_settings
6
+ #
7
+ # Canonical (style-lint.mdc): вимкнути вбудовану валідацію CSS/SCSS/Less, щоб
8
+ # stylelint був єдиним джерелом діагностики.
9
+ # { "css.validate": false, "less.validate": false, "scss.validate": false }
10
+ #
11
+ # `editor.codeActionsOnSave` у каноні є, але це smell-test — навмисно не deny,
12
+ # щоб не падати на пакетах, які мають свій codeActionsOnSave-конфіг.
13
+ #
14
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
15
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`.
16
+ package style_lint.vscode_settings
17
+
18
+ import rego.v1
19
+
20
+ deny contains msg if {
21
+ some key in {"css.validate", "less.validate", "scss.validate"}
22
+ object.get(input, key, null) != false
23
+ msg := sprintf(".vscode/settings.json: \"%s\" має бути false (style-lint.mdc)", [key])
24
+ }
@@ -0,0 +1,49 @@
1
+ # Тести для `style_lint.vscode_settings`. Запуск:
2
+ # conftest verify -p npm/policy/style_lint/vscode_settings
3
+ package style_lint.vscode_settings_test
4
+
5
+ import rego.v1
6
+
7
+ import data.style_lint.vscode_settings
8
+
9
+ valid_cfg := {
10
+ "css.validate": false,
11
+ "less.validate": false,
12
+ "scss.validate": false,
13
+ }
14
+
15
+ # ── happy path ────────────────────────────────────────────────────────────
16
+
17
+ test_allow_canonical if {
18
+ count(vscode_settings.deny) == 0 with input as valid_cfg
19
+ }
20
+
21
+ test_allow_with_additional_keys if {
22
+ cfg := json.patch(valid_cfg, [{
23
+ "op": "add",
24
+ "path": "/editor.codeActionsOnSave",
25
+ "value": {"source.fixAll": "explicit"},
26
+ }])
27
+ count(vscode_settings.deny) == 0 with input as cfg
28
+ }
29
+
30
+ # ── deny ──────────────────────────────────────────────────────────────────
31
+
32
+ test_deny_css_validate_true if {
33
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/css.validate", "value": true}])
34
+ count(vscode_settings.deny) > 0 with input as cfg
35
+ }
36
+
37
+ test_deny_scss_validate_missing if {
38
+ cfg := json.patch(valid_cfg, [{"op": "remove", "path": "/scss.validate"}])
39
+ count(vscode_settings.deny) > 0 with input as cfg
40
+ }
41
+
42
+ test_deny_less_validate_string if {
43
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/less.validate", "value": "off"}])
44
+ count(vscode_settings.deny) > 0 with input as cfg
45
+ }
46
+
47
+ test_deny_empty_object if {
48
+ count(vscode_settings.deny) > 0 with input as {}
49
+ }