@nitra/cursor 1.8.206 → 1.8.208
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 +36 -0
- package/mdc/js-run.mdc +49 -2
- package/package.json +1 -1
- package/policy/abie/health_check_policy/health_check_policy.rego +73 -0
- package/policy/abie/http_route_base/http_route_base.rego +45 -0
- package/policy/adr/settings_json/settings_json.rego +31 -0
- package/policy/adr/settings_local_json/settings_local_json.rego +28 -0
- package/policy/bun/bunfig/bunfig.rego +33 -0
- package/policy/bun/package_json/package_json.rego +94 -0
- package/policy/capacitor/package_json/package_json.rego +45 -0
- package/policy/ga/clean_ga_workflows/clean_ga_workflows.rego +0 -26
- package/policy/ga/clean_merged_branch/clean_merged_branch.rego +0 -25
- package/policy/ga/git_ai/git_ai.rego +0 -26
- package/policy/ga/lint_ga/lint_ga.rego +0 -26
- package/policy/ga/workflow_common/workflow_common.rego +161 -0
- package/policy/graphql/package_json/package_json.rego +35 -0
- package/policy/hasura/svc_hl/svc_hl.rego +27 -0
- package/policy/image_compress/package_json/package_json.rego +94 -0
- package/policy/js_bun_db/package_json/package_json.rego +28 -0
- package/policy/js_lint/lint_js_yml/lint_js_yml.rego +98 -0
- package/policy/js_lint/package_json/package_json.rego +137 -0
- package/policy/js_mssql/package_json/package_json.rego +57 -0
- package/policy/js_run/configmap/configmap.rego +45 -0
- package/policy/js_run/jsconfig/jsconfig.rego +66 -0
- package/policy/js_run/package_json/package_json.rego +31 -0
- package/policy/k8s/manifest/manifest.rego +130 -0
- package/policy/npm_module/emit_types_config/emit_types_config.rego +37 -0
- package/policy/npm_module/npm_package_json/npm_package_json.rego +55 -0
- package/policy/npm_module/npm_publish_yml/npm_publish_yml.rego +79 -0
- package/policy/npm_module/root_package_json/root_package_json.rego +28 -0
- package/policy/php/lint_php_yml/lint_php_yml.rego +32 -0
- package/policy/php/package_json/package_json.rego +19 -0
- package/policy/style_lint/lint_style_yml/lint_style_yml.rego +35 -0
- package/policy/style_lint/package_json/package_json.rego +49 -0
- package/policy/text/cspell/cspell.rego +91 -0
- package/policy/text/markdownlint/markdownlint.rego +21 -0
- package/policy/text/oxfmtrc/oxfmtrc.rego +90 -0
- package/policy/text/package_json/package_json.rego +88 -0
- package/policy/vue/package_json/package_json.rego +54 -0
- package/scripts/check-adr.mjs +3 -2
- package/scripts/check-bun.mjs +21 -117
- package/scripts/check-graphql.mjs +6 -45
- package/scripts/check-hasura.mjs +2 -3
- package/scripts/check-image-avif.mjs +3 -3
- package/scripts/check-image-compress.mjs +25 -132
- package/scripts/check-js-bun-db.mjs +3 -50
- package/scripts/check-js-run.mjs +84 -86
- package/scripts/check-k8s.mjs +4 -4
- package/scripts/check-npm-module.mjs +17 -8
- package/scripts/check-php.mjs +16 -51
- package/scripts/check-style-lint.mjs +28 -52
- package/scripts/check-text.mjs +47 -219
- package/scripts/check-vue.mjs +3 -16
- package/scripts/lint-conftest.mjs +351 -0
- package/scripts/lint-ga.mjs +39 -2
- package/scripts/run-shellcheck-text.mjs +2 -2
- package/scripts/utils/conn-file-rules.mjs +170 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Порт перевірок `package.json` з `npm/scripts/check-style-lint.mjs` (style-lint.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test package.json -p npm/policy/style_lint --namespace style_lint.package_json
|
|
5
|
+
#
|
|
6
|
+
# Перевіряє: наявність скрипта `lint-style` з `npx stylelint`, `@nitra/stylelint-config`
|
|
7
|
+
# у `devDependencies`, поле `stylelint.extends == "@nitra/stylelint-config"`. FS-частина
|
|
8
|
+
# (зовнішні `.stylelintrc.*` як альтернатива полю; `.stylelintignore`) лишається у JS.
|
|
9
|
+
#
|
|
10
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
11
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
12
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
13
|
+
package style_lint.package_json
|
|
14
|
+
|
|
15
|
+
import rego.v1
|
|
16
|
+
|
|
17
|
+
# ── deny: lint-style скрипт ───────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
deny contains msg if {
|
|
20
|
+
not object.get(object.get(input, "scripts", {}), "lint-style", false)
|
|
21
|
+
msg := "package.json не містить скрипт \"lint-style\" (style-lint.mdc)"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
deny contains msg if {
|
|
25
|
+
lint_style := object.get(object.get(input, "scripts", {}), "lint-style", "")
|
|
26
|
+
lint_style != ""
|
|
27
|
+
not contains(lint_style, "npx stylelint")
|
|
28
|
+
msg := sprintf("lint-style має викликати stylelint через npx (зараз: %q) (style-lint.mdc)", [lint_style])
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# ── deny: @nitra/stylelint-config у devDependencies ───────────────────────
|
|
32
|
+
|
|
33
|
+
deny contains msg if {
|
|
34
|
+
dev := object.get(input, "devDependencies", {})
|
|
35
|
+
not "@nitra/stylelint-config" in object.keys(dev)
|
|
36
|
+
msg := "@nitra/stylelint-config відсутній — bun add -d @nitra/stylelint-config (style-lint.mdc)"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# ── deny: поле stylelint.extends = "@nitra/stylelint-config" ──────────────
|
|
40
|
+
#
|
|
41
|
+
# JS-перевірка дозволяє альтернативу: окремий файл `.stylelintrc.*`. Цю частину
|
|
42
|
+
# перевіряємо в JS (FS-вибірка); тут — лише структурна валідація поля, якщо воно є.
|
|
43
|
+
|
|
44
|
+
deny contains msg if {
|
|
45
|
+
cfg := object.get(input, "stylelint", null)
|
|
46
|
+
is_object(cfg)
|
|
47
|
+
object.get(cfg, "extends", null) != "@nitra/stylelint-config"
|
|
48
|
+
msg := "package.json: stylelint.extends має бути \"@nitra/stylelint-config\" (style-lint.mdc)"
|
|
49
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Порт перевірок `.cspell.json` з `npm/scripts/check-text.mjs` (text.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test .cspell.json -p npm/policy/text --namespace text.cspell
|
|
5
|
+
#
|
|
6
|
+
# Перевіряє: `version: "0.2"`, наявність `language`, імпорт `@nitra/cspell-dict`,
|
|
7
|
+
# відсутність прямих імпортів `@cspell/dict-*`, обовʼязкові glob-и в `ignorePaths`
|
|
8
|
+
# (text.mdc).
|
|
9
|
+
#
|
|
10
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
11
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
12
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
13
|
+
package text.cspell
|
|
14
|
+
|
|
15
|
+
import rego.v1
|
|
16
|
+
|
|
17
|
+
# ── Очікувані значення ─────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
# Канонічні `ignorePaths` з text.mdc — кожен має бути присутнім.
|
|
20
|
+
required_ignore_paths := {
|
|
21
|
+
"**/node_modules/**",
|
|
22
|
+
"**/vscode-extension/**",
|
|
23
|
+
"**/.git/**",
|
|
24
|
+
".vscode",
|
|
25
|
+
"report",
|
|
26
|
+
"*.svg",
|
|
27
|
+
"**/k8s/**/*.yaml",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
nitra_cspell_dict_marker := "@nitra/cspell-dict"
|
|
31
|
+
|
|
32
|
+
legacy_dict_marker := "@cspell/dict-"
|
|
33
|
+
|
|
34
|
+
# Шаблон повідомлення про заборонений імпорт `@cspell/dict-*` — через `concat`
|
|
35
|
+
# для regal style/line-length.
|
|
36
|
+
legacy_dict_import_template := concat(" ", [
|
|
37
|
+
".cspell.json не має імпортувати @cspell/dict-* —",
|
|
38
|
+
"використовуй лише @nitra/cspell-dict (знайдено: %s) (text.mdc)",
|
|
39
|
+
])
|
|
40
|
+
|
|
41
|
+
# ── deny: version / language ──────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
deny contains msg if {
|
|
44
|
+
object.get(input, "version", null) != "0.2"
|
|
45
|
+
msg := ".cspell.json: version має бути \"0.2\" (text.mdc)"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
deny contains msg if {
|
|
49
|
+
not object.get(input, "language", false)
|
|
50
|
+
msg := ".cspell.json: відсутнє поле language (text.mdc)"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# ── deny: imports ─────────────────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
deny contains msg if {
|
|
56
|
+
imports := object.get(input, "import", [])
|
|
57
|
+
is_array(imports)
|
|
58
|
+
not has_nitra_dict_import(imports)
|
|
59
|
+
msg := ".cspell.json не імпортує @nitra/cspell-dict/cspell-ext.json (text.mdc)"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
deny contains msg if {
|
|
63
|
+
imports := object.get(input, "import", [])
|
|
64
|
+
is_array(imports)
|
|
65
|
+
some imp in imports
|
|
66
|
+
is_string(imp)
|
|
67
|
+
contains(imp, legacy_dict_marker)
|
|
68
|
+
msg := sprintf(legacy_dict_import_template, [imp])
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# ── deny: ignorePaths ─────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
deny contains msg if {
|
|
74
|
+
not is_array(object.get(input, "ignorePaths", null))
|
|
75
|
+
msg := ".cspell.json: додай масив ignorePaths з канонічними glob-ами (text.mdc)"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
deny contains msg if {
|
|
79
|
+
is_array(input.ignorePaths)
|
|
80
|
+
some path in required_ignore_paths
|
|
81
|
+
not path in {p | some p in input.ignorePaths}
|
|
82
|
+
msg := sprintf(".cspell.json ignorePaths: додай %q (text.mdc)", [path])
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# ── helpers ────────────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
has_nitra_dict_import(imports) if {
|
|
88
|
+
some imp in imports
|
|
89
|
+
is_string(imp)
|
|
90
|
+
contains(imp, nitra_cspell_dict_marker)
|
|
91
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Порт перевірки `.markdownlint-cli2.jsonc` з `npm/scripts/check-text.mjs` (text.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test .markdownlint-cli2.jsonc -p npm/policy/text \
|
|
5
|
+
# --namespace text.markdownlint --parser json
|
|
6
|
+
#
|
|
7
|
+
# Конфтест парсить `.jsonc` як JSON лише якщо файл — валідний JSON (без коментарів).
|
|
8
|
+
# У випадку справжнього JSONC з `//` коментарями цей крок мовчки ігноруватиметься
|
|
9
|
+
# (conftest skip). FS-перевірка (наявність файлу) живе у JS.
|
|
10
|
+
#
|
|
11
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
12
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
13
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
14
|
+
package text.markdownlint
|
|
15
|
+
|
|
16
|
+
import rego.v1
|
|
17
|
+
|
|
18
|
+
deny contains msg if {
|
|
19
|
+
object.get(input, "gitignore", null) != true
|
|
20
|
+
msg := ".markdownlint-cli2.jsonc: додай на верхньому рівні \"gitignore\": true (text.mdc)"
|
|
21
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Порт перевірок `.oxfmtrc.json` з `npm/scripts/check-text.mjs` (text.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test .oxfmtrc.json -p npm/policy/text --namespace text.oxfmtrc
|
|
5
|
+
#
|
|
6
|
+
# Перевіряє: обовʼязкові ключі, канонічні значення (`semi=false`, `singleQuote=true`,
|
|
7
|
+
# `tabWidth=2`, `useTabs=false`, `printWidth=120`), масив `ignorePatterns` з
|
|
8
|
+
# канонічними glob-ами (hasura/metadata, schema.graphql, auto-imports.d.ts).
|
|
9
|
+
#
|
|
10
|
+
# FS-перевірки (наявність самого `.oxfmtrc.json`, `.prettierrc.*` файлів) живуть
|
|
11
|
+
# у `check-text.mjs`. Тут — лише про вже завантажений input.
|
|
12
|
+
#
|
|
13
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
14
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
15
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
16
|
+
package text.oxfmtrc
|
|
17
|
+
|
|
18
|
+
import rego.v1
|
|
19
|
+
|
|
20
|
+
# ── Очікувані значення ─────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
required_keys := [
|
|
23
|
+
"arrowParens",
|
|
24
|
+
"printWidth",
|
|
25
|
+
"bracketSpacing",
|
|
26
|
+
"bracketSameLine",
|
|
27
|
+
"semi",
|
|
28
|
+
"singleQuote",
|
|
29
|
+
"tabWidth",
|
|
30
|
+
"trailingComma",
|
|
31
|
+
"useTabs",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
required_ignore_patterns := {
|
|
35
|
+
"**/hasura/metadata/**",
|
|
36
|
+
"**/schema.graphql",
|
|
37
|
+
"**/auto-imports.d.ts",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# ── deny: обовʼязкові ключі ────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
deny contains msg if {
|
|
43
|
+
some key in required_keys
|
|
44
|
+
not key in object.keys(input)
|
|
45
|
+
msg := sprintf(".oxfmtrc.json: відсутній обовʼязковий ключ %q (text.mdc)", [key])
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# ── deny: канонічні значення ───────────────────────────────────────────────
|
|
49
|
+
#
|
|
50
|
+
# `object.get(…, sentinel)` робить значення визначеним — інакше при відсутньому
|
|
51
|
+
# ключі порівняння дало б `undefined`, не `true`, і правило мовчки не спрацювало б.
|
|
52
|
+
|
|
53
|
+
deny contains msg if {
|
|
54
|
+
object.get(input, "semi", null) != false
|
|
55
|
+
msg := ".oxfmtrc.json: semi має бути false (text.mdc)"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
deny contains msg if {
|
|
59
|
+
object.get(input, "singleQuote", null) != true
|
|
60
|
+
msg := ".oxfmtrc.json: singleQuote має бути true (text.mdc)"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
deny contains msg if {
|
|
64
|
+
object.get(input, "tabWidth", null) != 2
|
|
65
|
+
msg := ".oxfmtrc.json: tabWidth має бути 2 (text.mdc)"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
deny contains msg if {
|
|
69
|
+
object.get(input, "useTabs", null) != false
|
|
70
|
+
msg := ".oxfmtrc.json: useTabs має бути false (text.mdc)"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
deny contains msg if {
|
|
74
|
+
object.get(input, "printWidth", null) != 120
|
|
75
|
+
msg := ".oxfmtrc.json: printWidth має бути 120 (text.mdc)"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# ── deny: ignorePatterns ───────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
deny contains msg if {
|
|
81
|
+
not is_array(object.get(input, "ignorePatterns", null))
|
|
82
|
+
msg := ".oxfmtrc.json: додай масив ignorePatterns з канонічними glob-ами (text.mdc)"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
deny contains msg if {
|
|
86
|
+
is_array(input.ignorePatterns)
|
|
87
|
+
some pattern in required_ignore_patterns
|
|
88
|
+
not pattern in {p | some p in input.ignorePatterns}
|
|
89
|
+
msg := sprintf(".oxfmtrc.json ignorePatterns: додай %q (text.mdc)", [pattern])
|
|
90
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Порт текст-специфічних перевірок `package.json` з `npm/scripts/check-text.mjs` (text.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test package.json -p npm/policy/text --namespace text.package_json
|
|
5
|
+
#
|
|
6
|
+
# Перевіряє: відсутність Prettier (поле + конфіги в deps), `@nitra/cspell-dict ^2.0.0+`
|
|
7
|
+
# у `devDependencies`, заборона `markdownlint-cli2` у dependencies/devDependencies.
|
|
8
|
+
#
|
|
9
|
+
# Перевірка скрипта `lint-text` (cspell, run-shellcheck-text.mjs, markdownlint, v8r,
|
|
10
|
+
# обовʼязкові glob-и для v8r) — у JS-частині (`check-text.mjs`): занадто варіативна
|
|
11
|
+
# для декларативної політики (3 режими v8r з різними вимогами до глобів).
|
|
12
|
+
#
|
|
13
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
14
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
15
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
16
|
+
package text.package_json
|
|
17
|
+
|
|
18
|
+
import rego.v1
|
|
19
|
+
|
|
20
|
+
# ── Заборонені пакети у dependencies/devDependencies ──────────────────────
|
|
21
|
+
|
|
22
|
+
forbidden_packages := {
|
|
23
|
+
"prettier": "Prettier заборонено — використовуй oxfmt (text.mdc)",
|
|
24
|
+
"@nitra/prettier-config": "Prettier-конфіг заборонено — використовуй oxfmt (text.mdc)",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# ── deny: заборонене поле `prettier` у package.json ───────────────────────
|
|
28
|
+
|
|
29
|
+
deny contains msg if {
|
|
30
|
+
object.get(input, "prettier", null) != null
|
|
31
|
+
msg := "package.json містить поле \"prettier\" — видали його (text.mdc)"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# ── deny: prettier у dependencies/devDependencies ─────────────────────────
|
|
35
|
+
|
|
36
|
+
deny contains msg if {
|
|
37
|
+
some pkg, hint in forbidden_packages
|
|
38
|
+
pkg in object.keys(object.get(input, "dependencies", {}))
|
|
39
|
+
msg := sprintf("package.json: dependencies містить %q — %s", [pkg, hint])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
deny contains msg if {
|
|
43
|
+
some pkg, hint in forbidden_packages
|
|
44
|
+
pkg in object.keys(object.get(input, "devDependencies", {}))
|
|
45
|
+
msg := sprintf("package.json: devDependencies містить %q — %s", [pkg, hint])
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# ── deny: markdownlint-cli2 не повинен бути у залежностях ─────────────────
|
|
49
|
+
#
|
|
50
|
+
# Канонічний виклик — `bunx markdownlint-cli2` у `lint-text`, без оголошення пакета.
|
|
51
|
+
|
|
52
|
+
deny contains msg if {
|
|
53
|
+
"markdownlint-cli2" in object.keys(object.get(input, "dependencies", {}))
|
|
54
|
+
msg := "package.json: dependencies містить markdownlint-cli2 — використовуй bunx у lint-text (text.mdc)"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
deny contains msg if {
|
|
58
|
+
"markdownlint-cli2" in object.keys(object.get(input, "devDependencies", {}))
|
|
59
|
+
msg := "package.json: devDependencies містить markdownlint-cli2 — використовуй bunx у lint-text (text.mdc)"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# ── deny: @nitra/cspell-dict ^2.0.0+ обовʼязковий ─────────────────────────
|
|
63
|
+
|
|
64
|
+
deny contains msg if {
|
|
65
|
+
dev := object.get(input, "devDependencies", {})
|
|
66
|
+
not "@nitra/cspell-dict" in object.keys(dev)
|
|
67
|
+
msg := "@nitra/cspell-dict у devDependencies обовʼязковий — bun add -d @nitra/cspell-dict@^2.0.0 (text.mdc)"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
deny contains msg if {
|
|
71
|
+
range := object.get(object.get(input, "devDependencies", {}), "@nitra/cspell-dict", "")
|
|
72
|
+
range != ""
|
|
73
|
+
not cspell_dict_major_at_least_2(range)
|
|
74
|
+
msg := sprintf("@nitra/cspell-dict має бути ^2.0.0 або новіший (зараз %q) (text.mdc)", [range])
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# ── helpers ────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
# Чи мажорна версія cspell-dict ≥ 2. Підтримує `^2.0.0`, `~2.x`, `2.5.0`,
|
|
80
|
+
# `>=2.0.0`, `workspace:*` (тоді fallback false), із префіксом і без.
|
|
81
|
+
# Regex `^[\^~>=<]*\s*(\d+)` дістає першу цифру після опціональних range-операторів.
|
|
82
|
+
cspell_dict_major_at_least_2(range) if {
|
|
83
|
+
# `regex.find_n` повертає масив збігів; беремо перший і дивимось на перше число.
|
|
84
|
+
match := regex.find_n(`^[\^~>=<]*\s*(\d+)`, range, 1)
|
|
85
|
+
count(match) > 0
|
|
86
|
+
major := to_number(regex.replace(match[0], `^[\^~>=<]*\s*`, ""))
|
|
87
|
+
major >= 2
|
|
88
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Порт перевірки версій з `package.json` для Vue+Vite пакетів з
|
|
2
|
+
# `npm/scripts/check-vue.mjs` (vue.mdc).
|
|
3
|
+
#
|
|
4
|
+
# Запуск (локально, у Vue+Vite-пакеті):
|
|
5
|
+
# conftest test path/to/package.json -p npm/policy/vue \
|
|
6
|
+
# --namespace vue.package_json
|
|
7
|
+
#
|
|
8
|
+
# Перевіряє: якщо в `dependencies` є `vue`, то у `devDependencies.vite` має бути
|
|
9
|
+
# мажорна версія ≥ 8.
|
|
10
|
+
#
|
|
11
|
+
# AST-сканування коду (заборона явних value-імпортів `from 'vue'`, заборона
|
|
12
|
+
# Node-нативних модулів у `.vue` SFC, перевірка `vite.config` на
|
|
13
|
+
# `process.env.npm_lifecycle_event`, vue-macros, auto-import тощо), а також
|
|
14
|
+
# FS-перевірки (`src/vite-env.d.ts`, `jsconfig.json` у корені пакета) — у JS.
|
|
15
|
+
#
|
|
16
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
17
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
18
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
19
|
+
package vue.package_json
|
|
20
|
+
|
|
21
|
+
import rego.v1
|
|
22
|
+
|
|
23
|
+
deny contains msg if {
|
|
24
|
+
uses_vue
|
|
25
|
+
not vite_in_dev_dependencies
|
|
26
|
+
msg := "Vue-пакет: відсутня залежність `vite` у devDependencies (vue.mdc)"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
deny contains msg if {
|
|
30
|
+
uses_vue
|
|
31
|
+
vite_in_dev_dependencies
|
|
32
|
+
not vite_major_at_least_8
|
|
33
|
+
vite_range := input.devDependencies.vite
|
|
34
|
+
msg := sprintf("Vue-пакет: vite має бути >= 8 (зараз %q) (vue.mdc)", [vite_range])
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# ── helpers ────────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
uses_vue if {
|
|
40
|
+
"vue" in object.keys(object.get(input, "dependencies", {}))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
vite_in_dev_dependencies if {
|
|
44
|
+
"vite" in object.keys(object.get(input, "devDependencies", {}))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
vite_major_at_least_8 if {
|
|
48
|
+
range := input.devDependencies.vite
|
|
49
|
+
|
|
50
|
+
# Перша мажорна цифра з рядка: `^8`, `>=8.0.0`, `8.x` → 8.
|
|
51
|
+
match := regex.find_n(`\d+`, range, 1)
|
|
52
|
+
count(match) > 0
|
|
53
|
+
to_number(match[0]) >= 8
|
|
54
|
+
}
|
package/scripts/check-adr.mjs
CHANGED
|
@@ -26,6 +26,7 @@ const PROJECT_SETTINGS_PATH = '.claude/settings.json'
|
|
|
26
26
|
const PROJECT_LOCAL_SETTINGS_PATH = '.claude/settings.local.json'
|
|
27
27
|
const PROJECT_LOG_PATH = '.claude/hooks/capture-decisions.log'
|
|
28
28
|
const HOOK_COMMAND_MARKER = '.claude/hooks/capture-decisions.sh'
|
|
29
|
+
const EOL_RE = /\r?\n/u
|
|
29
30
|
|
|
30
31
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
31
32
|
/** Канонічний bundled-скрипт у пакеті — джерело правди для звірки з проєктним. */
|
|
@@ -183,9 +184,9 @@ async function checkGitignore(reporter) {
|
|
|
183
184
|
}
|
|
184
185
|
const content = await readFile('.gitignore', 'utf8')
|
|
185
186
|
const covers = content
|
|
186
|
-
.split(
|
|
187
|
+
.split(EOL_RE)
|
|
187
188
|
.map(l => l.trim())
|
|
188
|
-
.some(gitignoreLineCoversHookLog)
|
|
189
|
+
.some(line => gitignoreLineCoversHookLog(line))
|
|
189
190
|
if (covers) {
|
|
190
191
|
pass(`.gitignore покриває ${PROJECT_LOG_PATH}`)
|
|
191
192
|
} else {
|
package/scripts/check-bun.mjs
CHANGED
|
@@ -1,54 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Перевіряє відповідність репозиторію правилам Bun (bun.mdc).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* **Що тут лишилося** (FS / cross-file — не покривається conftest):
|
|
5
|
+
* - наявність `bun.lock`, `bunfig.toml`, `package.json` у корені (FS-existence);
|
|
6
|
+
* - заборонені lockfile та артефакти yarn/pnpm (`package-lock.json`, `yarn.lock`,
|
|
7
|
+
* `pnpm-lock.yaml`, `.yarnrc.yml`, директорія `.yarn/`);
|
|
8
|
+
* - якщо в `.n-cursor.json` у `rules` є `docker` або `k8s`, у кореневому
|
|
9
|
+
* `package.json` має бути відповідний скрипт `lint-docker` / `lint-k8s`
|
|
10
|
+
* (cross-file: два JSON-файли).
|
|
7
11
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* Якщо в кореневому `package.json` є скрипти з префіксом `lint-`, перевіряє наявність агрегованого
|
|
15
|
-
* скрипта `lint`, у якому через `bun run <ім’я>` викликаються всі такі скрипти, і що рядок `lint`
|
|
16
|
-
* закінчується на `&& oxfmt .`.
|
|
12
|
+
* **Що покрила Rego** (`bun run lint-conftest`):
|
|
13
|
+
* - `npm/policy/bun/bunfig/` — `[install].linker == "hoisted"` у `bunfig.toml`;
|
|
14
|
+
* - `npm/policy/bun/package_json/` — відсутність `packageManager` / `dependencies`
|
|
15
|
+
* у кореневому `package.json`, у `devDependencies` лише `@nitra/*`, агрегований
|
|
16
|
+
* `lint`-скрипт покриває всі `lint-*` через `bun run` і завершується `&& oxfmt .`.
|
|
17
17
|
*/
|
|
18
18
|
import { existsSync } from 'node:fs'
|
|
19
19
|
import { readFile } from 'node:fs/promises'
|
|
20
20
|
|
|
21
21
|
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
22
22
|
|
|
23
|
-
const OXFMT_END_RE = /&&[ \t]+oxfmt[ \t]+\.[ \t]*$/
|
|
24
|
-
/** Пробіли/таби без `\s` (уникаємо super-linear backtracking у sonarjs/slow-regex). */
|
|
25
|
-
const HOISTED_LINKER_RE = /^[ \t]*linker[ \t]*=[ \t]*"hoisted"[ \t]*$/m
|
|
26
|
-
const INSTALL_SECTION_RE = /^[ \t]*\[install\][ \t]*$/m
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Перевіряє `bunfig.toml` на секцію `[install]` з `linker = "hoisted"`.
|
|
30
|
-
* @param {{ pass: (msg: string) => void, fail: (msg: string) => void }} reporter репортер
|
|
31
|
-
*/
|
|
32
|
-
async function checkBunfigHoisted(reporter) {
|
|
33
|
-
const { pass, fail } = reporter
|
|
34
|
-
if (!existsSync('bunfig.toml')) {
|
|
35
|
-
fail('Відсутній bunfig.toml — створи з [install] linker = "hoisted" (bun.mdc)')
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
const content = await readFile('bunfig.toml', 'utf8')
|
|
39
|
-
if (!INSTALL_SECTION_RE.test(content)) {
|
|
40
|
-
fail('bunfig.toml: відсутня секція [install] (bun.mdc)')
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
if (HOISTED_LINKER_RE.test(content)) {
|
|
44
|
-
pass('bunfig.toml: [install] linker = "hoisted"')
|
|
45
|
-
} else {
|
|
46
|
-
fail('bunfig.toml: у секції [install] має бути linker = "hoisted" (bun.mdc)')
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
23
|
/**
|
|
51
24
|
* Чи ім'я пакета дозволене в кореневих `devDependencies` за bun.mdc (лише **`@nitra/*`**).
|
|
25
|
+
*
|
|
26
|
+
* Залишилася як експорт для `check-text.mjs` і тестів — `bun.package_json` Rego
|
|
27
|
+
* робить ту саму перевірку для check-runner-а.
|
|
52
28
|
* @param {string} name ключ з поля `devDependencies`
|
|
53
29
|
* @returns {boolean} true, якщо префікс дозволений
|
|
54
30
|
*/
|
|
@@ -76,66 +52,6 @@ async function loadNCursorRules() {
|
|
|
76
52
|
}
|
|
77
53
|
}
|
|
78
54
|
|
|
79
|
-
/**
|
|
80
|
-
* @param {{ pass: (msg: string) => void, fail: (msg: string) => void }} reporter репортер для збору результатів
|
|
81
|
-
* @param {Record<string, unknown>} pkg розібраний package.json
|
|
82
|
-
*/
|
|
83
|
-
function checkDevDependencies(reporter, pkg) {
|
|
84
|
-
const { pass, fail } = reporter
|
|
85
|
-
const dev = pkg.devDependencies
|
|
86
|
-
if (dev === undefined) {
|
|
87
|
-
pass('Кореневий package.json без devDependencies')
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
if (dev === null || typeof dev !== 'object' || Array.isArray(dev)) {
|
|
91
|
-
fail(
|
|
92
|
-
'Кореневий package.json: `devDependencies` має бути object з ключами пакетів і діапазонами версій (не null, не масив)'
|
|
93
|
-
)
|
|
94
|
-
return
|
|
95
|
-
}
|
|
96
|
-
const bad = Object.keys(/** @type {object} */ (dev)).filter(n => !isAllowedRootDevDependency(n))
|
|
97
|
-
if (bad.length > 0) {
|
|
98
|
-
fail(`Кореневі devDependencies: дозволені лише @nitra/* — прибери або перенеси: ${bad.join(', ')} (bun.mdc)`)
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
const n = Object.keys(/** @type {object} */ (dev)).length
|
|
102
|
-
pass(
|
|
103
|
-
n === 0
|
|
104
|
-
? 'Кореневі devDependencies порожні або відсутні (лише @nitra/*)'
|
|
105
|
-
: `Кореневі devDependencies: лише @nitra/* (${n} пак.)`
|
|
106
|
-
)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* @param {{ pass: (msg: string) => void, fail: (msg: string) => void }} reporter репортер для збору результатів
|
|
111
|
-
* @param {Record<string, string>} scripts scripts з package.json
|
|
112
|
-
*/
|
|
113
|
-
function checkLintAggregate(reporter, scripts) {
|
|
114
|
-
const { pass, fail } = reporter
|
|
115
|
-
const lintPrefixed = Object.keys(scripts).filter(name => name.startsWith('lint-'))
|
|
116
|
-
if (lintPrefixed.length === 0) return
|
|
117
|
-
const aggregate = typeof scripts.lint === 'string' ? scripts.lint : ''
|
|
118
|
-
if (!aggregate.trim()) {
|
|
119
|
-
const scriptList = lintPrefixed.map(s => `\`${s}\``).join(', ')
|
|
120
|
-
fail(
|
|
121
|
-
`У package.json є скрипти ${scriptList}, але немає агрегованого \`lint\` — додай скрипт, який запускає їх через \`bun run\``
|
|
122
|
-
)
|
|
123
|
-
return
|
|
124
|
-
}
|
|
125
|
-
const missing = lintPrefixed.filter(name => !aggregate.includes(`bun run ${name}`))
|
|
126
|
-
if (missing.length > 0) {
|
|
127
|
-
const missingList = missing.map(s => '`' + s + '`').join(', ')
|
|
128
|
-
fail(`Скрипт \`lint\` має викликати всі lint-* через bun run; відсутньо: ${missingList}`)
|
|
129
|
-
return
|
|
130
|
-
}
|
|
131
|
-
pass('package.json: агрегований `lint` покриває всі `lint-*` скрипти')
|
|
132
|
-
if (OXFMT_END_RE.test(aggregate.trim())) {
|
|
133
|
-
pass('package.json: `lint` завершується `&& oxfmt .`')
|
|
134
|
-
} else {
|
|
135
|
-
fail('Скрипт `lint` має закінчуватися на `&& oxfmt .`')
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
55
|
/**
|
|
140
56
|
* @param {{ pass: (msg: string) => void, fail: (msg: string) => void }} reporter репортер для збору результатів
|
|
141
57
|
* @param {Record<string, string>} scripts scripts з package.json
|
|
@@ -188,34 +104,22 @@ export async function check() {
|
|
|
188
104
|
fail('Відсутній bun.lock — запусти bun i')
|
|
189
105
|
}
|
|
190
106
|
|
|
191
|
-
|
|
107
|
+
if (!existsSync('bunfig.toml')) {
|
|
108
|
+
fail('Відсутній bunfig.toml — створи з [install] linker = "hoisted" (bun.mdc)')
|
|
109
|
+
} else {
|
|
110
|
+
pass('bunfig.toml є (структуру перевіряє bun run lint-conftest → bun.bunfig)')
|
|
111
|
+
}
|
|
192
112
|
|
|
193
113
|
const cursorRules = await loadNCursorRules()
|
|
194
114
|
|
|
195
115
|
if (!existsSync('package.json')) {
|
|
116
|
+
fail('Відсутній package.json у корені')
|
|
196
117
|
return reporter.getExitCode()
|
|
197
118
|
}
|
|
198
119
|
|
|
199
120
|
const pkg = JSON.parse(await readFile('package.json', 'utf8'))
|
|
200
|
-
if (pkg.packageManager) {
|
|
201
|
-
fail(`package.json містить поле packageManager: "${pkg.packageManager}" — видали його`)
|
|
202
|
-
} else {
|
|
203
|
-
pass('package.json не містить packageManager')
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (pkg.dependencies === undefined) {
|
|
207
|
-
pass('Кореневий package.json без поля `dependencies`')
|
|
208
|
-
} else {
|
|
209
|
-
fail(
|
|
210
|
-
'Кореневий package.json не повинен містити поле `dependencies` — додай залежності в workspace-пакети (bun.mdc)'
|
|
211
|
-
)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
checkDevDependencies(reporter, pkg)
|
|
215
|
-
|
|
216
121
|
const scripts = pkg.scripts && typeof pkg.scripts === 'object' ? pkg.scripts : {}
|
|
217
122
|
checkCursorRuleScripts(reporter, scripts, cursorRules)
|
|
218
|
-
checkLintAggregate(reporter, scripts)
|
|
219
123
|
|
|
220
124
|
return reporter.getExitCode()
|
|
221
125
|
}
|