@nitra/cursor 3.21.1 → 3.23.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.
Files changed (231) hide show
  1. package/.pi-template/extensions/n-cursor-adr/docs/index.md +181 -0
  2. package/CHANGELOG.md +37 -3
  3. package/bin/docs/n-cursor.md +636 -0
  4. package/bin/docs/rename-yaml-extensions.md +207 -0
  5. package/bin/n-cursor.js +30 -3
  6. package/package.json +1 -1
  7. package/rules/abie/docs/fix.md +18 -0
  8. package/rules/abie/js/docs/applies.md +26 -0
  9. package/rules/abie/js/docs/env_dns.md +32 -0
  10. package/rules/abie/js/docs/firebase_hosting.md +23 -0
  11. package/rules/abie/js/docs/hc_pairing.md +35 -0
  12. package/rules/abie/js/docs/ua_http_route.md +28 -0
  13. package/rules/abie/js/docs/ua_node_selector.md +28 -0
  14. package/rules/abie/lib/docs/enabled.md +29 -0
  15. package/rules/abie/lib/docs/env-dns.md +35 -0
  16. package/rules/abie/lib/docs/hc-yaml.md +33 -0
  17. package/rules/abie/lib/docs/http-route.md +44 -0
  18. package/rules/abie/lib/docs/k8s-tree.md +40 -0
  19. package/rules/abie/lib/docs/kustomization-patches.md +47 -0
  20. package/rules/abie/lib/docs/overlay-paths.md +38 -0
  21. package/rules/abie/lib/docs/yaml.md +29 -0
  22. package/rules/adr/docs/fix.md +148 -0
  23. package/rules/adr/js/docs/hooks.md +259 -0
  24. package/rules/bun/docs/fix.md +156 -0
  25. package/rules/bun/js/docs/layout.md +393 -0
  26. package/rules/capacitor/docs/fix.md +121 -0
  27. package/rules/capacitor/js/docs/platforms.md +295 -0
  28. package/rules/changelog/changelog.mdc +2 -2
  29. package/rules/changelog/docs/fix.md +174 -0
  30. package/rules/changelog/js/consistency.mjs +114 -13
  31. package/rules/changelog/js/docs/consistency.md +387 -0
  32. package/rules/changelog/lib/docs/package-manifest.md +210 -0
  33. package/rules/ci4/docs/fix.md +179 -0
  34. package/rules/ci4/js/docs/marksman_config.md +128 -0
  35. package/rules/docker/docker.mdc +8 -3
  36. package/rules/docker/docs/fix.md +171 -0
  37. package/rules/docker/js/docs/lint.md +258 -0
  38. package/rules/docker/lib/docs/docker-hadolint.md +184 -0
  39. package/rules/docker/lib/docs/docker-mirror.md +247 -0
  40. package/rules/docker/lib/docs/docker-native-addon.md +170 -0
  41. package/rules/docker/lib/docs/docker-nginx-user.md +219 -0
  42. package/rules/docker/lint/docs/lint.md +193 -0
  43. package/rules/efes/docs/fix.md +203 -0
  44. package/rules/feedback/docs/fix.md +140 -0
  45. package/rules/flow/docs/fix.md +152 -0
  46. package/rules/ga/docs/fix.md +158 -0
  47. package/rules/ga/js/docs/lint.md +100 -0
  48. package/rules/ga/js/docs/workflows.md +217 -0
  49. package/rules/ga/lint/docs/lint.md +209 -0
  50. package/rules/ga/policy/clean_merged_branch/clean_merged_branch.rego +11 -2
  51. package/rules/ga/policy/clean_merged_branch/template/clean-merged-branch.yml.snippet.yml +3 -4
  52. package/rules/graphql/docs/fix.md +126 -0
  53. package/rules/graphql/js/docs/tooling.md +264 -0
  54. package/rules/graphql/lib/docs/graphql-gql-scan.md +219 -0
  55. package/rules/hasura/docs/fix.md +120 -0
  56. package/rules/hasura/hasura.mdc +14 -0
  57. package/rules/hasura/js/docs/internal_urls.md +326 -0
  58. package/rules/image-avif/docs/fix.md +132 -0
  59. package/rules/image-avif/js/docs/avif_generation.md +241 -0
  60. package/rules/image-compress/docs/fix.md +150 -0
  61. package/rules/image-compress/js/docs/package_setup.md +191 -0
  62. package/rules/js-bun-db/docs/fix.md +148 -0
  63. package/rules/js-bun-db/js/docs/safety.md +231 -0
  64. package/rules/js-bun-db/js-bun-db.mdc +42 -13
  65. package/rules/js-bun-db/lib/docs/bun-sql-scan.md +347 -0
  66. package/rules/js-bun-redis/docs/fix.md +123 -0
  67. package/rules/js-bun-redis/js/docs/imports.md +176 -0
  68. package/rules/js-bun-redis/lib/docs/redis-imports.md +223 -0
  69. package/rules/js-lint/docs/fix.md +117 -0
  70. package/rules/js-lint/js/docs/lint.md +250 -0
  71. package/rules/js-lint/js/docs/tooling.md +348 -0
  72. package/rules/js-lint/js/docs/utils_imports.md +207 -0
  73. package/rules/js-lint/js/lint-findings.mjs +110 -0
  74. package/rules/js-lint/js/lint.mjs +86 -15
  75. package/rules/js-lint-ci/docs/fix.md +154 -0
  76. package/rules/js-lint-ci/js/docs/lint.md +144 -0
  77. package/rules/js-mssql/docs/fix.md +128 -0
  78. package/rules/js-mssql/js/docs/deps.md +263 -0
  79. package/rules/js-mssql/lib/docs/mssql-pool-scan.md +367 -0
  80. package/rules/js-run/docs/fix.md +144 -0
  81. package/rules/js-run/js/docs/runtime.md +388 -0
  82. package/rules/js-run/lib/docs/bunyan-imports.md +117 -0
  83. package/rules/js-run/lib/docs/check-env-scan.md +433 -0
  84. package/rules/js-run/lib/docs/conn-file-rules.md +300 -0
  85. package/rules/js-run/lib/docs/conn-imports-scan.md +204 -0
  86. package/rules/js-run/lib/docs/promise-settimeout-scan.md +326 -0
  87. package/rules/k8s/docs/fix.md +129 -0
  88. package/rules/k8s/js/docs/manifests.md +344 -0
  89. package/rules/k8s/js/manifests.mjs +6 -2
  90. package/rules/k8s/k8s.mdc +4 -2
  91. package/rules/k8s/lint/docs/lint.md +411 -0
  92. package/rules/k8s/policy/network_policy/template/deployment.snippet.yaml +2 -0
  93. package/rules/k8s/policy/network_policy/template/stateful-set.snippet.yaml +2 -0
  94. package/rules/nginx-default-tpl/docs/fix.md +124 -0
  95. package/rules/nginx-default-tpl/js/docs/template.md +378 -0
  96. package/rules/npm-module/docs/fix.md +98 -0
  97. package/rules/npm-module/js/docs/package_structure.md +274 -0
  98. package/rules/npm-module/js/docs/rule_meta.md +137 -0
  99. package/rules/npm-module/js/docs/skill_meta.md +190 -0
  100. package/rules/php/docs/fix.md +107 -0
  101. package/rules/php/js/docs/tooling.md +152 -0
  102. package/rules/php/lint/docs/lint.md +215 -0
  103. package/rules/python/docs/fix.md +163 -0
  104. package/rules/python/js/docs/applies.md +108 -0
  105. package/rules/python/js/docs/tooling.md +153 -0
  106. package/rules/python/lint/docs/lint.md +322 -0
  107. package/rules/rego/docs/fix.md +121 -0
  108. package/rules/rego/js/docs/applies.md +174 -0
  109. package/rules/rego/js/docs/lint.md +118 -0
  110. package/rules/rego/lint/docs/lint.md +204 -0
  111. package/rules/release/docs/change.md +185 -0
  112. package/rules/release/docs/fix.md +119 -0
  113. package/rules/release/docs/release.md +222 -0
  114. package/rules/release/lib/docs/aggregate.md +246 -0
  115. package/rules/release/lib/docs/change-file.md +200 -0
  116. package/rules/release/lib/docs/fallback.md +203 -0
  117. package/rules/rust/docs/fix.md +129 -0
  118. package/rules/rust/js/docs/applies.md +140 -0
  119. package/rules/rust/lib/docs/has-cargo-toml.md +130 -0
  120. package/rules/security/docs/fix.md +86 -0
  121. package/rules/security/js/docs/lint.md +171 -0
  122. package/rules/security/js/docs/sample_secret.md +190 -0
  123. package/rules/security/js/docs/trufflehog.md +137 -0
  124. package/rules/security/js/lint.mjs +9 -1
  125. package/rules/style-lint/docs/fix.md +155 -0
  126. package/rules/style-lint/js/docs/lint.md +184 -0
  127. package/rules/style-lint/js/docs/tooling.md +194 -0
  128. package/rules/tauri/docs/fix.md +158 -0
  129. package/rules/tauri/js/docs/cargo_mutants_config.md +168 -0
  130. package/rules/tauri/js/docs/tooling.md +228 -0
  131. package/rules/test/coverage/coverage.mjs +15 -3
  132. package/rules/test/docs/fix.md +132 -0
  133. package/rules/test/js/data/stryker_config/docs/stryker-vue-macros-ignorer.md +138 -0
  134. package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +134 -0
  135. package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +160 -0
  136. package/rules/test/js/data/vitest_config/docs/vitest.config.baseline.md +195 -0
  137. package/rules/test/js/docs/cargo_mutants_config.md +173 -0
  138. package/rules/test/js/docs/location.md +136 -0
  139. package/rules/test/js/docs/no-process-chdir.md +160 -0
  140. package/rules/test/js/docs/no-relative-fs-path.md +271 -0
  141. package/rules/test/js/docs/stryker_config.md +152 -0
  142. package/rules/test/js/docs/vitest-config-pool-forks.md +174 -0
  143. package/rules/text/docs/fix.md +118 -0
  144. package/rules/text/js/docs/forbidden-prettier.md +143 -0
  145. package/rules/text/js/docs/formatting.md +256 -0
  146. package/rules/text/js/docs/lint.md +122 -0
  147. package/rules/text/lint/docs/lint.md +220 -0
  148. package/rules/text/lint/docs/run-dotenv-linter.md +157 -0
  149. package/rules/text/lint/docs/run-shellcheck.md +212 -0
  150. package/rules/text/lint/docs/run-v8r.md +197 -0
  151. package/rules/vue/docs/fix.md +127 -0
  152. package/rules/vue/js/docs/packages.md +335 -0
  153. package/rules/vue/lib/docs/vue-forbidden-imports.md +261 -0
  154. package/rules/worktree/docs/fix.md +161 -0
  155. package/schemas/rule-meta.json +5 -1
  156. package/scripts/auto-rules.mjs +7 -4
  157. package/scripts/coverage-classify/docs/apply.md +202 -0
  158. package/scripts/coverage-classify/docs/cache.md +203 -0
  159. package/scripts/coverage-classify/docs/index.md +218 -0
  160. package/scripts/coverage-classify/docs/prompt.md +132 -0
  161. package/scripts/coverage-classify/docs/verdict-schema.md +169 -0
  162. package/scripts/coverage-fix-extract.mjs +122 -0
  163. package/scripts/coverage-fix.mjs +1 -1
  164. package/scripts/dispatcher/docs/graph.md +346 -0
  165. package/scripts/dispatcher/docs/index.md +236 -0
  166. package/scripts/dispatcher/docs/trace.md +296 -0
  167. package/scripts/dispatcher/index.mjs +1 -1
  168. package/scripts/dispatcher/lib/active.mjs +4 -8
  169. package/scripts/dispatcher/lib/commands.mjs +7 -11
  170. package/scripts/dispatcher/lib/docs/active.md +348 -0
  171. package/scripts/dispatcher/lib/docs/artifact.md +232 -0
  172. package/scripts/dispatcher/lib/docs/budget.md +167 -0
  173. package/scripts/dispatcher/lib/docs/capability.md +196 -0
  174. package/scripts/dispatcher/lib/docs/commands.md +210 -0
  175. package/scripts/dispatcher/lib/docs/events.md +182 -0
  176. package/scripts/dispatcher/lib/docs/executor.md +190 -0
  177. package/scripts/dispatcher/lib/docs/flow-lock.md +161 -0
  178. package/scripts/dispatcher/lib/docs/flow-resolve.md +267 -0
  179. package/scripts/dispatcher/lib/docs/gate.md +231 -0
  180. package/scripts/dispatcher/lib/docs/level.md +335 -0
  181. package/scripts/dispatcher/lib/docs/plan-panel.md +181 -0
  182. package/scripts/dispatcher/lib/docs/plan.md +200 -0
  183. package/scripts/dispatcher/lib/docs/planner.md +269 -0
  184. package/scripts/dispatcher/lib/docs/review.md +255 -0
  185. package/scripts/dispatcher/lib/docs/reviewer.md +240 -0
  186. package/scripts/dispatcher/lib/docs/snapshot.md +247 -0
  187. package/scripts/dispatcher/lib/docs/spec.md +203 -0
  188. package/scripts/dispatcher/lib/docs/state-store.md +303 -0
  189. package/scripts/dispatcher/lib/docs/subagent-runner.md +173 -0
  190. package/scripts/dispatcher/lib/executor.mjs +6 -1
  191. package/scripts/dispatcher/lib/flow-resolve.mjs +3 -1
  192. package/scripts/dispatcher/lib/level.mjs +29 -3
  193. package/scripts/dispatcher/lib/review.mjs +1 -1
  194. package/scripts/dispatcher/lib/subagent-runner.mjs +5 -3
  195. package/scripts/docs/auto-rules.md +376 -0
  196. package/scripts/docs/auto-skills.md +173 -0
  197. package/scripts/docs/build-agents-commands.md +183 -0
  198. package/scripts/docs/cli-entry.md +153 -0
  199. package/scripts/docs/coverage-fix.md +177 -0
  200. package/scripts/docs/ensure-nitra-cursor-dev-dependencies.md +189 -0
  201. package/scripts/lib/changed-files.mjs +4 -1
  202. package/scripts/lib/diff-added-lines.mjs +85 -0
  203. package/scripts/lib/docs/changed-files.md +149 -0
  204. package/scripts/lib/docs/check-mdc-template-refs.md +222 -0
  205. package/scripts/lib/docs/check-reporter.md +175 -0
  206. package/scripts/lib/docs/discover-check-rules-from-cursor.md +157 -0
  207. package/scripts/lib/docs/discover-checkable-rules.md +165 -0
  208. package/scripts/lib/docs/ensure-tool.md +254 -0
  209. package/scripts/lib/docs/generated-markdown.md +275 -0
  210. package/scripts/lib/docs/gha-workflow.md +326 -0
  211. package/scripts/lib/docs/inline-template-links.md +303 -0
  212. package/scripts/lib/docs/list-rule-ids.md +156 -0
  213. package/scripts/lib/docs/load-cursor-config.md +147 -0
  214. package/scripts/lib/docs/mirror-parity.md +167 -0
  215. package/scripts/lib/worktree.mjs +26 -0
  216. package/scripts/worktree-cli.mjs +12 -2
  217. package/skills/coverage-fix/SKILL.md +34 -45
  218. package/skills/docgen/SKILL.md +44 -23
  219. package/skills/docgen/bench/etalon/firebase_hosting.md +19 -0
  220. package/skills/docgen/bench/etalon/k8s-tree.md +24 -0
  221. package/skills/docgen/bench/etalon/overlay-paths.md +24 -0
  222. package/skills/docgen/js/docgen-ignore.mjs +54 -0
  223. package/skills/docgen/js/docgen-scan.mjs +37 -21
  224. package/skills/llm-patch/SKILL.md +23 -2
  225. package/skills/start-check/SKILL.md +26 -53
  226. package/skills/start-check/js/check.mjs +211 -0
  227. package/skills/taze/SKILL.md +9 -3
  228. package/skills/taze/js/diff.mjs +154 -0
  229. package/types/bin/n-cursor.d.ts +1 -1
  230. package/skills/fix-tests/SKILL.md +0 -119
  231. package/skills/fix-tests/meta.json +0 -1
@@ -0,0 +1,127 @@
1
+ # fix.mjs — entry-point правила `vue`
2
+
3
+ ## Огляд
4
+
5
+ Файл `npm/rules/vue/fix.mjs` — це **точка входу** (entry-point) для правила `vue` у системі `@nitra/cursor`. Файл одночасно виконує дві ролі:
6
+
7
+ 1. **Library mode** — експортує функцію `run(ctx)`, яку викликає зовнішній оркестратор (CLI `@nitra/cursor`, інші правила, тести) з підготовленим контекстом прогону.
8
+ 2. **Standalone mode** — якщо файл запущено напряму (наприклад, `bun npm/rules/vue/fix.mjs`), він самостійно піднімає повноцінний CLI-цикл, еквівалентний `npx @nitra/cursor fix vue`.
9
+
10
+ Сам файл не містить специфічної для Vue логіки правил. Уся логіка делегується у спільні бібліотеки `runStandardRule` (виконання стандартних фаз правила) і `runRuleCli` (підготовка CLI-середовища). Завдяки цьому `fix.mjs` у директорії правила `vue` залишається тонким shim-файлом, який лише підставляє свою директорію як ідентифікатор правила.
11
+
12
+ Стандартний пайплайн, який під цим запускається (через `runStandardRule`), складається з фаз:
13
+
14
+ - `applies` — детекція файлів/області застосування правила,
15
+ - `JS-concerns` — перевірки JS/JSX/TS-аспектів коду,
16
+ - `policy` — застосування policy-чеків (warn/error),
17
+ - `mdc-refs` — узгодженість MDC-документації та посилань.
18
+
19
+ ## Експорти / API
20
+
21
+ | Назва | Тип | Опис |
22
+ | ----- | ---------- | ----------------------------------------------------------------------------------------------- |
23
+ | `run` | `function` | Named export. Library-точка входу правила. Приймає опційний `ctx` і повертає `Promise<number>`. |
24
+
25
+ **Side-effect експорт:** при виконанні модуля під CLI (умова `isRunAsCli(import.meta.url)`) модуль вмикає standalone-режим і завершує процес викликом `process.exit(...)` з exit-кодом, отриманим від `runRuleCli`. Цей побічний ефект виконується на верхньому рівні модуля (top-level `await`), тому імпорт файлу як ESM-модуля з іншого файлу **не** активує standalone-гілку — її активує лише пряме виконання інтерпретатором.
26
+
27
+ ## Функції
28
+
29
+ ### `run(ctx)`
30
+
31
+ Library-функція правила. Делегує виконання у спільну реалізацію `runStandardRule`, передаючи їй власну директорію модуля (`import.meta.dirname`) як ідентифікатор правила та переданий контекст.
32
+
33
+ - **Сигнатура:** `function run(ctx): Promise<number>`
34
+ - **Параметри:**
35
+ - `ctx` _(optional)_ — об'єкт типу `RuleContext` із модуля `../../scripts/lib/run-standard-rule.mjs`. У ньому, серед іншого, передається `walkCache` для уникнення повторного обходу файлової системи між послідовно запущеними правилами. Якщо `ctx` не передано, `runStandardRule` створює його самостійно.
36
+ - **Повертає:** `Promise<number>` — exit-код прогону правила:
37
+ - `0` — порушень немає (OK);
38
+ - `1` — знайдено порушення (rule violations).
39
+ - **Side effects:** усі побічні ефекти ховаються всередині `runStandardRule` (читання файлів проєкту, можливі автоматичні фікси, виведення summary в stdout/stderr). Сам `run` додаткових side-effects не має, поза тим, що значення повернення завжди є `Promise`, навіть якщо `runStandardRule` поверне синхронно — це гарантує контракт async-pipeline для оркестратора.
40
+ - **Помилки:** функція не обгортає виняткові ситуації — будь-яка помилка з `runStandardRule` пробрасується нагору (rejection промісу) і має оброблятись на рівні CLI/орхестратора.
41
+
42
+ ### Top-level standalone-блок
43
+
44
+ Не функція, а top-level гілка модуля:
45
+
46
+ ```js
47
+ if (isRunAsCli(import.meta.url)) {
48
+ process.exit(await runRuleCli(import.meta.dirname))
49
+ }
50
+ ```
51
+
52
+ - **Семантика:** `isRunAsCli(import.meta.url)` повертає `true`, коли поточний модуль є entry-point процесу (тобто Node/Bun запустив саме цей файл, а не імпортував його). У такому разі викликається `runRuleCli` з директорією поточного правила; її exit-код передається у `process.exit(...)`, щоб CI/IDE отримали коректний статус.
53
+ - **Side effects:**
54
+ - синхронне завершення процесу через `process.exit`;
55
+ - усе, що робить `runRuleCli` (завантаження конфігу, whitelist, summary).
56
+ - **ESLint-винятки:** standalone-entry-point має право робити `process.exit`, тому над цим викликом стоять директиви `// eslint-disable-next-line n/no-process-exit, unicorn/no-process-exit`. Це штатна практика для двороле́вих fix.mjs.
57
+
58
+ ## Залежності
59
+
60
+ Імпорти модуля:
61
+
62
+ - `isRunAsCli` — з `../../scripts/lib/run-rule-cli.mjs`. Утиліта-детектор: чи поточний модуль є entry-point (CLI mode), чи його імпортнули як бібліотеку.
63
+ - `runRuleCli` — з `../../scripts/lib/run-rule-cli.mjs`. Виконує повну CLI-оркестрацію для одного правила: завантажує конфіг, формує whitelist, друкує summary та повертає exit-код. Використовується тільки у standalone-гілці.
64
+ - `runStandardRule` — з `../../scripts/lib/run-standard-rule.mjs`. Виконує стандартний пайплайн правила: `applies → JS-concerns → policy → mdc-refs`. JSDoc-типи `RuleContext` теж приходять із цього модуля (через `@param`-import).
65
+
66
+ Неявні залежності:
67
+
68
+ - Структура директорії правила (`npm/rules/vue/`) має містити очікувані `runStandardRule` під-директорії/файли: `js/`, `lib/`, `policy/`, `meta.json`, `vue.mdc` тощо. Сам `fix.mjs` не звертається до них напряму — він лише підставляє свій `import.meta.dirname`, а решту виявляє shared-runner.
69
+ - ESM-середовище (Node.js ≥ 20 / Bun) з підтримкою `import.meta.url` та `import.meta.dirname`.
70
+
71
+ ## Потік виконання / Використання
72
+
73
+ ### Library mode (виклик з оркестратора)
74
+
75
+ ```js
76
+ import { run } from './npm/rules/vue/fix.mjs'
77
+
78
+ const exitCode = await run(ctx) // ctx — спільний RuleContext із walkCache
79
+ if (exitCode !== 0) {
80
+ // у правилі vue знайдено порушення → обробляємо
81
+ }
82
+ ```
83
+
84
+ Послідовність:
85
+
86
+ 1. Оркестратор імпортує `fix.mjs`. Гілка `if (isRunAsCli(...))` не виконується, бо `import.meta.url` не є entry-point.
87
+ 2. Оркестратор викликає `run(ctx)`.
88
+ 3. `run` передає `import.meta.dirname` (= `…/npm/rules/vue`) і `ctx` у `runStandardRule`.
89
+ 4. `runStandardRule` виконує фази `applies → JS-concerns → policy → mdc-refs` для правила `vue`, використовуючи кеші з `ctx` (наприклад, `walkCache`).
90
+ 5. Повертається exit-код (`0` або `1`), який оркестратор інтегрує у свій агрегований підсумок.
91
+
92
+ ### Standalone mode (пряме виконання)
93
+
94
+ ```bash
95
+ bun npm/rules/vue/fix.mjs
96
+ # або
97
+ node npm/rules/vue/fix.mjs
98
+ ```
99
+
100
+ Послідовність:
101
+
102
+ 1. Інтерпретатор завантажує модуль; виконуються імпорти.
103
+ 2. Виконується top-level код: `isRunAsCli(import.meta.url)` повертає `true`.
104
+ 3. Викликається `await runRuleCli(import.meta.dirname)` — це повний еквівалент `npx @nitra/cursor fix vue`:
105
+ - читає конфіг,
106
+ - застосовує whitelist,
107
+ - усередині піднімає той самий `runStandardRule`-цикл (або сумісний з ним),
108
+ - друкує summary.
109
+ 4. Отриманий exit-код передається у `process.exit(...)`, процес завершується з відповідним кодом для CI/IDE.
110
+
111
+ ### Чому існують дві ролі
112
+
113
+ Подвійна реалізація (`run` + standalone) дозволяє:
114
+
115
+ - викликати правило **разом з іншими** з єдиного оркестратора без зайвого spawn-у процесів і без втрати спільних кешів (бібліотечний шлях);
116
+ - запускати правило **окремо** з командного рядка під час локальної розробки/дебагу, маючи повноцінний CLI-вивід (standalone-шлях).
117
+
118
+ ## Rebuild Test
119
+
120
+ Можна повністю відтворити поведінку цього файлу з опису вище:
121
+
122
+ - Імпортуються тільки два символи з двох сусідніх бібліотек: `isRunAsCli`, `runRuleCli` з `../../scripts/lib/run-rule-cli.mjs` та `runStandardRule` з `../../scripts/lib/run-standard-rule.mjs`.
123
+ - Експортується одна named-функція `run(ctx)`, яка повертає результат `runStandardRule(import.meta.dirname, ctx)` — отже, є async (повертає `Promise<number>`).
124
+ - На рівні модуля стоїть умова `if (isRunAsCli(import.meta.url)) { process.exit(await runRuleCli(import.meta.dirname)) }` з відповідними ESLint-disable-коментарями для `n/no-process-exit` та `unicorn/no-process-exit`.
125
+ - Жодних додаткових іменованих експортів, default-експортів, констант чи побічних викликів у файлі немає.
126
+
127
+ Цього достатньо, щоб переписати файл байт-у-байт еквівалентно (з точністю до коментарів/форматування), не звертаючись до оригіналу.
@@ -0,0 +1,335 @@
1
+ # packages.mjs
2
+
3
+ ## Огляд
4
+
5
+ Модуль `packages.mjs` реалізує перевірку відповідності Vue-пакетів monorepo правилам `vue.mdc`. Він обходить усі workspace-пакети, знаходить серед них ті, що мають `vue` у `dependencies`, і виконує над кожним набір контрольних кроків:
6
+
7
+ - наявність та коректність `src/vite-env.d.ts` із triple-slash-посиланням на `vite/client`;
8
+ - наявність `jsconfig.json` у корені пакета;
9
+ - наявність та коректне конфігурування `vite.config.{js,ts,mjs}` (зокрема `VueMacros`, `AutoImport` і Bun-сумісність);
10
+ - наявність `'vue'` у списку `imports` плагіна `unplugin-auto-import` (`AutoImport`);
11
+ - відсутність заборонених явних value-імпортів з `'vue'` у джерелах пакета (скан через oxc-parser у сусідньому модулі);
12
+ - відсутність імпортів Node-нативних модулів (`node:*` або bare-ім’я кшталту `fs`, `path`) у `.vue` SFC;
13
+ - відсутність згадок `esbuild` у джерелах пакета (заохочується перехід на `rolldown`);
14
+ - наявність рекомендації розширення `Vue.volar` у `.vscode/extensions.json` на рівні всього репозиторію.
15
+
16
+ Перевірка залежностей `package.json` (`vite >= 8`, `@vitejs/plugin-vue`, `vue-macros`, `unplugin-auto-import`, `vite-plugin-vue-layouts-next`, заборона `esbuild`) виноситься у policy `vue.package_json` і викликається через CLI `npx @nitra/cursor fix`; цей модуль лише друкує підказку про це.
17
+
18
+ Результати збираються через `createCheckReporter()` і повертаються у вигляді exit-коду: `0` — все OK, `1` — є проблеми. Основна публічна точка входу — функція `check(cwd)`.
19
+
20
+ ## Експорти / API
21
+
22
+ Модуль є ES-модулем (`*.mjs`) і має такі іменовані експорти:
23
+
24
+ - `isVueComponentLibraryPkg(pkg)` — predicate, що визначає, чи є пакет бібліотекою компонентів Vue (за наявністю `vue` у `peerDependencies`).
25
+ - `check(cwd?)` — головна функція перевірки усього репозиторію; повертає `Promise<number>` із exit-кодом.
26
+
27
+ Решта функцій модуля — приватні (file-local) helpers, які використовуються лише всередині `packages.mjs`.
28
+
29
+ ## Функції
30
+
31
+ ### `isEsbuildScanFile(relPosix)`
32
+
33
+ - **Сигнатура:** `function isEsbuildScanFile(relPosix: string): boolean`
34
+ - **Параметри:**
35
+ - `relPosix` — відносний шлях у POSIX-форматі (з прямими слешами), відраховуваний від кореня пакета.
36
+ - **Повертає:** `boolean` — `true`, якщо файл варто перевіряти на текстові згадки `esbuild`, інакше `false`.
37
+ - **Логіка:** виключає типові службові каталоги (`node_modules/`, `dist/`, `build/`, `coverage/`, `.git/`), типові lock-файли (`bun.lock`, `bun.lockb`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`), і допускає лише розширення з білого списку: `.js`, `.mjs`, `.cjs`, `.ts`, `.tsx`, `.vue`, `.json`, `.jsonc`, `.yaml`, `.yml`, `.md`, `.mdc`.
38
+ - **Side effects:** немає.
39
+
40
+ ### `appendEsbuildLineMatches(rel, content, matches, maxMatches)`
41
+
42
+ - **Сигнатура:** `function appendEsbuildLineMatches(rel: string, content: string, matches: { rel: string; line: number; snippet: string }[], maxMatches: number): void`
43
+ - **Параметри:**
44
+ - `rel` — відносний шлях до файлу (для повідомлень).
45
+ - `content` — повний вміст файлу як рядок.
46
+ - `matches` — мутабельний буфер, у який дописуються знайдені збіги.
47
+ - `maxMatches` — верхня межа кількості елементів у `matches`; при досягненні функція припиняє обробку.
48
+ - **Повертає:** `void`. Результат повертається через мутацію аргументу `matches`.
49
+ - **Логіка:** розбиває `content` на рядки, перевіряє регулярний вираз `\besbuild\b` і додає об’єкт `{ rel, line: i + 1, snippet: line.trim() }` для кожного збігу.
50
+ - **Side effects:** мутує переданий масив `matches`.
51
+
52
+ ### `collectEsbuildMatchesInFiles(absPackageRoot, files, maxMatches)`
53
+
54
+ - **Сигнатура:** `async function collectEsbuildMatchesInFiles(absPackageRoot: string, files: { rel: string }[], maxMatches: number): Promise<{ rel: string; line: number; snippet: string }[]>`
55
+ - **Параметри:**
56
+ - `absPackageRoot` — абсолютний шлях до кореня пакета.
57
+ - `files` — перелік відносних шляхів-кандидатів, відфільтрованих через `isEsbuildScanFile`.
58
+ - `maxMatches` — максимальна кількість збігів, які треба зібрати.
59
+ - **Повертає:** `Promise` зі списком знайдених збігів `{ rel, line, snippet }`.
60
+ - **Логіка:** для кожного файлу читає вміст через `fs/promises.readFile(..., 'utf8')`; якщо у файлі немає згадки `esbuild`, переходить далі. Інакше викликає `appendEsbuildLineMatches`. Зупиняється, щойно `matches.length` досягає `maxMatches`.
61
+ - **Side effects:** виконує читання файлів з диска.
62
+
63
+ ### `checkEsbuildMentions(rootDir, absPackageRoot, ignorePaths, prefix, passFn, fail)`
64
+
65
+ - **Сигнатура:** `async function checkEsbuildMentions(rootDir: string, absPackageRoot: string, ignorePaths: string[], prefix: string, passFn: (msg: string) => void, fail: (msg: string) => void): Promise<void>`
66
+ - **Параметри:**
67
+ - `rootDir` — відносний шлях до пакета (для повідомлень).
68
+ - `absPackageRoot` — абсолютний шлях до кореня пакета (для обходу).
69
+ - `ignorePaths` — масив абсолютних шляхів каталогів, які повністю виключені з обходу.
70
+ - `prefix` — текстовий префікс для повідомлень (`[<label>] `).
71
+ - `passFn` — callback на успішне повідомлення (як у check-reporter).
72
+ - `fail` — callback на помилку перевірки.
73
+ - **Повертає:** `Promise<void>`.
74
+ - **Логіка:** обходить дерево через `walkDir(absPackageRoot, visitor, ignorePaths)`; кожен файл прогоняється через `isEsbuildScanFile`; кандидати збираються у локальний масив. Далі викликає `collectEsbuildMatchesInFiles` з лімітом `maxMatches = 30`. Якщо збігів немає — друкує pass із підказкою «очікується `rolldown`». Якщо є — реєструє `fail` на кожен збіг з підказкою замінити на `rolldown` та (якщо ліміт вичерпано) додає ще один `fail`, що показано лише перші 30.
75
+ - **Side effects:** виконує читання файлів з диска; викликає `passFn`/`fail`.
76
+
77
+ ### `packageLabel(rootDir)`
78
+
79
+ - **Сигнатура:** `function packageLabel(rootDir: string): string`
80
+ - **Параметри:**
81
+ - `rootDir` — відносний шлях до пакета (`'.'` для кореня monorepo або, наприклад, `site`).
82
+ - **Повертає:** `string` — підпис для логів: `'корінь'` якщо `rootDir === '.'`, інакше сам `rootDir`.
83
+ - **Side effects:** немає.
84
+
85
+ ### `ukFilesCountPhrase(n)`
86
+
87
+ - **Сигнатура:** `function ukFilesCountPhrase(n: number): string`
88
+ - **Параметри:**
89
+ - `n` — невід’ємна кількість файлів.
90
+ - **Повертає:** фразу українською мовою з відмінком «файл» / «файли» / «файлів» відповідно до правил pluralization:
91
+ - залишок від 100 у діапазоні 11..14 → `«N файлів»`;
92
+ - залишок від 10 рівний 1 → `«N файл»`;
93
+ - залишок від 10 у 2..4 → `«N файли»`;
94
+ - решта → `«N файлів»`.
95
+ - **Side effects:** немає.
96
+
97
+ ### `checkViteClientEnvAndEditorConfig(rootDir, prefix, passFn, fail, cwd)`
98
+
99
+ - **Сигнатура:** `async function checkViteClientEnvAndEditorConfig(rootDir: string, prefix: string, passFn: (msg: string) => void, fail: (msg: string) => void, cwd: string): Promise<void>`
100
+ - **Параметри:**
101
+ - `rootDir` — відносний шлях до кореня пакета.
102
+ - `prefix` — префікс повідомлень.
103
+ - `passFn` / `fail` — callbacks check-reporter-а.
104
+ - `cwd` — корінь репозиторію.
105
+ - **Повертає:** `Promise<void>`.
106
+ - **Логіка:**
107
+ 1. Перевіряє, що `<cwd>/<rootDir>/src/vite-env.d.ts` існує; якщо ні — `fail` з підказкою про `/// <reference types="vite/client" />`.
108
+ 2. Якщо файл існує, читає його вміст і перевіряє регулярним виразом `VITE_CLIENT_REFERENCE_RE = /\/\/\/\s*<reference\s+types\s*=\s*["']vite\/client["']\s*\/>/`. За відсутності збігу — `fail`.
109
+ 3. Якщо обидва кроки успішні — `passFn` про коректний `vite-env.d.ts`.
110
+ 4. Перевіряє наявність `<cwd>/<rootDir>/jsconfig.json`; за відсутності — `fail`, інакше `passFn`.
111
+ - **Side effects:** читання файлів з диска; виклики `passFn`/`fail`.
112
+
113
+ ### `isVueComponentLibraryPkg(pkg)` _(експорт)_
114
+
115
+ - **Сигнатура:** `function isVueComponentLibraryPkg(pkg: { peerDependencies?: Record<string, string> }): boolean`
116
+ - **Параметри:**
117
+ - `pkg` — розпарсений `package.json` пакета.
118
+ - **Повертає:** `boolean` — `true`, якщо `vue` присутній у `peerDependencies`.
119
+ - **Семантика:** такі пакети — бібліотеки компонентів Vue. Їхні джерела не проходять через `unplugin-auto-import` споживача (auto-import резолвиться лише в коді додатка, не в `node_modules`), тому правило «без явних value-імпортів з `'vue'`» до них не застосовується.
120
+ - **Side effects:** немає.
121
+
122
+ ### `extractAutoImportCallArgs(content)`
123
+
124
+ - **Сигнатура:** `function extractAutoImportCallArgs(content: string): string | null`
125
+ - **Параметри:**
126
+ - `content` — повний текст `vite.config.*`.
127
+ - **Повертає:** текст усередині найближчого виклику `AutoImport(...)` без зовнішніх дужок, або `null`, якщо виклик не знайдено або дужки не збалансовані.
128
+ - **Логіка:** шукає marker `AutoImport(`, після нього просувається посимвольно з лічильником глибини дужок (`(` → `+1`, `)` → `-1`) і повертає підрядок від першого символу після `(` до символу, на якому `depth` стає `0`.
129
+ - **Side effects:** немає.
130
+
131
+ ### `viteConfigHasVueInAutoImports(content)`
132
+
133
+ - **Сигнатура:** `function viteConfigHasVueInAutoImports(content: string): boolean`
134
+ - **Параметри:**
135
+ - `content` — повний текст `vite.config.*`.
136
+ - **Повертає:** `boolean` — `true`, якщо у `AutoImport(...)` як рядковий елемент `imports` фігурує `'vue'` або `"vue"`.
137
+ - **Логіка:** делегує до `extractAutoImportCallArgs`; якщо `null` — `false`; інакше повертає `args.includes("'vue'") || args.includes('"vue"')`. Зауважте: перевірка є текстовим contains, без парсингу JS — точкою обʼєктивізації пошуку є вже відокремлений виклик `AutoImport(...)`.
138
+ - **Side effects:** немає.
139
+
140
+ ### `checkViteConfig(rootDir, isComponentLibrary, prefix, passFn, fail, cwd)`
141
+
142
+ - **Сигнатура:** `async function checkViteConfig(rootDir: string, isComponentLibrary: boolean, prefix: string, passFn: (msg: string) => void, fail: (msg: string) => void, cwd: string): Promise<{ hasVueAutoImport: boolean }>`
143
+ - **Параметри:**
144
+ - `rootDir` — відносний шлях до пакета.
145
+ - `isComponentLibrary` — чи це бібліотека компонентів Vue.
146
+ - `prefix`, `passFn`, `fail` — як вище.
147
+ - `cwd` — корінь репозиторію.
148
+ - **Повертає:** `Promise<{ hasVueAutoImport: boolean }>` — ознака, чи AutoImport сконфігуровано на `'vue'`. Її використовує `checkVueImportViolations`.
149
+ - **Логіка:**
150
+ 1. Шукає перший наявний з `vite.config.js`, `vite.config.ts`, `vite.config.mjs`; якщо жоден не існує — `fail` і повертає `{ hasVueAutoImport: false }`.
151
+ 2. Читає вміст vite.config.
152
+ 3. Якщо вміст містить `esbuild` (за регуляркою `ESBUILD_RE`) — `fail` з підказкою замінити на `rolldown`.
153
+ 4. Викликає `viteConfigHasVueInAutoImports(content)` → `hasVueAutoImport`.
154
+ 5. Якщо це бібліотека компонентів — `passFn`, що `VueMacros`/`AutoImport` не вимагаються.
155
+ 6. Інакше — пробігає список перевірок `[ VueMacros, AutoImport ]` і реєструє `passFn`/`fail` за наявністю токенів у вмісті. Якщо у файлі є виклик `AutoImport(`, додатково перевіряє `hasVueAutoImport`: `pass` — якщо `'vue'` у `imports`, інакше `fail` з підказкою додати `'vue'` (бо інакше прибирати явні value-імпорти `from 'vue'` небезпечно).
156
+ 7. Незалежно від типу пакета, якщо у vite.config фігурує `process.env.npm_lifecycle_event` — `fail` з підказкою перейти на `mode` з `defineConfig(({ mode }) => ...)` (Bun не підставляє `npm_lifecycle_event` так, як npm).
157
+ - **Side effects:** читання `vite.config.*` з диска; виклики `passFn`/`fail`.
158
+
159
+ ### `checkVueNodeImportViolations(rootDir, absPackageRoot, ignorePaths, prefix, passFn, fail)`
160
+
161
+ - **Сигнатура:** `async function checkVueNodeImportViolations(rootDir: string, absPackageRoot: string, ignorePaths: string[], prefix: string, passFn: (msg: string) => void, fail: (msg: string) => void): Promise<void>`
162
+ - **Параметри:** як в інших чек-функцій; `absPackageRoot` — абсолютний шлях до пакета.
163
+ - **Повертає:** `Promise<void>`.
164
+ - **Логіка:**
165
+ 1. Обходить `absPackageRoot` через `walkDir(absPackageRoot, visitor, ignorePaths)`, збирає абсолютні шляхи всіх `.vue`-файлів, що не пропускаються через `shouldSkipFileForVueImportScan(rel)`.
166
+ 2. Для кожного `.vue` читає вміст і викликає `findForbiddenNodeImportsInVueFile(content, rel)`; на кожне порушення — `fail` з підказкою винести логіку у server-side утіліту (SFC виконується у браузері, Node API недоступне).
167
+ 3. Якщо порушень немає — `passFn` з фразою «`немає імпортів Node-нативних модулів у .vue (проскановано N файлів)`», де `N` форматується через `ukFilesCountPhrase`.
168
+ - **Side effects:** обхід та читання файлів з диска; виклики `passFn`/`fail`.
169
+
170
+ ### `checkVueImportViolations(rootDir, absPackageRoot, ignorePaths, isComponentLibrary, hasVueAutoImport, prefix, passFn, fail)`
171
+
172
+ - **Сигнатура:** `async function checkVueImportViolations(rootDir: string, absPackageRoot: string, ignorePaths: string[], isComponentLibrary: boolean, hasVueAutoImport: boolean, prefix: string, passFn: (msg: string) => void, fail: (msg: string) => void): Promise<void>`
173
+ - **Параметри:**
174
+ - `rootDir` — відносний шлях до пакета.
175
+ - `absPackageRoot` — абсолютний шлях до кореня пакета.
176
+ - `ignorePaths` — каталоги, повністю виключені з обходу.
177
+ - `isComponentLibrary` — чи це бібліотека компонентів (`vue` у `peerDependencies`).
178
+ - `hasVueAutoImport` — чи AutoImport сконфігуровано на `'vue'` (з `checkViteConfig`).
179
+ - `prefix`, `passFn`, `fail` — як вище.
180
+ - **Повертає:** `Promise<void>`.
181
+ - **Логіка:**
182
+ 1. Якщо `isComponentLibrary === true` — `passFn` із поясненням, що для бібліотек компонентів явні value-імпорти з `'vue'` дозволені, і повертається. Джерела бібліотеки не проходять через `unplugin-auto-import` споживача.
183
+ 2. Інакше якщо `hasVueAutoImport === false` — `passFn` з підказкою спершу додати `'vue'` до `AutoImport.imports` (fail про це вже зареєстровано в `checkViteConfig`), і повертається.
184
+ 3. Інакше обходить пакет через `walkDir`, збирає абсолютні шляхи всіх source-файлів, для яких `isVueImportScanSourceFile(rel) === true` і `shouldSkipFileForVueImportScan(rel) === false`.
185
+ 4. Для кожного такого файлу читає вміст і викликає `findForbiddenVueImportsInSourceFile(content, rel)`; на кожне порушення — `fail` з підказкою прибрати явний value-імпорт з `'vue'`.
186
+ 5. Якщо порушень не знайдено — `passFn` з фразою «немає заборонених value-імпортів з `'vue'` у джерелах (проскановано N файлів)».
187
+ - **Side effects:** обхід та читання файлів з диска; виклики `passFn`/`fail`.
188
+
189
+ ### `checkVuePackage(rootDir, isComponentLibrary, ignorePaths, fail, passFn, cwd)`
190
+
191
+ - **Сигнатура:** `async function checkVuePackage(rootDir: string, isComponentLibrary: boolean, ignorePaths: string[], fail: (msg: string) => void, passFn: (msg: string) => void, cwd: string): Promise<void>`
192
+ - **Параметри:**
193
+ - `rootDir` — відносний шлях до пакета.
194
+ - `isComponentLibrary` — чи це бібліотека компонентів Vue.
195
+ - `ignorePaths` — каталоги, повністю виключені з обходу.
196
+ - `fail`, `passFn` — callbacks check-reporter-а (порядок `fail` перед `passFn` тут зворотний до інших функцій).
197
+ - `cwd` — корінь репозиторію.
198
+ - **Повертає:** `Promise<void>` — завершується після усього набору перевірок пакета.
199
+ - **Логіка послідовно виконує:**
200
+ 1. Формує `prefix = "[" + packageLabel(rootDir) + "] "`.
201
+ 2. Реєструє інформаційний `passFn`: «`package.json` залежності перевіряє `npx @nitra/cursor fix → vue.package_json`».
202
+ 3. `checkViteClientEnvAndEditorConfig(rootDir, prefix, passFn, fail, cwd)`.
203
+ 4. `checkViteConfig(rootDir, isComponentLibrary, prefix, passFn, fail, cwd)` → отримує `hasVueAutoImport`.
204
+ 5. `checkVueImportViolations(rootDir, join(cwd, rootDir), ignorePaths, isComponentLibrary, hasVueAutoImport, prefix, passFn, fail)`.
205
+ 6. `checkVueNodeImportViolations(rootDir, join(cwd, rootDir), ignorePaths, prefix, passFn, fail)`.
206
+ 7. `checkEsbuildMentions(rootDir, join(cwd, rootDir), ignorePaths, prefix, passFn, fail)`.
207
+ - **Side effects:** виконує всі вкладені перевірки (читання файлів, виклики `passFn`/`fail`).
208
+
209
+ ### `collectVueRoots(roots, cwd)`
210
+
211
+ - **Сигнатура:** `async function collectVueRoots(roots: string[], cwd: string): Promise<Array<{ rootDir: string, isComponentLibrary: boolean }>>`
212
+ - **Параметри:**
213
+ - `roots` — усі корені пакетів monorepo (відносні шляхи), отримані від `getMonorepoPackageRootDirs`.
214
+ - `cwd` — корінь репозиторію.
215
+ - **Повертає:** масив описів пакетів, у яких `vue` зазначений у `dependencies`, з ознакою `isComponentLibrary`.
216
+ - **Логіка:**
217
+ 1. Для кожного `r` будує абсолютний шлях `<cwd>/<r>/package.json` і пропускає його, якщо файл відсутній.
218
+ 2. Парсить JSON-вміст `package.json`.
219
+ 3. Якщо `pkg.dependencies?.vue` істина — додає до результату об’єкт `{ rootDir: r, isComponentLibrary: isVueComponentLibraryPkg(pkg) }`.
220
+ 4. Пакети, у яких `vue` лише в `peerDependencies` (без `dependencies`), не додаються — це самостійні бібліотеки компонентів, до них app-перевірки не застосовуються.
221
+ - **Side effects:** читання `package.json` файлів з диска.
222
+
223
+ ### `checkVueVolarRecommendation(pass, fail, cwd)`
224
+
225
+ - **Сигнатура:** `async function checkVueVolarRecommendation(pass: (msg: string) => void, fail: (msg: string) => void, cwd: string): Promise<void>`
226
+ - **Параметри:**
227
+ - `pass`, `fail` — callbacks check-reporter-а.
228
+ - `cwd` — корінь репозиторію.
229
+ - **Повертає:** `Promise<void>`.
230
+ - **Логіка:**
231
+ 1. Перевіряє наявність `<cwd>/.vscode/extensions.json`; за відсутності — `fail` з поясненням, що для Vue-проєкту потрібна рекомендація `Vue.volar`.
232
+ 2. Парсить JSON; якщо `recommendations` містить `'Vue.volar'` — `pass`, інакше — `fail` з підказкою додати рекомендацію.
233
+ - **Side effects:** читання `extensions.json` з диска; виклики `pass`/`fail`.
234
+
235
+ ### `check(cwd)` _(експорт, точка входу)_
236
+
237
+ - **Сигнатура:** `async function check(cwd: string = process.cwd()): Promise<number>`
238
+ - **Параметри:**
239
+ - `cwd` — корінь репозиторію; за замовчуванням `process.cwd()`.
240
+ - **Повертає:** `Promise<number>` — `0`, якщо всі перевірки успішні; `1`, якщо є зареєстровані `fail`.
241
+ - **Логіка:**
242
+ 1. Створює `reporter = createCheckReporter()` і деструктурує `{ pass, fail }`.
243
+ 2. Отримує корені всіх workspace-пакетів через `getMonorepoPackageRootDirs(cwd)`.
244
+ 3. Викликає `collectVueRoots(roots, cwd)` → список Vue-пакетів.
245
+ 4. Якщо список порожній — друкує два `pass`-повідомлення (про пропуск Volar і про відсутність `vue` у будь-яких `dependencies`) і одразу повертає `reporter.getExitCode()`.
246
+ 5. Інакше викликає `checkVueVolarRecommendation(pass, fail, cwd)`.
247
+ 6. Завантажує абсолютні `ignorePaths` через `loadCursorIgnorePaths(cwd)` (читає `.cursorignore`-подібну конфігурацію).
248
+ 7. Для кожного `{ rootDir, isComponentLibrary }` із `vueRoots` викликає `checkVuePackage(rootDir, isComponentLibrary, ignorePaths, fail, pass, cwd)`.
249
+ 8. Повертає `reporter.getExitCode()`.
250
+ - **Side effects:** усе разом — повний обхід monorepo, читання багатьох файлів з диска, накопичення pass/fail у check-reporter-і.
251
+
252
+ ## Залежності
253
+
254
+ ### Імпорти зі стандартної бібліотеки Node.js
255
+
256
+ - `node:fs` — `existsSync` (синхронна перевірка існування файлу).
257
+ - `node:fs/promises` — `readFile` (асинхронне читання файлу як `utf8`).
258
+ - `node:path` — `join`, `relative` (компонування і обчислення відносних шляхів).
259
+
260
+ ### Внутрішні модулі проєкту
261
+
262
+ - `../../../scripts/lib/check-reporter.mjs` — `createCheckReporter()`: створює reporter з API `{ pass, fail, getExitCode }`, який накопичує повідомлення перевірки та підраховує підсумковий exit-код.
263
+ - `../lib/vue-forbidden-imports.mjs` — функції сканування заборонених імпортів:
264
+ - `findForbiddenNodeImportsInVueFile(content, rel)` — повертає список порушень із Node-імпортами у `.vue` SFC.
265
+ - `findForbiddenVueImportsInSourceFile(content, rel)` — повертає список явних value-імпортів з `'vue'` у звичайних source-файлах.
266
+ - `isVueImportScanSourceFile(rel)` — чи варто файл взагалі сканувати на value-імпорти з `'vue'` (за розширенням/типом).
267
+ - `shouldSkipFileForVueImportScan(rel)` — predicate-фільтр (`node_modules/`, `dist/`, lock-файли тощо).
268
+ - `../../../scripts/lib/load-cursor-config.mjs` — `loadCursorIgnorePaths(cwd)`: повертає масив абсолютних шляхів каталогів, які повністю виключаються з обходу.
269
+ - `../../../scripts/utils/walkDir.mjs` — `walkDir(absRoot, visitor, ignorePaths)`: рекурсивний обхід дерева; visitor викликається з абсолютним шляхом кожного файлу.
270
+ - `../../../scripts/lib/workspaces.mjs` — `getMonorepoPackageRootDirs(cwd)`: повертає відносні шляхи всіх workspace-пакетів monorepo (включно з коренем `'.'`, якщо він є пакетом).
271
+
272
+ ### Зовнішні (вказані у документації, не у коді цього модуля)
273
+
274
+ - `oxc-parser` — використовується транзитивно через `../lib/vue-forbidden-imports.mjs` для парсингу і аналізу `module.staticImports`. У самому `packages.mjs` прямого виклику парсера немає.
275
+ - Vue/Vite-стек, для якого писано перевірки: `vite >= 8`, `@vitejs/plugin-vue`, `vue-macros`, `unplugin-auto-import`, `vite-plugin-vue-layouts-next`. Заборонено: `esbuild` (треба використовувати `rolldown`).
276
+
277
+ ### Константи
278
+
279
+ - `ESBUILD_RE = /\besbuild\b/` — регулярний вираз для пошуку слова `esbuild` як цілого ідентифікатора.
280
+ - `VITE_CLIENT_REFERENCE_RE = /\/\/\/\s*<reference\s+types\s*=\s*["']vite\/client["']\s*\/>/` — triple-slash-директива для `vite/client` у `src/vite-env.d.ts`.
281
+
282
+ ## Потік виконання / Використання
283
+
284
+ ### Виклик
285
+
286
+ Модуль експортує `check(cwd)`. Зовнішній runner (CLI `@nitra/cursor` або pipeline-перевірка) викликає його приблизно так:
287
+
288
+ ```js
289
+ import { check } from './npm/rules/vue/js/packages.mjs'
290
+
291
+ const code = await check(process.cwd())
292
+ process.exit(code)
293
+ ```
294
+
295
+ ### Послідовність кроків `check()`
296
+
297
+ 1. Інстанціювання check-reporter-а.
298
+ 2. Отримання списку workspace-пакетів через `getMonorepoPackageRootDirs(cwd)`.
299
+ 3. Фільтрація пакетів через `collectVueRoots(roots, cwd)`: залишаються лише ті, що мають `vue` у `dependencies`. Кожному обчислюється `isComponentLibrary` через `isVueComponentLibraryPkg(pkg)` (за наявністю `vue` у `peerDependencies`).
300
+ 4. Якщо Vue-пакетів немає — друкує два інформаційних `pass`-повідомлення і повертає exit-код reporter-а (`0`).
301
+ 5. Перевіряє `.vscode/extensions.json` на рекомендацію `Vue.volar` (`checkVueVolarRecommendation`).
302
+ 6. Завантажує `ignorePaths` через `loadCursorIgnorePaths(cwd)`.
303
+ 7. Для кожного Vue-пакета послідовно виконує `checkVuePackage`:
304
+ 1. Інформаційний `pass` про `package.json`-перевірку через `npx @nitra/cursor fix → vue.package_json`.
305
+ 2. `checkViteClientEnvAndEditorConfig` — `src/vite-env.d.ts` + `jsconfig.json`.
306
+ 3. `checkViteConfig` — наявність `vite.config.*`, `VueMacros`, `AutoImport`, `'vue'` у `AutoImport.imports`, заборона `process.env.npm_lifecycle_event`, заборона `esbuild` у vite.config.
307
+ 4. `checkVueImportViolations` — скан source-файлів пакета на заборонені value-імпорти з `'vue'` (тільки якщо це не бібліотека компонентів і AutoImport налаштовано на `'vue'`).
308
+ 5. `checkVueNodeImportViolations` — скан `.vue` SFC на заборонені імпорти Node-нативних модулів.
309
+ 6. `checkEsbuildMentions` — текстовий скан усіх scan-сумісних файлів пакета на згадки `esbuild` із підказкою переходу на `rolldown`.
310
+ 8. Повертає `reporter.getExitCode()` — `0` за умови відсутності `fail`, `1` за наявності.
311
+
312
+ ### Особливості/інваріанти
313
+
314
+ - Перевірки vite.config — текстові (`String.prototype.includes` / regex). Винятком є `extractAutoImportCallArgs`, яка точково виокремлює аргументи виклику `AutoImport(...)` за збалансованими дужками, щоб уникнути false-positive поза цим викликом.
315
+ - Перевірка `'vue'` у `AutoImport.imports` свідомо не використовує AST-парсер: вона працює лише після того, як аргументи `AutoImport(...)` уже виокремлено, і обмежується пошуком підрядків `'vue'` або `"vue"`.
316
+ - Скан value-імпортів `'vue'` пропускається, якщо `'vue'` ще не доданий до `AutoImport.imports` — інакше пропозиція прибрати імпорти зламала б код (нікому буде надати `ref`/`createApp` тощо). Користувача спершу штовхають полагодити `vite.config`, і лише наступний прогін викриє самі заборонені імпорти.
317
+ - Бібліотеки компонентів (`vue` у `peerDependencies`) звільнюються від вимог `VueMacros`/`AutoImport` і правила «без явних value-імпортів з `'vue'`».
318
+ - Сканування `.vue` SFC на Node-імпорти виконується завжди, незалежно від типу пакета.
319
+ - Скан `esbuild`-згадок має жорсткий ліміт у 30 збігів; при перевищенні додатково реєструється fail-маркер «показано перші 30 збігів».
320
+ - Обхід `walkDir` керується `ignorePaths` із `.cursor`-конфігу; усередині кожної чек-функції додатково діють фільтри `isEsbuildScanFile` / `isVueImportScanSourceFile` / `shouldSkipFileForVueImportScan`.
321
+
322
+ ### Корисність окремих експортів
323
+
324
+ - `isVueComponentLibraryPkg(pkg)` — корисна як reusable predicate в інших правилах/інструментах, які мають відрізняти бібліотеки компонентів Vue від додатків.
325
+ - `check(cwd)` — основна публічна точка для запуску перевірки. Повертає exit-код, який інтегрується у будь-який runner перевірок.
326
+
327
+ ## Rebuild Test
328
+
329
+ За цим документом має бути можливо однозначно відтворити поведінку `packages.mjs`:
330
+
331
+ - Імпорти, експорти (`isVueComponentLibraryPkg`, `check`) і їхні сигнатури документовані.
332
+ - Усі file-local helper-функції перераховані з параметрами, поверненнями, логікою та side effects.
333
+ - Описано константи (`ESBUILD_RE`, `VITE_CLIENT_REFERENCE_RE`), залежні модулі і їхні ролі.
334
+ - Описано послідовність викликів у `check(cwd)` і `checkVuePackage(...)`.
335
+ - Зафіксовано інваріанти: ліміт у 30 esbuild-збігів, виключення для бібліотек компонентів, пропуск value-імпорт-скану до додавання `'vue'` у `AutoImport.imports`, скан Node-імпортів у `.vue` незалежно від типу пакета, заборона `process.env.npm_lifecycle_event` у vite.config (Bun-сумісність), вимога `mode` з `defineConfig(({ mode }) => ...)`.