@7n/rules-lang-js 0.3.1 → 0.4.0
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 +6 -0
- package/package.json +14 -4
- package/rules/bun/bunfig/bunfig.mdc +17 -0
- package/rules/bun/bunfig/bunfig.rego +29 -0
- package/rules/bun/bunfig/concern.json +9 -0
- package/rules/bun/bunfig/template/bunfig.toml.snippet.toml +2 -0
- package/rules/bun/docs/index.md +11 -0
- package/rules/bun/layout/concern.json +16 -0
- package/rules/bun/layout/docs/fix-layout.md +29 -0
- package/rules/bun/layout/docs/main.md +34 -0
- package/rules/bun/layout/fix-layout.mjs +63 -0
- package/rules/bun/layout/layout.mdc +60 -0
- package/rules/bun/layout/main.mjs +53 -0
- package/rules/bun/licensee/concern.json +7 -0
- package/rules/bun/licensee/docs/fix-licensee.md +27 -0
- package/rules/bun/licensee/docs/index.md +12 -0
- package/rules/bun/licensee/docs/main.md +30 -0
- package/rules/bun/licensee/fix-licensee.mjs +31 -0
- package/rules/bun/licensee/main.mjs +68 -0
- package/rules/bun/lint-surface/concern.json +3 -0
- package/rules/bun/lint-surface/lint-surface.mdc +13 -0
- package/rules/bun/main.json +1 -0
- package/rules/bun/main.mdc +11 -0
- package/rules/bun/package_json/concern.json +9 -0
- package/rules/bun/package_json/docs/fix-package_json.md +28 -0
- package/rules/bun/package_json/docs/index.md +9 -0
- package/rules/bun/package_json/fix-package_json.mjs +311 -0
- package/rules/bun/package_json/package_json.mdc +14 -0
- package/rules/bun/package_json/package_json.rego +64 -0
- package/rules/bun/package_json/template/package.json.deny.json +4 -0
- package/rules/js/check/check.mdc +26 -0
- package/rules/js/check/concern.json +19 -0
- package/rules/js/check/docs/eslint-config.md +56 -0
- package/rules/js/check/docs/fix-check.md +48 -0
- package/rules/js/check/docs/index.md +13 -0
- package/rules/js/check/docs/main.md +48 -0
- package/rules/js/check/eslint-config.mjs +262 -0
- package/rules/js/check/fix-check.mjs +82 -0
- package/rules/js/check/main.mjs +310 -0
- package/rules/js/dep-policy/concern.json +4 -0
- package/rules/js/dep-policy/dep-policy.mdc +36 -0
- package/rules/js/dep-policy/docs/main.md +35 -0
- package/rules/js/dep-policy/main.mjs +99 -0
- package/rules/js/docs/index.md +11 -0
- package/rules/js/eslint/concern.json +8 -0
- package/rules/js/eslint/docs/fix-eslint.md +50 -0
- package/rules/js/eslint/docs/fix-worker.md +31 -0
- package/rules/js/eslint/docs/index.md +11 -0
- package/rules/js/eslint/docs/main.md +35 -0
- package/rules/js/eslint/fix-eslint.mjs +160 -0
- package/rules/js/eslint/fix-worker.mjs +139 -0
- package/rules/js/eslint/main.mjs +115 -0
- package/rules/js/file-extensions/concern.json +3 -0
- package/rules/js/file-extensions/file-extensions.mdc +12 -0
- package/rules/js/jscpd_config/concern.json +11 -0
- package/rules/js/jscpd_config/docs/fix-jscpd_config.md +25 -0
- package/rules/js/jscpd_config/docs/index.md +9 -0
- package/rules/js/jscpd_config/fix-jscpd_config.mjs +3 -0
- package/rules/js/jscpd_config/jscpd_config.mdc +42 -0
- package/rules/js/jscpd_config/jscpd_config.rego +44 -0
- package/rules/js/jscpd_config/template/.jscpd.json.snippet.json +7 -0
- package/rules/js/jscpd_duplicates/concern.json +7 -0
- package/rules/js/jscpd_duplicates/docs/main.md +29 -0
- package/rules/js/jscpd_duplicates/main.mjs +69 -0
- package/rules/js/knip/concern.json +7 -0
- package/rules/js/knip/docs/main.md +32 -0
- package/rules/js/knip/knip.mdc +15 -0
- package/rules/js/knip/main.mjs +68 -0
- package/rules/js/lint-findings/concern.json +3 -0
- package/rules/js/lint-findings/docs/main.md +40 -0
- package/rules/js/lint-findings/main.mjs +125 -0
- package/rules/js/main.json +1 -0
- package/rules/js/main.mdc +18 -0
- package/rules/js/package_json/concern.json +9 -0
- package/rules/js/package_json/docs/fix-package_json.md +25 -0
- package/rules/js/package_json/docs/index.md +9 -0
- package/rules/js/package_json/fix-package_json.mjs +3 -0
- package/rules/js/package_json/package_json.mdc +15 -0
- package/rules/js/package_json/package_json.rego +142 -0
- package/rules/js/package_json/template/package.json.snippet.json +6 -0
- package/rules/js/tooling/concern.json +3 -0
- package/rules/js/tooling/data/tooling/knip-canonical.json +30 -0
- package/rules/js/tooling/data/tooling/oxlint-canonical.json +400 -0
- package/rules/js/tooling/docs/main.md +53 -0
- package/rules/js/tooling/main.mjs +183 -0
- package/rules/js/utils_imports/concern.json +4 -0
- package/rules/js/utils_imports/docs/main.md +50 -0
- package/rules/js/utils_imports/main.mjs +185 -0
- package/rules/js/utils_imports/utils_imports.mdc +15 -0
- package/rules/js/vscode_extensions/concern.json +11 -0
- package/rules/js/vscode_extensions/docs/fix-vscode_extensions.md +24 -0
- package/rules/js/vscode_extensions/docs/index.md +11 -0
- package/rules/js/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/js/vscode_extensions/template/extensions.json.snippet.json +6 -0
- package/rules/js/vscode_extensions/vscode_extensions.mdc +11 -0
- package/rules/js/vscode_extensions/vscode_extensions.rego +12 -0
- package/rules/js-bun-db/connection/concern.json +3 -0
- package/rules/js-bun-db/connection/connection.mdc +42 -0
- package/rules/js-bun-db/docs/index.md +11 -0
- package/rules/js-bun-db/lib/bun-sql-scan.mjs +1047 -0
- package/rules/js-bun-db/lib/docs/bun-sql-scan.md +63 -0
- package/rules/js-bun-db/lib/docs/index.md +11 -0
- package/rules/js-bun-db/main.json +1 -0
- package/rules/js-bun-db/main.mdc +8 -0
- package/rules/js-bun-db/package_json/concern.json +9 -0
- package/rules/js-bun-db/package_json/package_json.mdc +31 -0
- package/rules/js-bun-db/package_json/package_json.rego +15 -0
- package/rules/js-bun-db/package_json/template/package.json.deny.json +6 -0
- package/rules/js-bun-db/pg_format_identifiers/concern.json +3 -0
- package/rules/js-bun-db/pg_format_identifiers/pg_format_identifiers.mdc +104 -0
- package/rules/js-bun-db/safety/concern.json +4 -0
- package/rules/js-bun-db/safety/docs/main.md +34 -0
- package/rules/js-bun-db/safety/main.mjs +430 -0
- package/rules/js-bun-db/safety/safety.mdc +458 -0
- package/rules/js-bun-redis/docs/index.md +11 -0
- package/rules/js-bun-redis/imports/concern.json +4 -0
- package/rules/js-bun-redis/imports/docs/main.md +36 -0
- package/rules/js-bun-redis/imports/imports.mdc +47 -0
- package/rules/js-bun-redis/imports/main.mjs +88 -0
- package/rules/js-bun-redis/lib/docs/index.md +11 -0
- package/rules/js-bun-redis/lib/docs/redis-imports.md +227 -0
- package/rules/js-bun-redis/lib/redis-imports.mjs +130 -0
- package/rules/js-bun-redis/main.json +1 -0
- package/rules/js-bun-redis/main.mdc +8 -0
- package/rules/js-bun-redis/package_json/concern.json +9 -0
- package/rules/js-bun-redis/package_json/package_json.mdc +11 -0
- package/rules/js-bun-redis/package_json/package_json.rego +15 -0
- package/rules/js-bun-redis/package_json/template/package.json.deny.json +12 -0
- package/rules/js-mssql/deps/concern.json +4 -0
- package/rules/js-mssql/deps/docs/main.md +33 -0
- package/rules/js-mssql/deps/main.mjs +297 -0
- package/rules/js-mssql/docs/index.md +11 -0
- package/rules/js-mssql/lib/docs/index.md +11 -0
- package/rules/js-mssql/lib/docs/mssql-pool-scan.md +380 -0
- package/rules/js-mssql/lib/mssql-pool-scan.mjs +610 -0
- package/rules/js-mssql/main.json +1 -0
- package/rules/js-mssql/main.mdc +144 -0
- package/rules/js-mssql/mssql-tvp/concern.json +3 -0
- package/rules/js-mssql/mssql-tvp/mssql-tvp.mdc +77 -0
- package/rules/js-mssql/package_json/concern.json +9 -0
- package/rules/js-mssql/package_json/package_json.mdc +9 -0
- package/rules/js-mssql/package_json/package_json.rego +57 -0
- package/rules/js-run/configmap/concern.json +9 -0
- package/rules/js-run/configmap/configmap.mdc +37 -0
- package/rules/js-run/configmap/configmap.rego +21 -0
- package/rules/js-run/configmap/template/configmap.yaml.contains.yml +4 -0
- package/rules/js-run/docs/index.md +11 -0
- package/rules/js-run/jsconfig/concern.json +9 -0
- package/rules/js-run/jsconfig/docs/fix-jsconfig.md +28 -0
- package/rules/js-run/jsconfig/docs/index.md +9 -0
- package/rules/js-run/jsconfig/fix-jsconfig.mjs +119 -0
- package/rules/js-run/jsconfig/jsconfig.mdc +48 -0
- package/rules/js-run/jsconfig/jsconfig.rego +59 -0
- package/rules/js-run/jsconfig/template/jsconfig.json.snippet.json +10 -0
- package/rules/js-run/lib/bunyan-imports.mjs +98 -0
- package/rules/js-run/lib/check-env-scan.mjs +338 -0
- package/rules/js-run/lib/conn-file-rules.mjs +214 -0
- package/rules/js-run/lib/conn-imports-scan.mjs +154 -0
- package/rules/js-run/lib/docs/bunyan-imports.md +121 -0
- package/rules/js-run/lib/docs/check-env-scan.md +438 -0
- package/rules/js-run/lib/docs/conn-file-rules.md +304 -0
- package/rules/js-run/lib/docs/conn-imports-scan.md +208 -0
- package/rules/js-run/lib/docs/index.md +16 -0
- package/rules/js-run/lib/docs/promise-settimeout-scan.md +334 -0
- package/rules/js-run/lib/docs/temporal-scan.md +29 -0
- package/rules/js-run/lib/promise-settimeout-scan.mjs +128 -0
- package/rules/js-run/lib/temporal-scan.mjs +52 -0
- package/rules/js-run/main.json +1 -0
- package/rules/js-run/main.mdc +16 -0
- package/rules/js-run/package_json/concern.json +9 -0
- package/rules/js-run/package_json/package_json.mdc +44 -0
- package/rules/js-run/package_json/package_json.rego +37 -0
- package/rules/js-run/package_json/template/package.json.deny.json +22 -0
- package/rules/js-run/project-structure/concern.json +3 -0
- package/rules/js-run/project-structure/project-structure.mdc +11 -0
- package/rules/js-run/runtime/concern.json +12 -0
- package/rules/js-run/runtime/docs/fix-runtime.md +27 -0
- package/rules/js-run/runtime/docs/main.md +35 -0
- package/rules/js-run/runtime/fix-runtime.mjs +46 -0
- package/rules/js-run/runtime/main.mjs +496 -0
- package/rules/js-run/runtime/runtime.mdc +184 -0
- package/rules/js-run/scope/concern.json +3 -0
- package/rules/js-run/scope/scope.mdc +11 -0
- package/rules/npm-module/docs/index.md +11 -0
- package/rules/npm-module/emit_types_config/concern.json +9 -0
- package/rules/npm-module/emit_types_config/docs/fix-emit_types_config.md +25 -0
- package/rules/npm-module/emit_types_config/docs/index.md +9 -0
- package/rules/npm-module/emit_types_config/emit_types_config.mdc +43 -0
- package/rules/npm-module/emit_types_config/emit_types_config.rego +28 -0
- package/rules/npm-module/emit_types_config/fix-emit_types_config.mjs +5 -0
- package/rules/npm-module/emit_types_config/template/tsconfig.emit-types.json.snippet.json +9 -0
- package/rules/npm-module/header_doc_pointer/concern.json +5 -0
- package/rules/npm-module/header_doc_pointer/docs/main.md +40 -0
- package/rules/npm-module/header_doc_pointer/header_doc_pointer.mdc +18 -0
- package/rules/npm-module/header_doc_pointer/main.mjs +131 -0
- package/rules/npm-module/main.json +1 -0
- package/rules/npm-module/main.mdc +36 -0
- package/rules/npm-module/npm_package_json/concern.json +9 -0
- package/rules/npm-module/npm_package_json/docs/fix-npm_package_json.md +26 -0
- package/rules/npm-module/npm_package_json/docs/index.md +9 -0
- package/rules/npm-module/npm_package_json/fix-npm_package_json.mjs +5 -0
- package/rules/npm-module/npm_package_json/npm_package_json.mdc +57 -0
- package/rules/npm-module/npm_package_json/npm_package_json.rego +73 -0
- package/rules/npm-module/npm_package_json/template/package.json.snippet.json +1 -0
- package/rules/npm-module/package_structure/concern.json +5 -0
- package/rules/npm-module/package_structure/docs/main.md +37 -0
- package/rules/npm-module/package_structure/main.mjs +448 -0
- package/rules/npm-module/package_structure/package_structure.mdc +63 -0
- package/rules/npm-module/root_package_json/concern.json +9 -0
- package/rules/npm-module/root_package_json/docs/fix-root_package_json.md +25 -0
- package/rules/npm-module/root_package_json/docs/index.md +9 -0
- package/rules/npm-module/root_package_json/fix-root_package_json.mjs +5 -0
- package/rules/npm-module/root_package_json/root_package_json.mdc +41 -0
- package/rules/npm-module/root_package_json/root_package_json.rego +28 -0
- package/rules/npm-module/root_package_json/template/package.json.snippet.json +1 -0
- package/rules/npm-module/rule_meta/concern.json +8 -0
- package/rules/npm-module/rule_meta/docs/main.md +35 -0
- package/rules/npm-module/rule_meta/main.mjs +119 -0
- package/rules/npm-module/rule_meta/rule_meta.mdc +11 -0
- package/rules/npm-module/skill_meta/concern.json +5 -0
- package/rules/npm-module/skill_meta/docs/main.md +149 -0
- package/rules/npm-module/skill_meta/main.mjs +91 -0
- package/rules/npm-module/skill_meta/skill_meta.mdc +11 -0
- package/rules/tool-surface/docs/index.md +11 -0
- package/rules/tool-surface/main.json +6 -0
- package/rules/tool-surface/main.mdc +72 -0
- package/rules/vue/composition-api/composition-api.mdc +82 -0
- package/rules/vue/composition-api/concern.json +3 -0
- package/rules/vue/docs/index.md +11 -0
- package/rules/vue/lib/docs/index.md +11 -0
- package/rules/vue/lib/docs/vue-forbidden-imports.md +265 -0
- package/rules/vue/lib/vue-forbidden-imports.mjs +240 -0
- package/rules/vue/main.json +1 -0
- package/rules/vue/main.mdc +18 -0
- package/rules/vue/nheader-layout/concern.json +3 -0
- package/rules/vue/nheader-layout/nheader-layout.mdc +171 -0
- package/rules/vue/package_json/concern.json +9 -0
- package/rules/vue/package_json/package_json.mdc +30 -0
- package/rules/vue/package_json/package_json.rego +140 -0
- package/rules/vue/packages/concern.json +6 -0
- package/rules/vue/packages/docs/index.md +11 -0
- package/rules/vue/packages/docs/main.md +35 -0
- package/rules/vue/packages/main.mjs +575 -0
- package/rules/vue/packages/packages.mdc +56 -0
- package/rules/vue/quasar-ui/concern.json +3 -0
- package/rules/vue/quasar-ui/quasar-ui.mdc +32 -0
- package/rules/vue/structure/concern.json +3 -0
- package/rules/vue/structure/structure.mdc +101 -0
- package/rules/vue/testing/concern.json +3 -0
- package/rules/vue/testing/testing.mdc +40 -0
- package/rules/vue/tfm-translations/concern.json +7 -0
- package/rules/vue/tfm-translations/docs/main.md +29 -0
- package/rules/vue/tfm-translations/main.mjs +55 -0
- package/rules/vue/tfm-translations/tfm-translations.mdc +32 -0
- package/rules/vue/vite-config/concern.json +3 -0
- package/rules/vue/vite-config/vite-config.mdc +153 -0
- package/rules/vue/vite-env/concern.json +3 -0
- package/rules/vue/vite-env/vite-env.mdc +61 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Визначає явні імпорти з модуля `vue`, які суперечать vue.mdc (має працювати unplugin-auto-import),
|
|
3
|
+
* а також заборонені імпорти Node-нативних модулів у `.vue` SFC (`node:*` префікс або bare ім’я
|
|
4
|
+
* вбудованого модуля Node — `fs`, `path`, `timers/promises` тощо). Vue SFC виконується у браузері,
|
|
5
|
+
* де Node API недоступне, тож такі імпорти ламають збірку/рантайм.
|
|
6
|
+
*
|
|
7
|
+
* Аналіз import виконується через **oxc-parser** (`parseSync`, поле `module.staticImports`) — ESTree-сумісний
|
|
8
|
+
* розбір без евристик по рядках. Дозволені лише side-effect `import 'vue'`, повністю type-only імпорти
|
|
9
|
+
* та `import { type A, type B } from 'vue'` (перевірка `entries[].isType`).
|
|
10
|
+
*
|
|
11
|
+
* Для `.vue` з шаблону витягуються лише теги `<script>` / `<script setup>` (регулярний вираз); далі той самий Oxc-парсинг
|
|
12
|
+
* вмісту скрипта з віртуальним ім’ям `*.ts` для режиму TypeScript.
|
|
13
|
+
*/
|
|
14
|
+
import { builtinModules } from 'node:module'
|
|
15
|
+
|
|
16
|
+
import { parseSync } from 'oxc-parser'
|
|
17
|
+
|
|
18
|
+
import { contentForVueImportScan, extractVueScriptBlocks } from '@7n/rules/scripts/lib/js-source-signals.mjs'
|
|
19
|
+
|
|
20
|
+
// Витяг script-блоків SFC живе в ядрі (потрібен auto-rules для fact-збору); ре-експорт зберігає API lib-а
|
|
21
|
+
export { contentForVueImportScan, extractVueScriptBlocks } from '@7n/rules/scripts/lib/js-source-signals.mjs'
|
|
22
|
+
|
|
23
|
+
const NODE_BUILTIN_MODULES = new Set(builtinModules)
|
|
24
|
+
|
|
25
|
+
const VUE_EXT_RE = /\.vue$/u
|
|
26
|
+
const SOURCE_FILE_RE = /\.(vue|[cm]?[jt]sx?)$/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Мова для Oxc за шляхом файлу (розширення).
|
|
30
|
+
* @param {string} filePath віртуальний або реальний шлях до файлу
|
|
31
|
+
* @returns {'js' | 'jsx' | 'ts' | 'tsx'} значення опції `lang` для `parseSync`
|
|
32
|
+
*/
|
|
33
|
+
function langFromPath(filePath) {
|
|
34
|
+
const lower = filePath.toLowerCase()
|
|
35
|
+
if (lower.endsWith('.tsx')) {
|
|
36
|
+
return 'tsx'
|
|
37
|
+
}
|
|
38
|
+
if (lower.endsWith('.ts') || lower.endsWith('.mts') || lower.endsWith('.cts')) {
|
|
39
|
+
return 'ts'
|
|
40
|
+
}
|
|
41
|
+
if (lower.endsWith('.jsx')) {
|
|
42
|
+
return 'jsx'
|
|
43
|
+
}
|
|
44
|
+
return 'js'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Номер рядка (1-based) за зміщенням у буфері.
|
|
49
|
+
* @param {string} content повний текст файлу
|
|
50
|
+
* @param {number} offset байтове зміщення початку import
|
|
51
|
+
* @returns {number} номер рядка від 1
|
|
52
|
+
*/
|
|
53
|
+
function offsetToLine(content, offset) {
|
|
54
|
+
let line = 1
|
|
55
|
+
const n = Math.min(offset, content.length)
|
|
56
|
+
for (let i = 0; i < n; i++) {
|
|
57
|
+
if (content.codePointAt(i) === 10) {
|
|
58
|
+
line++
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return line
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Стискає пробіли для повідомлення про порушення.
|
|
66
|
+
* @param {string} s фрагмент коду
|
|
67
|
+
* @returns {string} скорочений однорядковий рядок
|
|
68
|
+
*/
|
|
69
|
+
function normalizeSnippet(s) {
|
|
70
|
+
return s.replaceAll(/\s+/g, ' ').trim().slice(0, 160)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Чи цей static import з `vue` дозволено правилом (усі записи type-only або порожній side-effect).
|
|
75
|
+
* @param {{ moduleRequest: { value: string }, entries: { isType: boolean }[] }} imp запис з `module.staticImports`
|
|
76
|
+
* @returns {boolean} `true`, якщо імпорт дозволено (type-only або `import 'vue'`)
|
|
77
|
+
*/
|
|
78
|
+
function isAllowedVueStaticImport(imp) {
|
|
79
|
+
if (imp.entries.length === 0) {
|
|
80
|
+
return true
|
|
81
|
+
}
|
|
82
|
+
return imp.entries.every(e => e.isType)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Віртуальний шлях для парсера: вміст з `<script>` у `.vue` розбираємо як TypeScript.
|
|
87
|
+
* @param {string} relativePath шлях до файлу в пакеті
|
|
88
|
+
* @returns {string} той самий шлях або з `.vue` заміненим на `.ts`
|
|
89
|
+
*/
|
|
90
|
+
function virtualPathForParse(relativePath) {
|
|
91
|
+
if (relativePath.endsWith('.vue')) {
|
|
92
|
+
return relativePath.replace(VUE_EXT_RE, '.ts')
|
|
93
|
+
}
|
|
94
|
+
return relativePath
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Знаходить заборонені static import з `vue` у вже підготовленому тексті (без `<template>`).
|
|
99
|
+
* Використовує **oxc-parser**; при синтаксичних помилках повертає порожній масив (спочатку виправ синтаксис).
|
|
100
|
+
* @param {string} content вихідний код
|
|
101
|
+
* @param {string} [virtualPath] шлях для вибору `lang` (наприклад `app/src/foo.ts` або віртуальний після `.vue` → `.ts`)
|
|
102
|
+
* @returns {{ line: number, snippet: string }[]} список порушень з номером рядка початку import
|
|
103
|
+
*/
|
|
104
|
+
export function findForbiddenVueImportsInText(content, virtualPath = 'scan.ts') {
|
|
105
|
+
const pathForLang = virtualPath || 'scan.ts'
|
|
106
|
+
const lang = langFromPath(pathForLang)
|
|
107
|
+
let result
|
|
108
|
+
try {
|
|
109
|
+
result = parseSync(pathForLang, content, { lang, sourceType: 'module' })
|
|
110
|
+
} catch {
|
|
111
|
+
return []
|
|
112
|
+
}
|
|
113
|
+
if (result.errors?.length) {
|
|
114
|
+
return []
|
|
115
|
+
}
|
|
116
|
+
/** @type {{ line: number, snippet: string }[]} */
|
|
117
|
+
const out = []
|
|
118
|
+
for (const imp of result.module.staticImports) {
|
|
119
|
+
if (imp.moduleRequest.value === 'vue' && !isAllowedVueStaticImport(imp)) {
|
|
120
|
+
out.push({
|
|
121
|
+
line: offsetToLine(content, imp.start),
|
|
122
|
+
snippet: normalizeSnippet(content.slice(imp.start, imp.end))
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return out
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Чи слід пропустити файл під час обходу пакета (генерація, типи).
|
|
131
|
+
* @param {string} relativePosix шлях з posix-слешами
|
|
132
|
+
* @returns {boolean} `true`, якщо файл не сканувати (`.d.ts`, згенеровані імена)
|
|
133
|
+
*/
|
|
134
|
+
export function shouldSkipFileForVueImportScan(relativePosix) {
|
|
135
|
+
const base = relativePosix.split('/').pop() || ''
|
|
136
|
+
if (base === 'auto-imports.d.ts' || base === 'components.d.ts') {
|
|
137
|
+
return true
|
|
138
|
+
}
|
|
139
|
+
return Boolean(relativePosix.endsWith('.d.ts'))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Чи сканувати цей файл за розширенням.
|
|
144
|
+
* @param {string} relativePath відносний шлях до файлу
|
|
145
|
+
* @returns {boolean} `true`, якщо розширення підходить для пошуку import
|
|
146
|
+
*/
|
|
147
|
+
export function isVueImportScanSourceFile(relativePath) {
|
|
148
|
+
return SOURCE_FILE_RE.test(relativePath)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Знаходить порушення в одному файлі (з урахуванням .vue script extraction).
|
|
153
|
+
* @param {string} content сирий вміст файлу
|
|
154
|
+
* @param {string} relativePath шлях відносно кореня пакета або репо
|
|
155
|
+
* @returns {{ line: number, snippet: string }[]} список порушень для цього файлу
|
|
156
|
+
*/
|
|
157
|
+
export function findForbiddenVueImportsInSourceFile(content, relativePath) {
|
|
158
|
+
const scan = contentForVueImportScan(content, relativePath)
|
|
159
|
+
const virtualPath = virtualPathForParse(relativePath)
|
|
160
|
+
return findForbiddenVueImportsInText(scan, virtualPath)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Чи є рядок-специфікатор імпортом вбудованого Node-модуля.
|
|
165
|
+
* Покриває обидві форми: `node:fs`, `node:timers/promises` (явний префікс) і bare-ім’я
|
|
166
|
+
* вбудованого модуля (`fs`, `path`, `crypto` тощо), включно з підшляхами (`fs/promises`).
|
|
167
|
+
* @param {string} spec значення `moduleRequest.value` (специфікатор імпорту)
|
|
168
|
+
* @returns {boolean} `true`, якщо це Node-нативний модуль
|
|
169
|
+
*/
|
|
170
|
+
export function isNodeBuiltinSpecifier(spec) {
|
|
171
|
+
if (typeof spec !== 'string' || spec.length === 0) {
|
|
172
|
+
return false
|
|
173
|
+
}
|
|
174
|
+
if (spec.startsWith('node:')) {
|
|
175
|
+
return true
|
|
176
|
+
}
|
|
177
|
+
if (NODE_BUILTIN_MODULES.has(spec)) {
|
|
178
|
+
return true
|
|
179
|
+
}
|
|
180
|
+
const slashIdx = spec.indexOf('/')
|
|
181
|
+
if (slashIdx > 0) {
|
|
182
|
+
const head = spec.slice(0, slashIdx)
|
|
183
|
+
if (NODE_BUILTIN_MODULES.has(head)) {
|
|
184
|
+
return true
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return false
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Знаходить заборонені імпорти Node-нативних модулів у вмісті (без `<template>`).
|
|
192
|
+
* Vue SFC виконується у браузері, тож будь-який Node API там недоступний — навіть type-only
|
|
193
|
+
* імпорти збивають з пантелику (краще тримати такий код у server-side утілітах).
|
|
194
|
+
* @param {string} content вихідний код (для `.vue` — вже витягнуті `<script>` блоки)
|
|
195
|
+
* @param {string} [virtualPath] шлях для вибору `lang` (наприклад віртуальний `*.ts` після `.vue`)
|
|
196
|
+
* @returns {{ line: number, snippet: string, specifier: string }[]} список порушень з номером рядка
|
|
197
|
+
*/
|
|
198
|
+
export function findForbiddenNodeImportsInText(content, virtualPath = 'scan.ts') {
|
|
199
|
+
const pathForLang = virtualPath || 'scan.ts'
|
|
200
|
+
const lang = langFromPath(pathForLang)
|
|
201
|
+
let result
|
|
202
|
+
try {
|
|
203
|
+
result = parseSync(pathForLang, content, { lang, sourceType: 'module' })
|
|
204
|
+
} catch {
|
|
205
|
+
return []
|
|
206
|
+
}
|
|
207
|
+
if (result.errors?.length) {
|
|
208
|
+
return []
|
|
209
|
+
}
|
|
210
|
+
/** @type {{ line: number, snippet: string, specifier: string }[]} */
|
|
211
|
+
const out = []
|
|
212
|
+
for (const imp of result.module.staticImports) {
|
|
213
|
+
const spec = imp.moduleRequest.value
|
|
214
|
+
if (isNodeBuiltinSpecifier(spec)) {
|
|
215
|
+
out.push({
|
|
216
|
+
line: offsetToLine(content, imp.start),
|
|
217
|
+
snippet: normalizeSnippet(content.slice(imp.start, imp.end)),
|
|
218
|
+
specifier: spec
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return out
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Знаходить заборонені імпорти Node-нативних модулів у `.vue` SFC.
|
|
227
|
+
* Сканує лише `<script>` блоки (template ігноруємо). Для не-`.vue` файлів повертає `[]` —
|
|
228
|
+
* композаблі/утіліти на Node-side можуть бути в `.ts`/`.js`, а правило стосується SFC.
|
|
229
|
+
* @param {string} content сирий вміст файлу
|
|
230
|
+
* @param {string} relativePath шлях відносно кореня пакета або репо
|
|
231
|
+
* @returns {{ line: number, snippet: string, specifier: string }[]} список порушень
|
|
232
|
+
*/
|
|
233
|
+
export function findForbiddenNodeImportsInVueFile(content, relativePath) {
|
|
234
|
+
if (!relativePath.endsWith('.vue')) {
|
|
235
|
+
return []
|
|
236
|
+
}
|
|
237
|
+
const scan = extractVueScriptBlocks(content)
|
|
238
|
+
const virtualPath = virtualPathForParse(relativePath)
|
|
239
|
+
return findForbiddenNodeImportsInText(scan, virtualPath)
|
|
240
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "auto": { "glob": "**/*.vue" } }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Vue
|
|
3
|
+
version: '2.3'
|
|
4
|
+
globs: "**/*.vue"
|
|
5
|
+
alwaysApply: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Vue 3 Composition API — правила для .cursorrules
|
|
9
|
+
|
|
10
|
+
Правило охоплює стандарти написання Vue 3 SFC з Composition API, конфігурацію Vite-проєкту, UI-бібліотеки, заборонені патерни та вимоги до тестування.
|
|
11
|
+
|
|
12
|
+
## Швидкий gate через conftest
|
|
13
|
+
|
|
14
|
+
Rego-перевірки (запускаються через `npx @7n/rules fix vue`):
|
|
15
|
+
|
|
16
|
+
## Перевірка
|
|
17
|
+
|
|
18
|
+
`npx @7n/rules fix vue` — перевіряє залежності, `vite.config`, наявність **`src/vite-env.d.ts`** з `/// <reference types="vite/client" />` та **`jsconfig.json`** у корені Vue-пакета; обходить джерела Vue-пакета (`.vue`, `.ts`, `.js` тощо) на заборонені value-імпорти з модуля `vue` (дозволені лише type-only та side-effect `import 'vue'`) і додатково сканує `.vue` SFC на імпорти Node-нативних модулів (`node:*` префікс або bare-ім'я вбудованого модуля Node — `fs`, `path`, `timers/promises` тощо). Імпорти аналізуються через **oxc-parser** (`module.staticImports`); для `.vue` вміст `<script>` витягується з SFC, далі той самий парсер (логіка в `npm/rules/vue/lib/vue-forbidden-imports.mjs`).
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
## NHeader — телепорт-слоти на сторінках
|
|
2
|
+
|
|
3
|
+
У проектах з `NHeader` використовуй `<teleport>` для вбудовування контенту сторінки в шапку:
|
|
4
|
+
|
|
5
|
+
| Слот | Призначення |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `#header-subtitle` | Заголовок сторінки (якщо потрібно перевизначити subtitle) |
|
|
8
|
+
| `#header-center` | Важливі повідомлення та елементи управління |
|
|
9
|
+
| `#header-filters` | Фільтри, кнопки та інші елементи управління сторінки |
|
|
10
|
+
|
|
11
|
+
Оскільки `NHeader` за замовчуванням темний, додавай до полів і селектів у телепортах: `dark`, `standout="bg-white text-primary"`, `:options-dark="false"`.
|
|
12
|
+
|
|
13
|
+
Завжди обгортай `<teleport>` в `v-if="mounted"` (де `mounted` — `$ref(false)`, що встановлюється в `onMounted`), щоб уникнути помилки відсутності target-елемента при SSR / першому рендері.
|
|
14
|
+
|
|
15
|
+
```vue
|
|
16
|
+
<template>
|
|
17
|
+
<q-page>
|
|
18
|
+
<!-- ФІЛЬТРИ В ШАПЦІ -->
|
|
19
|
+
<teleport v-if="mounted" to="#header-filters">
|
|
20
|
+
<div class="col row items-center n-gap-sm q-pa-sm">
|
|
21
|
+
<q-input
|
|
22
|
+
v-model="pageStore.filterName"
|
|
23
|
+
:label="t`Поиск по названию`"
|
|
24
|
+
debounce="200"
|
|
25
|
+
standout="bg-white text-primary"
|
|
26
|
+
clearable
|
|
27
|
+
dense
|
|
28
|
+
dark
|
|
29
|
+
class="col"
|
|
30
|
+
style="min-width: 120px">
|
|
31
|
+
<template #prepend>
|
|
32
|
+
<q-icon name="search" />
|
|
33
|
+
</template>
|
|
34
|
+
</q-input>
|
|
35
|
+
|
|
36
|
+
<n-select-multi
|
|
37
|
+
v-model="pageStore.filterRequestTypes"
|
|
38
|
+
:options="requestTypeOptions"
|
|
39
|
+
:label="t`Тип запроса`"
|
|
40
|
+
dark
|
|
41
|
+
standout="bg-white text-primary"
|
|
42
|
+
:options-dark="false"
|
|
43
|
+
style="min-width: 180px; max-width: 400px"
|
|
44
|
+
dense
|
|
45
|
+
emit-value
|
|
46
|
+
map-options
|
|
47
|
+
clearable
|
|
48
|
+
searchable
|
|
49
|
+
:stack-label="false" />
|
|
50
|
+
|
|
51
|
+
<q-btn
|
|
52
|
+
@click="addItem"
|
|
53
|
+
icon="add"
|
|
54
|
+
:label="t`Добавить`"
|
|
55
|
+
color="primary"
|
|
56
|
+
no-caps
|
|
57
|
+
padding="8px 12px"
|
|
58
|
+
unelevated />
|
|
59
|
+
</div>
|
|
60
|
+
</teleport>
|
|
61
|
+
|
|
62
|
+
<!-- ОСНОВНИЙ КОНТЕНТ -->
|
|
63
|
+
...
|
|
64
|
+
</q-page>
|
|
65
|
+
</template>
|
|
66
|
+
<script setup>
|
|
67
|
+
const mounted = $ref(false)
|
|
68
|
+
onMounted(() => { mounted = true })
|
|
69
|
+
</script>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Layout з NHeader
|
|
73
|
+
|
|
74
|
+
Для нових layout-ів використовуй `NHeader` з вбудованими `NLang` і `NMenu`.
|
|
75
|
+
|
|
76
|
+
```vue
|
|
77
|
+
<template>
|
|
78
|
+
<q-layout view="hHh Lpr lFf">
|
|
79
|
+
<n-header
|
|
80
|
+
v-model="leftSideOpened"
|
|
81
|
+
:logo="baseUrl + 'logo.png'"
|
|
82
|
+
:logo-url="homeUrl"
|
|
83
|
+
title="My App"
|
|
84
|
+
:subtitle="subtitle"
|
|
85
|
+
:username="userName"
|
|
86
|
+
toolbar-dark>
|
|
87
|
+
<template #top-toolbar>
|
|
88
|
+
<div class="platform-ios-only q-py-lg" />
|
|
89
|
+
</template>
|
|
90
|
+
<div>default slot content</div>
|
|
91
|
+
</n-header>
|
|
92
|
+
|
|
93
|
+
<!-- ЛЕВАЯ КОЛОНКА -->
|
|
94
|
+
<q-drawer v-model="leftSideOpened" :breakpoint="700" :width="300" overlay class="col column shadow-5 bg-grey-1">
|
|
95
|
+
<div class="platform-ios-only q-py-lg" />
|
|
96
|
+
<div v-if="!$q.screen.gt.xs" class="q-pa-sm row items-center">
|
|
97
|
+
<q-icon name="account_circle" color="primary" size="32px" class="q-mr-sm" />
|
|
98
|
+
<div class="text-subtitle1">{{ user.name }}</div>
|
|
99
|
+
</div>
|
|
100
|
+
<n-menu v-model="activeMenu" :menu="menu" />
|
|
101
|
+
<q-space />
|
|
102
|
+
<n-menu :menu="homeMenu" />
|
|
103
|
+
<div class="platform-ios-only q-py-md" />
|
|
104
|
+
</q-drawer>
|
|
105
|
+
|
|
106
|
+
<q-page-container>
|
|
107
|
+
<router-view />
|
|
108
|
+
</q-page-container>
|
|
109
|
+
</q-layout>
|
|
110
|
+
</template>
|
|
111
|
+
<script setup>
|
|
112
|
+
import { lang, tf as tfm } from '@nitra/tfm'
|
|
113
|
+
const t = tfm.bind({ tr: getTr() })
|
|
114
|
+
|
|
115
|
+
const baseUrl = import.meta.env.BASE_URL
|
|
116
|
+
const homeUrl = String.raw`https:\\` + import.meta.env.VITE_DOMAIN
|
|
117
|
+
|
|
118
|
+
// Ліва колонка
|
|
119
|
+
const leftSideOpened = $ref(false)
|
|
120
|
+
const activeMenu = $ref(null)
|
|
121
|
+
|
|
122
|
+
// Заголовок у шапці з поточного пункту меню
|
|
123
|
+
const subtitle = computed(() => (activeMenu ? getTr()[activeMenu.labelKey]?.[lang.value] || activeMenu?.labelKey : ''))
|
|
124
|
+
|
|
125
|
+
// Меню
|
|
126
|
+
const menu = computed(() =>
|
|
127
|
+
[
|
|
128
|
+
{
|
|
129
|
+
icon: 'sym_o_store',
|
|
130
|
+
label: t`Клиенты`,
|
|
131
|
+
labelKey: t`Клиенты`,
|
|
132
|
+
routeName: 'customer'
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
icon: 'sym_o_route',
|
|
136
|
+
label: t`Маршруты и визиты`,
|
|
137
|
+
items: [
|
|
138
|
+
{
|
|
139
|
+
icon: 'sym_o_route',
|
|
140
|
+
label: t`Маршруты`,
|
|
141
|
+
labelKey: t`Маршруты`,
|
|
142
|
+
routeName: 'route'
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
icon: 'sym_o_event_upcoming',
|
|
146
|
+
label: t`Переносы маршрутов`,
|
|
147
|
+
labelKey: t`Переносы маршрутов`,
|
|
148
|
+
routeName: 'route_postpone'
|
|
149
|
+
}
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
].filter(item => {
|
|
153
|
+
if (item.items?.length) {
|
|
154
|
+
item.items = item.items.filter(i => can[i.permissionRoute || i.routeName])
|
|
155
|
+
return item.items.length > 0
|
|
156
|
+
}
|
|
157
|
+
return can[item.routeName]
|
|
158
|
+
})
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* LOCALIZATION
|
|
163
|
+
* @returns {object} translations
|
|
164
|
+
*/
|
|
165
|
+
function getTr() {
|
|
166
|
+
return {
|
|
167
|
+
Клиенты: { en: 'Customers', ro: 'Clienți', tr: 'Müşteriler' }
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
</script>
|
|
171
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
## Залежності Vue+Vite пакета — канонічний набір і заборони
|
|
2
|
+
|
|
3
|
+
Rego-пакет: `vue.package_json`
|
|
4
|
+
|
|
5
|
+
Цільові файли: `**/package.json`
|
|
6
|
+
|
|
7
|
+
Перевірка активна лише якщо у `dependencies` присутній `vue`.
|
|
8
|
+
|
|
9
|
+
**Обов'язкові залежності:**
|
|
10
|
+
|
|
11
|
+
- `devDependencies.vite` — мажорна версія ≥ 8
|
|
12
|
+
- `devDependencies.@vitejs/plugin-vue`
|
|
13
|
+
- `vue-macros` (dependencies або devDependencies)
|
|
14
|
+
- `unplugin-auto-import` (dependencies або devDependencies)
|
|
15
|
+
- `vite-plugin-vue-layouts-next` (dependencies або devDependencies)
|
|
16
|
+
|
|
17
|
+
**Заборонені залежності (у будь-якій групі):**
|
|
18
|
+
|
|
19
|
+
- `esbuild` — замінено на rolldown
|
|
20
|
+
- `vitest` — замінено на Bun Test Runner (`bun test`)
|
|
21
|
+
- `jsdom` — замінено на happy-dom
|
|
22
|
+
|
|
23
|
+
Приклади:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
✓ "devDependencies": { "vite": "^8.0.0", "@vitejs/plugin-vue": "^6.0.0", "vue-macros": "^3.0.0", "unplugin-auto-import": "^20.0.0", "vite-plugin-vue-layouts-next": "^1.0.0" }
|
|
27
|
+
✗ "devDependencies": { "vite": "^7.0.0", "esbuild": "^0.25.0", "vitest": "^3.0.0", "jsdom": "^25.0.0" }
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Пакети без `vue` у `dependencies` — ігноруються повністю.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Порт перевірки версій з `package.json` для Vue+Vite пакетів з
|
|
2
|
+
# `npm/scripts/rules/vue/fix.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`, то потрібні канонічні Vue/Vite
|
|
9
|
+
# залежності, `devDependencies.vite` має бути мажорної версії ≥ 8, а `esbuild`
|
|
10
|
+
# (міграція на rolldown), `vitest` (заміна на Bun Test Runner) і `jsdom`
|
|
11
|
+
# (заміна на happy-dom) у dependencies/devDependencies заборонені.
|
|
12
|
+
#
|
|
13
|
+
# AST-сканування коду (заборона явних value-імпортів `from 'vue'`, заборона
|
|
14
|
+
# Node-нативних модулів у `.vue` SFC, перевірка `vite.config` на
|
|
15
|
+
# `process.env.npm_lifecycle_event`, vue-macros, auto-import тощо), а також
|
|
16
|
+
# FS-перевірки (`src/vite-env.d.ts`, `jsconfig.json` у корені пакета) — у JS.
|
|
17
|
+
#
|
|
18
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
19
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
20
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
21
|
+
package vue.package_json
|
|
22
|
+
|
|
23
|
+
import rego.v1
|
|
24
|
+
|
|
25
|
+
deny contains msg if {
|
|
26
|
+
uses_vue
|
|
27
|
+
not vite_in_dev_dependencies
|
|
28
|
+
msg := "Vue-пакет: відсутня залежність `vite` у devDependencies (vue.mdc)"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
deny contains msg if {
|
|
32
|
+
uses_vue
|
|
33
|
+
vite_in_dev_dependencies
|
|
34
|
+
not vite_major_at_least_8
|
|
35
|
+
vite_range := input.devDependencies.vite
|
|
36
|
+
msg := sprintf("Vue-пакет: vite має бути >= 8 (зараз %q) (vue.mdc)", [vite_range])
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
deny contains msg if {
|
|
40
|
+
uses_vue
|
|
41
|
+
vite_in_dev_dependencies
|
|
42
|
+
vite_major_at_least_8
|
|
43
|
+
not vite_at_least_8_1
|
|
44
|
+
vite_range := input.devDependencies.vite
|
|
45
|
+
msg := sprintf("Vue-пакет: vite має бути >= 8.1 (зараз %q) (vue.mdc)", [vite_range])
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
deny contains msg if {
|
|
49
|
+
uses_vue
|
|
50
|
+
not "@vitejs/plugin-vue" in dev_dependency_names
|
|
51
|
+
msg := "Vue-пакет: @vitejs/plugin-vue відсутній у devDependencies (vue.mdc)"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
deny contains msg if {
|
|
55
|
+
uses_vue
|
|
56
|
+
not "vue-macros" in all_dependency_names
|
|
57
|
+
msg := "Vue-пакет: vue-macros відсутній (vue.mdc)"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
deny contains msg if {
|
|
61
|
+
uses_vue
|
|
62
|
+
not "unplugin-auto-import" in all_dependency_names
|
|
63
|
+
msg := "Vue-пакет: unplugin-auto-import відсутній (vue.mdc)"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
deny contains msg if {
|
|
67
|
+
uses_vue
|
|
68
|
+
not "vite-plugin-vue-layouts-next" in all_dependency_names
|
|
69
|
+
msg := "Vue-пакет: vite-plugin-vue-layouts-next відсутній (vue.mdc)"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
deny contains msg if {
|
|
73
|
+
uses_vue
|
|
74
|
+
"esbuild" in all_dependency_names
|
|
75
|
+
msg := "Vue-пакет: esbuild заборонено — заміни на rolldown і прибери залежність (vue.mdc)"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
deny contains msg if {
|
|
79
|
+
uses_vue
|
|
80
|
+
"vitest" in all_dependency_names
|
|
81
|
+
msg := "Vue-пакет: vitest заборонено — використовуй Bun Test Runner (`bun test`) і прибери залежність (vue.mdc)"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
deny contains msg if {
|
|
85
|
+
uses_vue
|
|
86
|
+
"jsdom" in all_dependency_names
|
|
87
|
+
msg := "Vue-пакет: jsdom заборонено — використовуй happy-dom і прибери залежність (vue.mdc)"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# ── helpers ────────────────────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
uses_vue if {
|
|
93
|
+
"vue" in object.keys(object.get(input, "dependencies", {}))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
dependency_names := {n | some n in object.keys(object.get(input, "dependencies", {}))}
|
|
97
|
+
|
|
98
|
+
dev_dependency_names := {n | some n in object.keys(object.get(input, "devDependencies", {}))}
|
|
99
|
+
|
|
100
|
+
all_dependency_names contains n if {
|
|
101
|
+
some n in dependency_names
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
all_dependency_names contains n if {
|
|
105
|
+
some n in dev_dependency_names
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
vite_in_dev_dependencies if {
|
|
109
|
+
"vite" in object.keys(object.get(input, "devDependencies", {}))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
vite_major_at_least_8 if {
|
|
113
|
+
range := input.devDependencies.vite
|
|
114
|
+
|
|
115
|
+
# Перша мажорна цифра з рядка: `^8`, `>=8.0.0`, `8.x` → 8.
|
|
116
|
+
match := regex.find_n(`\d+`, range, 1)
|
|
117
|
+
count(match) > 0
|
|
118
|
+
to_number(match[0]) >= 8
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
vite_at_least_8_1 if {
|
|
122
|
+
range := input.devDependencies.vite
|
|
123
|
+
|
|
124
|
+
# Витягуємо першу пару цифр (мажор.мінор) із semver-рядка.
|
|
125
|
+
# Якщо мажор > 8 — завжди true; якщо мажор = 8 — мінор має бути >= 1.
|
|
126
|
+
parts := regex.find_n(`\d+`, range, 2)
|
|
127
|
+
count(parts) >= 1
|
|
128
|
+
major := to_number(parts[0])
|
|
129
|
+
major > 8
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
vite_at_least_8_1 if {
|
|
133
|
+
range := input.devDependencies.vite
|
|
134
|
+
|
|
135
|
+
parts := regex.find_n(`\d+`, range, 2)
|
|
136
|
+
count(parts) >= 2
|
|
137
|
+
major := to_number(parts[0])
|
|
138
|
+
major == 8
|
|
139
|
+
to_number(parts[1]) >= 1
|
|
140
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: main.mjs
|
|
4
|
+
resource: plugins/lang-js/rules/vue/packages/main.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: f5550b8c
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
9
|
+
issues: judge:inaccurate:0.98
|
|
10
|
+
judgeModel: openai-codex/gpt-5.4-mini
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Огляд
|
|
14
|
+
|
|
15
|
+
Модуль визначає, чи є поточний пакет бібліотекою компонентів Vue, та проводить валідацію проєкту на відповідність стандартам, визначеним у `vue.mdc`, враховуючи конфігураційні файли `package-lock.json`, `jsconfig.json`, `package.json`, `extensions.json`.
|
|
16
|
+
|
|
17
|
+
isVueComponentLibraryPkg визначає, чи є пакет бібліотекою компонентів Vue, аналізуючи його залежності.
|
|
18
|
+
main перевіряє відповідність проєкту правилам vue.mdc для пакетів з Vue, включаючи перевірки конфігурації Vite, імпортів, `esbuild` та рекомендації IDE. Логіка роботи не викликає винятків назовні (fail-safe), ігноруючи каталоги `.git` та `node_modules`. (vue.mdc)
|
|
19
|
+
|
|
20
|
+
## Поведінка
|
|
21
|
+
|
|
22
|
+
Поведінка
|
|
23
|
+
isVueComponentLibraryPkg визначає, чи є пакет бібліотекою компонентів Vue, аналізуючи його залежності.
|
|
24
|
+
main перевіряє відповідність проєкту правилам vue.mdc для пакетів з Vue, включаючи перевірки конфігурації Vite, імпортів, `esbuild` та рекомендації IDE. При цьому ігноруються каталоги `.git` та `node_modules`, а також файли локів `package-lock.json`, `bun.lock` тощо.
|
|
25
|
+
|
|
26
|
+
## Публічний API
|
|
27
|
+
|
|
28
|
+
isVueComponentLibraryPkg — визначає, чи повинен проект розглядатися як бібліотека компонентів Vue, що впливає на правила імпорту в IDE.
|
|
29
|
+
main — гарантує, що всі компоненти у робочому просторі коректно декларують залежність від Vue відповідно до стандартів.
|
|
30
|
+
|
|
31
|
+
## Гарантії поведінки
|
|
32
|
+
|
|
33
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
|
34
|
+
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
35
|
+
- Свідомо пропускає шляхи: `.git`, `node_modules`.
|