@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
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Перевірка `.vscode/extensions.json` для text (text.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test .vscode/extensions.json -p npm/policy/text/vscode_extensions \
|
|
5
|
+
# --namespace text.vscode_extensions
|
|
6
|
+
#
|
|
7
|
+
# Canonical (text.mdc): у `recommendations` мають бути три розширення
|
|
8
|
+
# - DavidAnson.vscode-markdownlint
|
|
9
|
+
# - oxc.oxc-vscode
|
|
10
|
+
# - timonwong.shellcheck
|
|
11
|
+
#
|
|
12
|
+
# Канон задає мінімум — додаткові записи (від інших правил) дозволені.
|
|
13
|
+
#
|
|
14
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
15
|
+
package text.vscode_extensions
|
|
16
|
+
|
|
17
|
+
import rego.v1
|
|
18
|
+
|
|
19
|
+
required_extensions := {
|
|
20
|
+
"DavidAnson.vscode-markdownlint",
|
|
21
|
+
"oxc.oxc-vscode",
|
|
22
|
+
"timonwong.shellcheck",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
missing_extension_template := ".vscode/extensions.json: recommendations має містити %q (text.mdc)"
|
|
26
|
+
|
|
27
|
+
# Множина усіх записів `recommendations` (вираз поза deny — щоб regal не лаявся
|
|
28
|
+
# performance/non-loop-expression: інакше `object.get` виконувався б на кожній
|
|
29
|
+
# ітерації по `required_extensions`).
|
|
30
|
+
recommendations_set := {r | some r in object.get(input, "recommendations", [])}
|
|
31
|
+
|
|
32
|
+
deny contains msg if {
|
|
33
|
+
some required in required_extensions
|
|
34
|
+
not required in recommendations_set
|
|
35
|
+
msg := sprintf(missing_extension_template, [required])
|
|
36
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Тести для `text.vscode_extensions`. Запуск:
|
|
2
|
+
# conftest verify -p npm/policy/text/vscode_extensions
|
|
3
|
+
package text.vscode_extensions_test
|
|
4
|
+
|
|
5
|
+
import rego.v1
|
|
6
|
+
|
|
7
|
+
import data.text.vscode_extensions
|
|
8
|
+
|
|
9
|
+
canonical := {"recommendations": [
|
|
10
|
+
"DavidAnson.vscode-markdownlint",
|
|
11
|
+
"oxc.oxc-vscode",
|
|
12
|
+
"timonwong.shellcheck",
|
|
13
|
+
]}
|
|
14
|
+
|
|
15
|
+
test_allow_canonical if {
|
|
16
|
+
count(vscode_extensions.deny) == 0 with input as canonical
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
test_allow_with_additional_extensions if {
|
|
20
|
+
cfg := {"recommendations": [
|
|
21
|
+
"DavidAnson.vscode-markdownlint",
|
|
22
|
+
"oxc.oxc-vscode",
|
|
23
|
+
"timonwong.shellcheck",
|
|
24
|
+
"dbaeumer.vscode-eslint",
|
|
25
|
+
"stylelint.vscode-stylelint",
|
|
26
|
+
]}
|
|
27
|
+
count(vscode_extensions.deny) == 0 with input as cfg
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
test_deny_missing_markdownlint if {
|
|
31
|
+
cfg := {"recommendations": ["oxc.oxc-vscode", "timonwong.shellcheck"]}
|
|
32
|
+
count(vscode_extensions.deny) > 0 with input as cfg
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
test_deny_missing_oxc if {
|
|
36
|
+
cfg := {"recommendations": ["DavidAnson.vscode-markdownlint", "timonwong.shellcheck"]}
|
|
37
|
+
count(vscode_extensions.deny) > 0 with input as cfg
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
test_deny_missing_shellcheck if {
|
|
41
|
+
cfg := {"recommendations": ["DavidAnson.vscode-markdownlint", "oxc.oxc-vscode"]}
|
|
42
|
+
count(vscode_extensions.deny) > 0 with input as cfg
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
test_deny_empty_recommendations if {
|
|
46
|
+
count(vscode_extensions.deny) > 0 with input as {"recommendations": []}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
test_deny_no_recommendations_field if {
|
|
50
|
+
count(vscode_extensions.deny) > 0 with input as {}
|
|
51
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Перевірка `.vscode/settings.json` для text (text.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test .vscode/settings.json -p npm/policy/text/vscode_settings \
|
|
5
|
+
# --namespace text.vscode_settings
|
|
6
|
+
#
|
|
7
|
+
# Canonical (text.mdc):
|
|
8
|
+
# { "editor.formatOnSave": true,
|
|
9
|
+
# "[javascript]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
|
|
10
|
+
# "[typescript]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
|
|
11
|
+
# "[json]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
|
|
12
|
+
# "[vue]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
|
|
13
|
+
# "[css]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
|
|
14
|
+
# "[html]": { "editor.defaultFormatter": "oxc.oxc-vscode" } }
|
|
15
|
+
#
|
|
16
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
17
|
+
package text.vscode_settings
|
|
18
|
+
|
|
19
|
+
import rego.v1
|
|
20
|
+
|
|
21
|
+
language_keys := {"[javascript]", "[typescript]", "[json]", "[vue]", "[css]", "[html]"}
|
|
22
|
+
|
|
23
|
+
# Шаблони повідомлень — через `concat` для regal style/line-length.
|
|
24
|
+
lang_block_not_object_template := concat(" ", [
|
|
25
|
+
".vscode/settings.json: %q має бути обʼєктом з",
|
|
26
|
+
"\"editor.defaultFormatter\": \"oxc.oxc-vscode\" (text.mdc)",
|
|
27
|
+
])
|
|
28
|
+
|
|
29
|
+
lang_wrong_formatter_template := concat(" ", [
|
|
30
|
+
".vscode/settings.json: %q має використовувати",
|
|
31
|
+
"\"oxc.oxc-vscode\" як editor.defaultFormatter (text.mdc)",
|
|
32
|
+
])
|
|
33
|
+
|
|
34
|
+
# ── deny: editor.formatOnSave ────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
deny contains msg if {
|
|
37
|
+
object.get(input, "editor.formatOnSave", null) != true
|
|
38
|
+
msg := ".vscode/settings.json: \"editor.formatOnSave\" має бути true (text.mdc)"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# ── deny: [lang].editor.defaultFormatter ────────────────────────────────
|
|
42
|
+
|
|
43
|
+
deny contains msg if {
|
|
44
|
+
some key in language_keys
|
|
45
|
+
block := object.get(input, key, {})
|
|
46
|
+
not is_object(block)
|
|
47
|
+
msg := sprintf(lang_block_not_object_template, [key])
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
deny contains msg if {
|
|
51
|
+
some key in language_keys
|
|
52
|
+
block := object.get(input, key, {})
|
|
53
|
+
is_object(block)
|
|
54
|
+
object.get(block, "editor.defaultFormatter", null) != "oxc.oxc-vscode"
|
|
55
|
+
msg := sprintf(lang_wrong_formatter_template, [key])
|
|
56
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Тести для `text.vscode_settings`. Запуск:
|
|
2
|
+
# conftest verify -p npm/policy/text/vscode_settings
|
|
3
|
+
package text.vscode_settings_test
|
|
4
|
+
|
|
5
|
+
import rego.v1
|
|
6
|
+
|
|
7
|
+
import data.text.vscode_settings
|
|
8
|
+
|
|
9
|
+
valid_cfg := {
|
|
10
|
+
"editor.formatOnSave": true,
|
|
11
|
+
"[javascript]": {"editor.defaultFormatter": "oxc.oxc-vscode"},
|
|
12
|
+
"[typescript]": {"editor.defaultFormatter": "oxc.oxc-vscode"},
|
|
13
|
+
"[json]": {"editor.defaultFormatter": "oxc.oxc-vscode"},
|
|
14
|
+
"[vue]": {"editor.defaultFormatter": "oxc.oxc-vscode"},
|
|
15
|
+
"[css]": {"editor.defaultFormatter": "oxc.oxc-vscode"},
|
|
16
|
+
"[html]": {"editor.defaultFormatter": "oxc.oxc-vscode"},
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# ── happy path ────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
test_allow_canonical if {
|
|
22
|
+
count(vscode_settings.deny) == 0 with input as valid_cfg
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
test_allow_with_additional_lang_block if {
|
|
26
|
+
cfg := json.patch(valid_cfg, [{
|
|
27
|
+
"op": "add",
|
|
28
|
+
"path": "/[python]",
|
|
29
|
+
"value": {"editor.defaultFormatter": "ms-python.python"},
|
|
30
|
+
}])
|
|
31
|
+
count(vscode_settings.deny) == 0 with input as cfg
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# ── deny: editor.formatOnSave ────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
test_deny_format_on_save_false if {
|
|
37
|
+
cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/editor.formatOnSave", "value": false}])
|
|
38
|
+
count(vscode_settings.deny) > 0 with input as cfg
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
test_deny_format_on_save_missing if {
|
|
42
|
+
cfg := json.patch(valid_cfg, [{"op": "remove", "path": "/editor.formatOnSave"}])
|
|
43
|
+
count(vscode_settings.deny) > 0 with input as cfg
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# ── deny: lang formatters (per-key) ──────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
test_deny_javascript_missing if {
|
|
49
|
+
cfg := json.patch(valid_cfg, [{"op": "remove", "path": "/[javascript]"}])
|
|
50
|
+
count(vscode_settings.deny) > 0 with input as cfg
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
test_deny_typescript_wrong_formatter if {
|
|
54
|
+
cfg := json.patch(
|
|
55
|
+
valid_cfg,
|
|
56
|
+
[{"op": "replace", "path": "/[typescript]/editor.defaultFormatter", "value": "prettier"}],
|
|
57
|
+
)
|
|
58
|
+
count(vscode_settings.deny) > 0 with input as cfg
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
test_deny_json_block_not_object if {
|
|
62
|
+
cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/[json]", "value": "oxc.oxc-vscode"}])
|
|
63
|
+
count(vscode_settings.deny) > 0 with input as cfg
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
test_deny_vue_missing_default_formatter if {
|
|
67
|
+
cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/[vue]", "value": {"editor.tabSize": 2}}])
|
|
68
|
+
count(vscode_settings.deny) > 0 with input as cfg
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
test_deny_css_missing if {
|
|
72
|
+
cfg := json.patch(valid_cfg, [{"op": "remove", "path": "/[css]"}])
|
|
73
|
+
count(vscode_settings.deny) > 0 with input as cfg
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
test_deny_html_missing if {
|
|
77
|
+
cfg := json.patch(valid_cfg, [{"op": "remove", "path": "/[html]"}])
|
|
78
|
+
count(vscode_settings.deny) > 0 with input as cfg
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# ── deny: empty object ───────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
test_deny_empty_object if {
|
|
84
|
+
count(vscode_settings.deny) > 0 with input as {}
|
|
85
|
+
}
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
sourceFileHasGqlTaggedTemplate
|
|
18
18
|
} from './utils/graphql-gql-scan.mjs'
|
|
19
19
|
import { loadCursorIgnorePaths } from './utils/load-cursor-config.mjs'
|
|
20
|
+
import { runConftestBatch } from './utils/run-conftest-batch.mjs'
|
|
20
21
|
import { walkDir } from './utils/walkDir.mjs'
|
|
21
22
|
|
|
22
23
|
/** Очікуваний файл GraphQL Config у корені (graphql.mdc). */
|
|
@@ -68,38 +69,29 @@ async function collectGqlHits(root, candidates) {
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
/**
|
|
71
|
-
*
|
|
72
|
+
* Делегує валідацію `.vscode/extensions.json` rego-пакету `graphql.vscode_extensions`
|
|
73
|
+
* через `runConftestBatch`. Викликається лише після того, як JS виявив `gql` у дереві
|
|
74
|
+
* (умовне правило — без gql цей крок не запускається).
|
|
72
75
|
* @param {(msg: string) => void} pass success-репортер
|
|
73
76
|
* @param {(msg: string) => void} fail fail-репортер
|
|
74
|
-
* @returns {
|
|
77
|
+
* @returns {void}
|
|
75
78
|
*/
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
)
|
|
79
|
+
function checkExtensionsRecommendation(pass, fail) {
|
|
80
|
+
const path = '.vscode/extensions.json'
|
|
81
|
+
if (!existsSync(path)) {
|
|
82
|
+
fail(`${path} не існує — створи файл і додай у recommendations ${REQUIRED_GRAPHQL_VSCODE_EXTENSION} (graphql.mdc)`)
|
|
81
83
|
return
|
|
82
84
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
85
|
+
const violations = runConftestBatch({
|
|
86
|
+
policyDirRel: 'graphql/vscode_extensions',
|
|
87
|
+
namespace: 'graphql.vscode_extensions',
|
|
88
|
+
files: [path]
|
|
89
|
+
})
|
|
90
|
+
if (violations.length === 0) {
|
|
91
|
+
pass(`${path} відповідає graphql.vscode_extensions (rego)`)
|
|
89
92
|
return
|
|
90
93
|
}
|
|
91
|
-
|
|
92
|
-
const rec = ext.recommendations
|
|
93
|
-
if (!Array.isArray(rec)) {
|
|
94
|
-
fail('.vscode/extensions.json: поле recommendations має бути масивом')
|
|
95
|
-
return
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (rec.includes(REQUIRED_GRAPHQL_VSCODE_EXTENSION)) {
|
|
99
|
-
pass(`.vscode/extensions.json: є ${REQUIRED_GRAPHQL_VSCODE_EXTENSION}`)
|
|
100
|
-
} else {
|
|
101
|
-
fail(`.vscode/extensions.json: додай у recommendations "${REQUIRED_GRAPHQL_VSCODE_EXTENSION}" (graphql.mdc)`)
|
|
102
|
-
}
|
|
94
|
+
for (const v of violations) fail(v.message)
|
|
103
95
|
}
|
|
104
96
|
|
|
105
97
|
/**
|
|
@@ -133,7 +125,7 @@ export async function check() {
|
|
|
133
125
|
)
|
|
134
126
|
}
|
|
135
127
|
|
|
136
|
-
|
|
128
|
+
checkExtensionsRecommendation(pass, fail)
|
|
137
129
|
|
|
138
130
|
return reporter.getExitCode()
|
|
139
131
|
}
|
package/scripts/check-js-run.mjs
CHANGED
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
} from './utils/bunyan-imports.mjs'
|
|
41
41
|
import { findUncheckedProcessEnvInText, isCheckEnvScanSourceFile } from './utils/check-env-scan.mjs'
|
|
42
42
|
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
43
|
+
import { runConftestBatch } from './utils/run-conftest-batch.mjs'
|
|
43
44
|
import { findConnFileRuleViolations, isConnFileRulesSourceFile } from './utils/conn-file-rules.mjs'
|
|
44
45
|
import {
|
|
45
46
|
findConnFactoryImportsInText,
|
|
@@ -67,10 +68,11 @@ function backendPackageHasSrcDir(absPackageRoot) {
|
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
/**
|
|
70
|
-
* FS-existence
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
71
|
+
* FS-existence + структурна валідація `jsconfig.json` у backend-пакеті з
|
|
72
|
+
* каталогом `src/`. Структуру (canonical `compilerOptions` і `include`)
|
|
73
|
+
* делегуємо у rego-пакет `js_run.jsconfig` через `runConftestBatch` — Plan B:
|
|
74
|
+
* Rego-authoritative, JS оркеструє per-package gate (frontend з `vite` сюди
|
|
75
|
+
* взагалі не доходить, бо викликається лише з backend-гілки).
|
|
74
76
|
* @param {string} rootDir відносний шлях workspace
|
|
75
77
|
* @param {string} absPackageRoot абсолютний корінь пакета
|
|
76
78
|
* @param {string} label префікс `[pkg] `
|
|
@@ -82,14 +84,23 @@ function checkBackendJsconfigWhenSrcPresent(rootDir, absPackageRoot, label, fail
|
|
|
82
84
|
if (!backendPackageHasSrcDir(absPackageRoot)) return
|
|
83
85
|
|
|
84
86
|
const jcPath = join(rootDir, 'jsconfig.json')
|
|
85
|
-
if (existsSync(jcPath)) {
|
|
86
|
-
passFn(`${label}jsconfig.json є (структуру перевіряє bun run lint-conftest → js_run.jsconfig)`)
|
|
87
|
-
} else {
|
|
87
|
+
if (!existsSync(jcPath)) {
|
|
88
88
|
fail(
|
|
89
89
|
`${label}є каталог src/, але немає jsconfig.json — додай канонічний файл з js-run.mdc ` +
|
|
90
90
|
`(NodeNext, include: src/**/*).`
|
|
91
91
|
)
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
const violations = runConftestBatch({
|
|
95
|
+
policyDirRel: 'js_run/jsconfig',
|
|
96
|
+
namespace: 'js_run.jsconfig',
|
|
97
|
+
files: [jcPath]
|
|
98
|
+
})
|
|
99
|
+
if (violations.length === 0) {
|
|
100
|
+
passFn(`${label}jsconfig.json відповідає js_run.jsconfig (rego)`)
|
|
101
|
+
return
|
|
92
102
|
}
|
|
103
|
+
for (const v of violations) fail(`${label}${v.message}`)
|
|
93
104
|
}
|
|
94
105
|
|
|
95
106
|
/**
|
|
@@ -20,6 +20,7 @@ import { basename, dirname, join, relative } from 'node:path'
|
|
|
20
20
|
import { findDockerfilePaths } from './check-docker.mjs'
|
|
21
21
|
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
22
22
|
import { loadCursorIgnorePaths } from './utils/load-cursor-config.mjs'
|
|
23
|
+
import { runConftestBatch } from './utils/run-conftest-batch.mjs'
|
|
23
24
|
import { walkDir } from './utils/walkDir.mjs'
|
|
24
25
|
|
|
25
26
|
const LINE_SPLIT_RE = /\r?\n/u
|
|
@@ -350,36 +351,45 @@ async function checkDockerfiles(root, ignorePaths, passFn, failFn) {
|
|
|
350
351
|
}
|
|
351
352
|
|
|
352
353
|
/**
|
|
353
|
-
*
|
|
354
|
+
* Делегує валідацію `.vscode/extensions.json` і `.vscode/settings.json` rego-пакетам
|
|
355
|
+
* `nginx_default_tpl.vscode_extensions` і `nginx_default_tpl.vscode_settings`
|
|
356
|
+
* через `runConftestBatch`. Викликається лише після того, як JS виявив
|
|
357
|
+
* `default.conf.template` (умовне правило — без шаблона цей крок не запускається).
|
|
354
358
|
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
355
359
|
* @param {(msg: string) => void} failFn callback при помилці
|
|
360
|
+
* @returns {void}
|
|
356
361
|
*/
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
+
function checkVscodeNginx(passFn, failFn) {
|
|
363
|
+
const extPath = '.vscode/extensions.json'
|
|
364
|
+
if (existsSync(extPath)) {
|
|
365
|
+
const violations = runConftestBatch({
|
|
366
|
+
policyDirRel: 'nginx_default_tpl/vscode_extensions',
|
|
367
|
+
namespace: 'nginx_default_tpl.vscode_extensions',
|
|
368
|
+
files: [extPath]
|
|
369
|
+
})
|
|
370
|
+
if (violations.length === 0) {
|
|
371
|
+
passFn(`${extPath} відповідає nginx_default_tpl.vscode_extensions (rego)`)
|
|
362
372
|
} else {
|
|
363
|
-
|
|
373
|
+
for (const v of violations) failFn(v.message)
|
|
364
374
|
}
|
|
365
375
|
} else {
|
|
366
376
|
failFn('Очікується .vscode/extensions.json з ahmadalli.vscode-nginx-conf (див. nginx-default-tpl.mdc)')
|
|
367
377
|
}
|
|
368
378
|
|
|
369
|
-
|
|
379
|
+
const setPath = '.vscode/settings.json'
|
|
380
|
+
if (!existsSync(setPath)) {
|
|
370
381
|
failFn('Очікується .vscode/settings.json з форматером nginx і formatOnSave (див. nginx-default-tpl.mdc)')
|
|
371
382
|
return
|
|
372
383
|
}
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
passFn('settings.json: [nginx] defaultFormatter налаштовано')
|
|
384
|
+
const violations = runConftestBatch({
|
|
385
|
+
policyDirRel: 'nginx_default_tpl/vscode_settings',
|
|
386
|
+
namespace: 'nginx_default_tpl.vscode_settings',
|
|
387
|
+
files: [setPath]
|
|
388
|
+
})
|
|
389
|
+
if (violations.length === 0) {
|
|
390
|
+
passFn(`${setPath} відповідає nginx_default_tpl.vscode_settings (rego)`)
|
|
381
391
|
} else {
|
|
382
|
-
|
|
392
|
+
for (const v of violations) failFn(v.message)
|
|
383
393
|
}
|
|
384
394
|
}
|
|
385
395
|
|
|
@@ -416,7 +426,7 @@ export async function check() {
|
|
|
416
426
|
}
|
|
417
427
|
|
|
418
428
|
await checkDockerfiles(root, ignorePaths, pass, fail)
|
|
419
|
-
|
|
429
|
+
checkVscodeNginx(pass, fail)
|
|
420
430
|
|
|
421
431
|
return reporter.getExitCode()
|
|
422
432
|
}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Перевіряє CSS/SCSS лінт за правилом style-lint.mdc.
|
|
3
3
|
*
|
|
4
|
-
* **Що тут лишилося** (FS /
|
|
4
|
+
* **Що тут лишилося** (FS / cross-file — не покривається conftest):
|
|
5
5
|
* - наявність зовнішнього файлу конфігу stylelint (`.stylelintrc.*`,
|
|
6
6
|
* `stylelint.config.js`) як альтернатива полю `stylelint` у `package.json`
|
|
7
7
|
* (cross-file: треба знати, чи є поле, чи немає);
|
|
8
|
-
* - `.stylelintignore` у
|
|
9
|
-
* - `.vscode/extensions.json` recommendation `stylelint.vscode-stylelint`;
|
|
10
|
-
* - `.vscode/settings.json` `css.validate` / `scss.validate` / `less.validate: false`.
|
|
8
|
+
* - `.stylelintignore` у корені.
|
|
11
9
|
*
|
|
12
10
|
* **Що покрила Rego** (`bun run lint-conftest`):
|
|
13
11
|
* - `npm/policy/style_lint/package_json/` — скрипт `lint-style` через `npx stylelint`,
|
|
14
12
|
* `@nitra/stylelint-config` у `devDependencies`, поле `stylelint.extends`;
|
|
15
|
-
* - `npm/policy/style_lint/lint_style_yml/` — `npx stylelint` у `run` workflow
|
|
13
|
+
* - `npm/policy/style_lint/lint_style_yml/` — `npx stylelint` у `run` workflow;
|
|
14
|
+
* - `npm/policy/style_lint/vscode_extensions/` — `stylelint.vscode-stylelint`
|
|
15
|
+
* у `recommendations` `.vscode/extensions.json`;
|
|
16
|
+
* - `npm/policy/style_lint/vscode_settings/` — `css.validate`/`scss.validate`/
|
|
17
|
+
* `less.validate: false` у `.vscode/settings.json`.
|
|
16
18
|
*/
|
|
17
19
|
import { existsSync } from 'node:fs'
|
|
18
20
|
import { readFile } from 'node:fs/promises'
|
|
@@ -39,32 +41,10 @@ async function checkStylelintConfigPresence(reporter) {
|
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const { pass, fail } = reporter
|
|
47
|
-
if (existsSync('.vscode/extensions.json')) {
|
|
48
|
-
const ext = JSON.parse(await readFile('.vscode/extensions.json', 'utf8'))
|
|
49
|
-
if (ext.recommendations?.includes('stylelint.vscode-stylelint')) {
|
|
50
|
-
pass('extensions.json містить stylelint.vscode-stylelint')
|
|
51
|
-
} else {
|
|
52
|
-
fail('extensions.json не містить stylelint.vscode-stylelint')
|
|
53
|
-
}
|
|
54
|
-
} else {
|
|
55
|
-
fail('.vscode/extensions.json не існує')
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (!existsSync('.vscode/settings.json')) return
|
|
59
|
-
const s = JSON.parse(await readFile('.vscode/settings.json', 'utf8'))
|
|
60
|
-
for (const key of ['css.validate', 'scss.validate', 'less.validate']) {
|
|
61
|
-
if (s[key] === false) {
|
|
62
|
-
pass(`${key} вимкнено`)
|
|
63
|
-
} else {
|
|
64
|
-
fail(`settings.json: ${key} має бути false`)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
44
|
+
// `.vscode/extensions.json` (`stylelint.vscode-stylelint`) і `.vscode/settings.json`
|
|
45
|
+
// (`css.validate`/`scss.validate`/`less.validate: false`) — у rego-пакетах
|
|
46
|
+
// `style_lint.vscode_extensions` і `style_lint.vscode_settings`, прогоняє
|
|
47
|
+
// `bun run lint-conftest`. JS-копії видалено, щоб не було двох джерел істини.
|
|
68
48
|
|
|
69
49
|
/**
|
|
70
50
|
* Перевіряє відповідність проєкту правилам style-lint.mdc
|
|
@@ -89,7 +69,5 @@ export async function check() {
|
|
|
89
69
|
fail(`${wfPath} не існує — створи його`)
|
|
90
70
|
}
|
|
91
71
|
|
|
92
|
-
await checkVscodeStylelint(reporter)
|
|
93
|
-
|
|
94
72
|
return reporter.getExitCode()
|
|
95
73
|
}
|
package/scripts/check-text.mjs
CHANGED
|
@@ -89,70 +89,11 @@ async function checkV8rIgnore(passFn, failFn) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
async function checkVscodeTextExtensions(passFn, failFn) {
|
|
98
|
-
if (!existsSync('.vscode/extensions.json')) {
|
|
99
|
-
failFn('.vscode/extensions.json не існує — створи з recommendations згідно n-text.mdc')
|
|
100
|
-
return
|
|
101
|
-
}
|
|
102
|
-
try {
|
|
103
|
-
const ext = JSON.parse(await readFile('.vscode/extensions.json', 'utf8'))
|
|
104
|
-
const rec = ext.recommendations
|
|
105
|
-
for (const id of ['DavidAnson.vscode-markdownlint', 'oxc.oxc-vscode', 'timonwong.shellcheck']) {
|
|
106
|
-
if (Array.isArray(rec) && rec.includes(id)) {
|
|
107
|
-
passFn(`extensions.json містить ${id}`)
|
|
108
|
-
} else {
|
|
109
|
-
failFn(`extensions.json: додай "${id}" у recommendations (див. n-text.mdc)`)
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
} catch {
|
|
113
|
-
failFn('.vscode/extensions.json — невалідний JSON')
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Перевіряє VSCode settings.json для текстового стека.
|
|
119
|
-
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
120
|
-
* @param {(msg: string) => void} failFn callback при помилці
|
|
121
|
-
*/
|
|
122
|
-
async function checkVscodeTextSettings(passFn, failFn) {
|
|
123
|
-
if (!existsSync('.vscode/settings.json')) {
|
|
124
|
-
failFn('.vscode/settings.json не існує — створи згідно n-text.mdc')
|
|
125
|
-
return
|
|
126
|
-
}
|
|
127
|
-
try {
|
|
128
|
-
const settings = JSON.parse(await readFile('.vscode/settings.json', 'utf8'))
|
|
129
|
-
if (settings['editor.formatOnSave'] === true) {
|
|
130
|
-
passFn('settings.json: editor.formatOnSave увімкнено')
|
|
131
|
-
} else {
|
|
132
|
-
failFn('settings.json: editor.formatOnSave має бути true')
|
|
133
|
-
}
|
|
134
|
-
for (const t of ['javascript', 'typescript', 'json', 'vue', 'css', 'html']) {
|
|
135
|
-
const key = `[${t}]`
|
|
136
|
-
if (settings[key]?.['editor.defaultFormatter'] === 'oxc.oxc-vscode') {
|
|
137
|
-
passFn(`settings.json: ${key} використовує oxc.oxc-vscode`)
|
|
138
|
-
} else {
|
|
139
|
-
failFn(`settings.json: ${key} має використовувати oxc.oxc-vscode як defaultFormatter`)
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
} catch {
|
|
143
|
-
failFn('.vscode/settings.json — невалідний JSON')
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Перевіряє VSCode extensions.json та settings.json для текстового стека.
|
|
149
|
-
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
150
|
-
* @param {(msg: string) => void} failFn callback при помилці
|
|
151
|
-
*/
|
|
152
|
-
async function checkVscodeText(passFn, failFn) {
|
|
153
|
-
await checkVscodeTextExtensions(passFn, failFn)
|
|
154
|
-
await checkVscodeTextSettings(passFn, failFn)
|
|
155
|
-
}
|
|
92
|
+
// `.vscode/extensions.json` (`DavidAnson.vscode-markdownlint`, `oxc.oxc-vscode`,
|
|
93
|
+
// `timonwong.shellcheck`) і `.vscode/settings.json` (`editor.formatOnSave` +
|
|
94
|
+
// `[lang].editor.defaultFormatter`) валідують rego-пакети `text.vscode_extensions`
|
|
95
|
+
// і `text.vscode_settings` (зареєстровані глобально у `lint-conftest.mjs` TARGETS
|
|
96
|
+
// з `rule: 'text'`). FS-existence файлів — у `checkTextConfigsExistence`.
|
|
156
97
|
|
|
157
98
|
/**
|
|
158
99
|
* FS-existence стек текстових конфігів. Контент-валідація — у Rego
|
|
@@ -165,7 +106,9 @@ function checkTextConfigsExistence(passFn, failFn) {
|
|
|
165
106
|
for (const [path, mdcRef] of [
|
|
166
107
|
['.oxfmtrc.json', 'text.oxfmtrc'],
|
|
167
108
|
['.cspell.json', 'text.cspell'],
|
|
168
|
-
['.markdownlint-cli2.jsonc', 'text.markdownlint']
|
|
109
|
+
['.markdownlint-cli2.jsonc', 'text.markdownlint'],
|
|
110
|
+
['.vscode/extensions.json', 'text.vscode_extensions'],
|
|
111
|
+
['.vscode/settings.json', 'text.vscode_settings']
|
|
169
112
|
]) {
|
|
170
113
|
if (existsSync(path)) {
|
|
171
114
|
passFn(`${path} є (структуру перевіряє bun run lint-conftest → ${mdcRef})`)
|
|
@@ -247,7 +190,6 @@ export async function check() {
|
|
|
247
190
|
const { pass, fail } = reporter
|
|
248
191
|
|
|
249
192
|
await checkV8rIgnore(pass, fail)
|
|
250
|
-
await checkVscodeText(pass, fail)
|
|
251
193
|
await checkTextConfigsExistence(pass, fail)
|
|
252
194
|
|
|
253
195
|
for (const f of ['.prettierrc', '.prettierrc.json', '.prettierrc.js', 'prettier.config.js', '.prettierrc.yml']) {
|
|
@@ -103,6 +103,8 @@ const TARGETS = [
|
|
|
103
103
|
{ namespace: 'text.cspell', policyDir: 'text', rule: 'text', single: '.cspell.json' },
|
|
104
104
|
{ namespace: 'text.markdownlint', policyDir: 'text', rule: 'text', single: '.markdownlint-cli2.jsonc' },
|
|
105
105
|
{ namespace: 'text.package_json', policyDir: 'text', rule: 'text', single: 'package.json' },
|
|
106
|
+
{ namespace: 'text.vscode_extensions', policyDir: 'text', rule: 'text', single: '.vscode/extensions.json' },
|
|
107
|
+
{ namespace: 'text.vscode_settings', policyDir: 'text', rule: 'text', single: '.vscode/settings.json' },
|
|
106
108
|
|
|
107
109
|
// ── style-lint ──────────────────────────────────────────────────────────
|
|
108
110
|
{ namespace: 'style_lint.package_json', policyDir: 'style_lint', rule: 'style-lint', single: 'package.json' },
|
|
@@ -112,6 +114,18 @@ const TARGETS = [
|
|
|
112
114
|
rule: 'style-lint',
|
|
113
115
|
single: '.github/workflows/lint-style.yml'
|
|
114
116
|
},
|
|
117
|
+
{
|
|
118
|
+
namespace: 'style_lint.vscode_extensions',
|
|
119
|
+
policyDir: 'style_lint',
|
|
120
|
+
rule: 'style-lint',
|
|
121
|
+
single: '.vscode/extensions.json'
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
namespace: 'style_lint.vscode_settings',
|
|
125
|
+
policyDir: 'style_lint',
|
|
126
|
+
rule: 'style-lint',
|
|
127
|
+
single: '.vscode/settings.json'
|
|
128
|
+
},
|
|
115
129
|
|
|
116
130
|
// ── php ─────────────────────────────────────────────────────────────────
|
|
117
131
|
{ namespace: 'php.package_json', policyDir: 'php', rule: 'php', single: 'package.json' },
|
|
@@ -157,13 +171,19 @@ const TARGETS = [
|
|
|
157
171
|
single: '.github/workflows/lint-js.yml'
|
|
158
172
|
},
|
|
159
173
|
|
|
160
|
-
// ── image-compress / capacitor
|
|
174
|
+
// ── image-compress / image-avif / capacitor ─────────────────────────────
|
|
161
175
|
{
|
|
162
176
|
namespace: 'image_compress.package_json',
|
|
163
177
|
policyDir: 'image_compress',
|
|
164
178
|
rule: 'image-compress',
|
|
165
179
|
single: 'package.json'
|
|
166
180
|
},
|
|
181
|
+
{
|
|
182
|
+
namespace: 'image_avif.package_json',
|
|
183
|
+
policyDir: 'image_avif',
|
|
184
|
+
rule: 'image-avif',
|
|
185
|
+
walk: { match: rel => rel.endsWith('/package.json') || rel === 'package.json' }
|
|
186
|
+
},
|
|
167
187
|
{
|
|
168
188
|
namespace: 'capacitor.package_json',
|
|
169
189
|
policyDir: 'capacitor',
|
|
@@ -214,6 +234,12 @@ const TARGETS = [
|
|
|
214
234
|
rule: 'js-run',
|
|
215
235
|
walk: { match: rel => rel.endsWith('/package.json') || rel === 'package.json' }
|
|
216
236
|
},
|
|
237
|
+
// `js_run.jsconfig` НЕ реєструємо тут — `jsconfig.json` має канонічну структуру
|
|
238
|
+
// лише для backend-пакетів (без `vite` у `devDependencies`) з каталогом `src/`,
|
|
239
|
+
// а lint-conftest фільтрує лише по `activeRules` на рівні репозиторію — не
|
|
240
|
+
// вміє пропустити окремий workspace-пакет за наявністю `vite`. Тому валідація
|
|
241
|
+
// структури делегується з `check-js-run.mjs` через `runConftestBatch` після
|
|
242
|
+
// того, як JS визначить, що пакет — backend з `src/`.
|
|
217
243
|
{
|
|
218
244
|
namespace: 'vue.package_json',
|
|
219
245
|
policyDir: 'vue',
|