@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.
- package/CHANGELOG.md +56 -0
- package/package.json +1 -1
- package/policy/graphql/vscode_extensions/vscode_extensions.rego +20 -0
- package/policy/graphql/vscode_extensions/vscode_extensions_test.rego +34 -0
- package/policy/image_avif/package_json/package_json.rego +61 -0
- package/policy/image_avif/package_json/package_json_test.rego +69 -0
- package/policy/js_run/jsconfig/jsconfig_test.rego +88 -0
- package/policy/nginx_default_tpl/vscode_extensions/vscode_extensions.rego +16 -0
- package/policy/nginx_default_tpl/vscode_extensions/vscode_extensions_test.rego +30 -0
- package/policy/nginx_default_tpl/vscode_settings/vscode_settings.rego +36 -0
- package/policy/nginx_default_tpl/vscode_settings/vscode_settings_test.rego +53 -0
- package/policy/style_lint/vscode_extensions/vscode_extensions.rego +23 -0
- package/policy/style_lint/vscode_extensions/vscode_extensions_test.rego +39 -0
- package/policy/style_lint/vscode_settings/vscode_settings.rego +24 -0
- package/policy/style_lint/vscode_settings/vscode_settings_test.rego +49 -0
- package/policy/text/vscode_extensions/vscode_extensions.rego +36 -0
- package/policy/text/vscode_extensions/vscode_extensions_test.rego +51 -0
- package/policy/text/vscode_settings/vscode_settings.rego +56 -0
- package/policy/text/vscode_settings/vscode_settings_test.rego +85 -0
- package/scripts/check-graphql.mjs +18 -26
- package/scripts/check-js-run.mjs +18 -7
- package/scripts/check-nginx-default-tpl.mjs +28 -18
- package/scripts/check-style-lint.mjs +11 -33
- package/scripts/check-text.mjs +8 -66
- 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
|
@@ -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
|
+
}
|