@7n/rules-lang-js 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +14 -4
  3. package/rules/bun/bunfig/bunfig.mdc +17 -0
  4. package/rules/bun/bunfig/bunfig.rego +29 -0
  5. package/rules/bun/bunfig/concern.json +9 -0
  6. package/rules/bun/bunfig/template/bunfig.toml.snippet.toml +2 -0
  7. package/rules/bun/docs/index.md +11 -0
  8. package/rules/bun/layout/concern.json +16 -0
  9. package/rules/bun/layout/docs/fix-layout.md +29 -0
  10. package/rules/bun/layout/docs/main.md +34 -0
  11. package/rules/bun/layout/fix-layout.mjs +63 -0
  12. package/rules/bun/layout/layout.mdc +60 -0
  13. package/rules/bun/layout/main.mjs +53 -0
  14. package/rules/bun/licensee/concern.json +7 -0
  15. package/rules/bun/licensee/docs/fix-licensee.md +27 -0
  16. package/rules/bun/licensee/docs/index.md +12 -0
  17. package/rules/bun/licensee/docs/main.md +30 -0
  18. package/rules/bun/licensee/fix-licensee.mjs +31 -0
  19. package/rules/bun/licensee/main.mjs +68 -0
  20. package/rules/bun/lint-surface/concern.json +3 -0
  21. package/rules/bun/lint-surface/lint-surface.mdc +13 -0
  22. package/rules/bun/main.json +1 -0
  23. package/rules/bun/main.mdc +11 -0
  24. package/rules/bun/package_json/concern.json +9 -0
  25. package/rules/bun/package_json/docs/fix-package_json.md +28 -0
  26. package/rules/bun/package_json/docs/index.md +9 -0
  27. package/rules/bun/package_json/fix-package_json.mjs +311 -0
  28. package/rules/bun/package_json/package_json.mdc +14 -0
  29. package/rules/bun/package_json/package_json.rego +64 -0
  30. package/rules/bun/package_json/template/package.json.deny.json +4 -0
  31. package/rules/js/check/check.mdc +26 -0
  32. package/rules/js/check/concern.json +19 -0
  33. package/rules/js/check/docs/eslint-config.md +56 -0
  34. package/rules/js/check/docs/fix-check.md +48 -0
  35. package/rules/js/check/docs/index.md +13 -0
  36. package/rules/js/check/docs/main.md +48 -0
  37. package/rules/js/check/eslint-config.mjs +262 -0
  38. package/rules/js/check/fix-check.mjs +82 -0
  39. package/rules/js/check/main.mjs +310 -0
  40. package/rules/js/dep-policy/concern.json +4 -0
  41. package/rules/js/dep-policy/dep-policy.mdc +36 -0
  42. package/rules/js/dep-policy/docs/main.md +35 -0
  43. package/rules/js/dep-policy/main.mjs +99 -0
  44. package/rules/js/docs/index.md +11 -0
  45. package/rules/js/eslint/concern.json +8 -0
  46. package/rules/js/eslint/docs/fix-eslint.md +50 -0
  47. package/rules/js/eslint/docs/fix-worker.md +31 -0
  48. package/rules/js/eslint/docs/index.md +11 -0
  49. package/rules/js/eslint/docs/main.md +35 -0
  50. package/rules/js/eslint/fix-eslint.mjs +160 -0
  51. package/rules/js/eslint/fix-worker.mjs +139 -0
  52. package/rules/js/eslint/main.mjs +115 -0
  53. package/rules/js/file-extensions/concern.json +3 -0
  54. package/rules/js/file-extensions/file-extensions.mdc +12 -0
  55. package/rules/js/jscpd_config/concern.json +11 -0
  56. package/rules/js/jscpd_config/docs/fix-jscpd_config.md +25 -0
  57. package/rules/js/jscpd_config/docs/index.md +9 -0
  58. package/rules/js/jscpd_config/fix-jscpd_config.mjs +3 -0
  59. package/rules/js/jscpd_config/jscpd_config.mdc +42 -0
  60. package/rules/js/jscpd_config/jscpd_config.rego +44 -0
  61. package/rules/js/jscpd_config/template/.jscpd.json.snippet.json +7 -0
  62. package/rules/js/jscpd_duplicates/concern.json +7 -0
  63. package/rules/js/jscpd_duplicates/docs/main.md +29 -0
  64. package/rules/js/jscpd_duplicates/main.mjs +69 -0
  65. package/rules/js/knip/concern.json +7 -0
  66. package/rules/js/knip/docs/main.md +31 -0
  67. package/rules/js/knip/knip.mdc +15 -0
  68. package/rules/js/knip/main.mjs +89 -0
  69. package/rules/js/lint-findings/concern.json +3 -0
  70. package/rules/js/lint-findings/docs/main.md +40 -0
  71. package/rules/js/lint-findings/main.mjs +125 -0
  72. package/rules/js/main.json +1 -0
  73. package/rules/js/main.mdc +18 -0
  74. package/rules/js/package_json/concern.json +9 -0
  75. package/rules/js/package_json/docs/fix-package_json.md +25 -0
  76. package/rules/js/package_json/docs/index.md +9 -0
  77. package/rules/js/package_json/fix-package_json.mjs +3 -0
  78. package/rules/js/package_json/package_json.mdc +15 -0
  79. package/rules/js/package_json/package_json.rego +142 -0
  80. package/rules/js/package_json/template/package.json.snippet.json +6 -0
  81. package/rules/js/tooling/concern.json +3 -0
  82. package/rules/js/tooling/data/tooling/knip-canonical.json +30 -0
  83. package/rules/js/tooling/data/tooling/oxlint-canonical.json +400 -0
  84. package/rules/js/tooling/docs/main.md +53 -0
  85. package/rules/js/tooling/main.mjs +183 -0
  86. package/rules/js/utils_imports/concern.json +4 -0
  87. package/rules/js/utils_imports/docs/main.md +50 -0
  88. package/rules/js/utils_imports/main.mjs +185 -0
  89. package/rules/js/utils_imports/utils_imports.mdc +15 -0
  90. package/rules/js/vscode_extensions/concern.json +11 -0
  91. package/rules/js/vscode_extensions/docs/fix-vscode_extensions.md +24 -0
  92. package/rules/js/vscode_extensions/docs/index.md +11 -0
  93. package/rules/js/vscode_extensions/fix-vscode_extensions.mjs +1 -0
  94. package/rules/js/vscode_extensions/template/extensions.json.snippet.json +6 -0
  95. package/rules/js/vscode_extensions/vscode_extensions.mdc +11 -0
  96. package/rules/js/vscode_extensions/vscode_extensions.rego +12 -0
  97. package/rules/js-bun-db/connection/concern.json +3 -0
  98. package/rules/js-bun-db/connection/connection.mdc +42 -0
  99. package/rules/js-bun-db/docs/index.md +11 -0
  100. package/rules/js-bun-db/lib/bun-sql-scan.mjs +1047 -0
  101. package/rules/js-bun-db/lib/docs/bun-sql-scan.md +63 -0
  102. package/rules/js-bun-db/lib/docs/index.md +11 -0
  103. package/rules/js-bun-db/main.json +1 -0
  104. package/rules/js-bun-db/main.mdc +8 -0
  105. package/rules/js-bun-db/package_json/concern.json +9 -0
  106. package/rules/js-bun-db/package_json/package_json.mdc +31 -0
  107. package/rules/js-bun-db/package_json/package_json.rego +15 -0
  108. package/rules/js-bun-db/package_json/template/package.json.deny.json +6 -0
  109. package/rules/js-bun-db/pg_format_identifiers/concern.json +3 -0
  110. package/rules/js-bun-db/pg_format_identifiers/pg_format_identifiers.mdc +104 -0
  111. package/rules/js-bun-db/safety/concern.json +4 -0
  112. package/rules/js-bun-db/safety/docs/main.md +34 -0
  113. package/rules/js-bun-db/safety/main.mjs +430 -0
  114. package/rules/js-bun-db/safety/safety.mdc +458 -0
  115. package/rules/js-bun-redis/docs/index.md +11 -0
  116. package/rules/js-bun-redis/imports/concern.json +4 -0
  117. package/rules/js-bun-redis/imports/docs/main.md +36 -0
  118. package/rules/js-bun-redis/imports/imports.mdc +47 -0
  119. package/rules/js-bun-redis/imports/main.mjs +88 -0
  120. package/rules/js-bun-redis/lib/docs/index.md +11 -0
  121. package/rules/js-bun-redis/lib/docs/redis-imports.md +227 -0
  122. package/rules/js-bun-redis/lib/redis-imports.mjs +130 -0
  123. package/rules/js-bun-redis/main.json +1 -0
  124. package/rules/js-bun-redis/main.mdc +8 -0
  125. package/rules/js-bun-redis/package_json/concern.json +9 -0
  126. package/rules/js-bun-redis/package_json/package_json.mdc +11 -0
  127. package/rules/js-bun-redis/package_json/package_json.rego +15 -0
  128. package/rules/js-bun-redis/package_json/template/package.json.deny.json +12 -0
  129. package/rules/js-mssql/deps/concern.json +4 -0
  130. package/rules/js-mssql/deps/docs/main.md +33 -0
  131. package/rules/js-mssql/deps/main.mjs +297 -0
  132. package/rules/js-mssql/docs/index.md +11 -0
  133. package/rules/js-mssql/lib/docs/index.md +11 -0
  134. package/rules/js-mssql/lib/docs/mssql-pool-scan.md +380 -0
  135. package/rules/js-mssql/lib/mssql-pool-scan.mjs +610 -0
  136. package/rules/js-mssql/main.json +1 -0
  137. package/rules/js-mssql/main.mdc +144 -0
  138. package/rules/js-mssql/mssql-tvp/concern.json +3 -0
  139. package/rules/js-mssql/mssql-tvp/mssql-tvp.mdc +77 -0
  140. package/rules/js-mssql/package_json/concern.json +9 -0
  141. package/rules/js-mssql/package_json/package_json.mdc +9 -0
  142. package/rules/js-mssql/package_json/package_json.rego +57 -0
  143. package/rules/js-run/configmap/concern.json +9 -0
  144. package/rules/js-run/configmap/configmap.mdc +37 -0
  145. package/rules/js-run/configmap/configmap.rego +21 -0
  146. package/rules/js-run/configmap/template/configmap.yaml.contains.yml +4 -0
  147. package/rules/js-run/docs/index.md +11 -0
  148. package/rules/js-run/jsconfig/concern.json +9 -0
  149. package/rules/js-run/jsconfig/docs/fix-jsconfig.md +28 -0
  150. package/rules/js-run/jsconfig/docs/index.md +9 -0
  151. package/rules/js-run/jsconfig/fix-jsconfig.mjs +119 -0
  152. package/rules/js-run/jsconfig/jsconfig.mdc +48 -0
  153. package/rules/js-run/jsconfig/jsconfig.rego +59 -0
  154. package/rules/js-run/jsconfig/template/jsconfig.json.snippet.json +10 -0
  155. package/rules/js-run/lib/bunyan-imports.mjs +98 -0
  156. package/rules/js-run/lib/check-env-scan.mjs +338 -0
  157. package/rules/js-run/lib/conn-file-rules.mjs +214 -0
  158. package/rules/js-run/lib/conn-imports-scan.mjs +154 -0
  159. package/rules/js-run/lib/docs/bunyan-imports.md +121 -0
  160. package/rules/js-run/lib/docs/check-env-scan.md +438 -0
  161. package/rules/js-run/lib/docs/conn-file-rules.md +304 -0
  162. package/rules/js-run/lib/docs/conn-imports-scan.md +208 -0
  163. package/rules/js-run/lib/docs/index.md +16 -0
  164. package/rules/js-run/lib/docs/promise-settimeout-scan.md +334 -0
  165. package/rules/js-run/lib/docs/temporal-scan.md +29 -0
  166. package/rules/js-run/lib/promise-settimeout-scan.mjs +128 -0
  167. package/rules/js-run/lib/temporal-scan.mjs +52 -0
  168. package/rules/js-run/main.json +1 -0
  169. package/rules/js-run/main.mdc +16 -0
  170. package/rules/js-run/package_json/concern.json +9 -0
  171. package/rules/js-run/package_json/package_json.mdc +44 -0
  172. package/rules/js-run/package_json/package_json.rego +37 -0
  173. package/rules/js-run/package_json/template/package.json.deny.json +22 -0
  174. package/rules/js-run/project-structure/concern.json +3 -0
  175. package/rules/js-run/project-structure/project-structure.mdc +11 -0
  176. package/rules/js-run/runtime/concern.json +12 -0
  177. package/rules/js-run/runtime/docs/fix-runtime.md +27 -0
  178. package/rules/js-run/runtime/docs/main.md +35 -0
  179. package/rules/js-run/runtime/fix-runtime.mjs +46 -0
  180. package/rules/js-run/runtime/main.mjs +496 -0
  181. package/rules/js-run/runtime/runtime.mdc +184 -0
  182. package/rules/js-run/scope/concern.json +3 -0
  183. package/rules/js-run/scope/scope.mdc +11 -0
  184. package/rules/npm-module/docs/index.md +11 -0
  185. package/rules/npm-module/emit_types_config/concern.json +9 -0
  186. package/rules/npm-module/emit_types_config/docs/fix-emit_types_config.md +25 -0
  187. package/rules/npm-module/emit_types_config/docs/index.md +9 -0
  188. package/rules/npm-module/emit_types_config/emit_types_config.mdc +43 -0
  189. package/rules/npm-module/emit_types_config/emit_types_config.rego +28 -0
  190. package/rules/npm-module/emit_types_config/fix-emit_types_config.mjs +5 -0
  191. package/rules/npm-module/emit_types_config/template/tsconfig.emit-types.json.snippet.json +9 -0
  192. package/rules/npm-module/header_doc_pointer/concern.json +5 -0
  193. package/rules/npm-module/header_doc_pointer/docs/main.md +40 -0
  194. package/rules/npm-module/header_doc_pointer/header_doc_pointer.mdc +18 -0
  195. package/rules/npm-module/header_doc_pointer/main.mjs +131 -0
  196. package/rules/npm-module/main.json +1 -0
  197. package/rules/npm-module/main.mdc +36 -0
  198. package/rules/npm-module/npm_package_json/concern.json +9 -0
  199. package/rules/npm-module/npm_package_json/docs/fix-npm_package_json.md +26 -0
  200. package/rules/npm-module/npm_package_json/docs/index.md +9 -0
  201. package/rules/npm-module/npm_package_json/fix-npm_package_json.mjs +5 -0
  202. package/rules/npm-module/npm_package_json/npm_package_json.mdc +57 -0
  203. package/rules/npm-module/npm_package_json/npm_package_json.rego +73 -0
  204. package/rules/npm-module/npm_package_json/template/package.json.snippet.json +1 -0
  205. package/rules/npm-module/package_structure/concern.json +5 -0
  206. package/rules/npm-module/package_structure/docs/main.md +37 -0
  207. package/rules/npm-module/package_structure/main.mjs +448 -0
  208. package/rules/npm-module/package_structure/package_structure.mdc +63 -0
  209. package/rules/npm-module/root_package_json/concern.json +9 -0
  210. package/rules/npm-module/root_package_json/docs/fix-root_package_json.md +25 -0
  211. package/rules/npm-module/root_package_json/docs/index.md +9 -0
  212. package/rules/npm-module/root_package_json/fix-root_package_json.mjs +5 -0
  213. package/rules/npm-module/root_package_json/root_package_json.mdc +41 -0
  214. package/rules/npm-module/root_package_json/root_package_json.rego +28 -0
  215. package/rules/npm-module/root_package_json/template/package.json.snippet.json +1 -0
  216. package/rules/npm-module/rule_meta/concern.json +8 -0
  217. package/rules/npm-module/rule_meta/docs/main.md +35 -0
  218. package/rules/npm-module/rule_meta/main.mjs +119 -0
  219. package/rules/npm-module/rule_meta/rule_meta.mdc +11 -0
  220. package/rules/npm-module/skill_meta/concern.json +5 -0
  221. package/rules/npm-module/skill_meta/docs/main.md +149 -0
  222. package/rules/npm-module/skill_meta/main.mjs +91 -0
  223. package/rules/npm-module/skill_meta/skill_meta.mdc +11 -0
  224. package/rules/tool-surface/docs/index.md +11 -0
  225. package/rules/tool-surface/main.json +6 -0
  226. package/rules/tool-surface/main.mdc +72 -0
  227. package/rules/vue/composition-api/composition-api.mdc +82 -0
  228. package/rules/vue/composition-api/concern.json +3 -0
  229. package/rules/vue/docs/index.md +11 -0
  230. package/rules/vue/lib/docs/index.md +11 -0
  231. package/rules/vue/lib/docs/vue-forbidden-imports.md +265 -0
  232. package/rules/vue/lib/vue-forbidden-imports.mjs +240 -0
  233. package/rules/vue/main.json +1 -0
  234. package/rules/vue/main.mdc +18 -0
  235. package/rules/vue/nheader-layout/concern.json +3 -0
  236. package/rules/vue/nheader-layout/nheader-layout.mdc +171 -0
  237. package/rules/vue/package_json/concern.json +9 -0
  238. package/rules/vue/package_json/package_json.mdc +30 -0
  239. package/rules/vue/package_json/package_json.rego +140 -0
  240. package/rules/vue/packages/concern.json +6 -0
  241. package/rules/vue/packages/docs/index.md +11 -0
  242. package/rules/vue/packages/docs/main.md +35 -0
  243. package/rules/vue/packages/main.mjs +575 -0
  244. package/rules/vue/packages/packages.mdc +56 -0
  245. package/rules/vue/quasar-ui/concern.json +3 -0
  246. package/rules/vue/quasar-ui/quasar-ui.mdc +32 -0
  247. package/rules/vue/structure/concern.json +3 -0
  248. package/rules/vue/structure/structure.mdc +101 -0
  249. package/rules/vue/testing/concern.json +3 -0
  250. package/rules/vue/testing/testing.mdc +40 -0
  251. package/rules/vue/tfm-translations/concern.json +7 -0
  252. package/rules/vue/tfm-translations/docs/main.md +29 -0
  253. package/rules/vue/tfm-translations/main.mjs +55 -0
  254. package/rules/vue/tfm-translations/tfm-translations.mdc +32 -0
  255. package/rules/vue/vite-config/concern.json +3 -0
  256. package/rules/vue/vite-config/vite-config.mdc +153 -0
  257. package/rules/vue/vite-env/concern.json +3 -0
  258. 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,3 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json"
3
+ }
@@ -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,9 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
+ "fixability": "config",
4
+ "policy": {
5
+ "files": {
6
+ "walkGlob": "**/package.json"
7
+ }
8
+ }
9
+ }
@@ -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,6 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
+ "lint": {
4
+ "scope": "full"
5
+ }
6
+ }
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: Directory Index
3
+ title: npm/rules/vue/packages
4
+ resource: plugins/lang-js/rules/vue/packages/
5
+ ---
6
+
7
+ # npm/rules/vue/packages
8
+
9
+ | Файл | Тип |
10
+ | ------------------- | --------- |
11
+ | [main.mjs](main.md) | JS Module |
@@ -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`.