@nitra/cursor 12.8.5 → 12.8.7

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 (202) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/bin/n-cursor.js +5 -5
  3. package/package.json +1 -1
  4. package/rules/abie/js/http_route_base.mdc +25 -0
  5. package/rules/abie/js/ua_http_route.mdc +1 -1
  6. package/rules/abie/main.mdc +12 -0
  7. package/rules/adr/js/hooks.mdc +32 -0
  8. package/rules/adr/js/madr_format.mdc +96 -0
  9. package/rules/adr/js/settings_policy.mdc +34 -0
  10. package/rules/adr/main.mdc +13 -95
  11. package/rules/bun/js/bunfig.mdc +12 -0
  12. package/rules/bun/js/layout.mdc +60 -0
  13. package/rules/bun/js/lint.mdc +9 -0
  14. package/rules/bun/js/package_json.mdc +19 -0
  15. package/rules/bun/main.mdc +9 -61
  16. package/rules/capacitor/js/ios_spm.mdc +69 -0
  17. package/rules/capacitor/js/version.mdc +29 -0
  18. package/rules/capacitor/main.mdc +8 -22
  19. package/rules/changelog/js/agent-workflow.mdc +15 -0
  20. package/rules/changelog/js/changelog-format.mdc +33 -0
  21. package/rules/changelog/js/comparison-models.mdc +40 -0
  22. package/rules/changelog/main.mdc +4 -98
  23. package/rules/ci4/js/marksman_config.mdc +31 -0
  24. package/rules/ci4/js/vscode_extensions.mdc +33 -0
  25. package/rules/ci4/main.mdc +14 -14
  26. package/rules/docker/js/compile.mdc +44 -0
  27. package/rules/docker/js/hadolint.mdc +50 -0
  28. package/rules/docker/js/mirror.mdc +13 -0
  29. package/rules/docker/js/multistage.mdc +13 -0
  30. package/rules/docker/js/native-addon.mdc +43 -0
  31. package/rules/docker/js/nginx-tag.mdc +7 -0
  32. package/rules/docker/js/nginx-user.mdc +37 -0
  33. package/rules/docker/js/non-root.mdc +39 -0
  34. package/rules/docker/main.mdc +15 -196
  35. package/rules/ga/js/lint_toolchain.mdc +15 -0
  36. package/rules/ga/js/required_workflows.mdc +35 -0
  37. package/rules/ga/js/vscode.mdc +17 -0
  38. package/rules/ga/js/workflow_common.mdc +108 -0
  39. package/rules/ga/js/workflows.mdc +32 -0
  40. package/rules/ga/js/zizmor.mdc +7 -0
  41. package/rules/ga/main.mdc +17 -125
  42. package/rules/graphql/js/tooling.mdc +13 -0
  43. package/rules/graphql/js/vscode_extensions.mdc +13 -0
  44. package/rules/graphql/main.mdc +3 -22
  45. package/rules/hasura/js/internal_urls.mdc +27 -0
  46. package/rules/hasura/js/migrations.mdc +13 -0
  47. package/rules/hasura/js/svc_hl.mdc +17 -0
  48. package/rules/hasura/main.mdc +8 -30
  49. package/rules/image-avif/js/avif_generation.mdc +26 -0
  50. package/rules/image-avif/js/package_json_optout.mdc +21 -0
  51. package/rules/image-avif/main.mdc +7 -34
  52. package/rules/image-compress/js/package_json.mdc +7 -0
  53. package/rules/image-compress/js/package_setup.mdc +13 -0
  54. package/rules/image-compress/main.mdc +4 -12
  55. package/rules/js/docs/index.md +3 -3
  56. package/rules/js/js/dep-policy.mdc +17 -0
  57. package/rules/js/js/eslint-config.mdc +28 -0
  58. package/rules/js/js/extensions.mdc +8 -0
  59. package/rules/js/js/file-extensions.mdc +12 -0
  60. package/rules/js/js/for-in.mdc +26 -0
  61. package/rules/js/js/jscpd.mdc +42 -0
  62. package/rules/js/js/knip.mdc +15 -0
  63. package/rules/js/js/lint-js-workflow.mdc +58 -0
  64. package/rules/js/js/oxlintrc.mdc +20 -0
  65. package/rules/js/js/package-json.mdc +31 -0
  66. package/rules/js/js/tests.mdc +9 -0
  67. package/rules/js/js/utils-lib-structure.mdc +15 -0
  68. package/rules/js/main.mdc +21 -214
  69. package/rules/js-bun-db/js/bun-sql-migration.mdc +15 -0
  70. package/rules/js-bun-db/js/connection.mdc +42 -0
  71. package/rules/js-bun-db/js/pg-format-identifiers.mdc +102 -0
  72. package/rules/js-bun-db/js/pg-format-shim.mdc +99 -0
  73. package/rules/js-bun-db/js/pg-leftover.mdc +27 -0
  74. package/rules/js-bun-db/js/pg-listen-notify.mdc +51 -0
  75. package/rules/js-bun-db/js/query-safety.mdc +117 -0
  76. package/rules/js-bun-db/js/sql-array.mdc +88 -0
  77. package/rules/js-bun-db/js/unsafe.mdc +65 -0
  78. package/rules/js-bun-db/main.mdc +15 -605
  79. package/rules/js-bun-redis/js/imports.mdc +47 -0
  80. package/rules/js-bun-redis/js/package_json.mdc +44 -0
  81. package/rules/js-bun-redis/main.mdc +3 -11
  82. package/rules/js-mssql/js/mssql-in-list.mdc +38 -0
  83. package/rules/js-mssql/js/mssql-pool.mdc +56 -0
  84. package/rules/js-mssql/js/mssql-query-template.mdc +33 -0
  85. package/rules/js-mssql/js/mssql-tvp.mdc +75 -0
  86. package/rules/js-mssql/js/mssql-version.mdc +7 -0
  87. package/rules/js-mssql/main.mdc +10 -198
  88. package/rules/js-run/js/check-env.mdc +35 -0
  89. package/rules/js-run/js/conn-aliases.mdc +109 -0
  90. package/rules/js-run/js/jsconfig.mdc +20 -0
  91. package/rules/js-run/js/otel-configmap.mdc +6 -0
  92. package/rules/js-run/js/pino.mdc +6 -0
  93. package/rules/js-run/js/project-structure.mdc +11 -0
  94. package/rules/js-run/js/runtime.mdc +14 -0
  95. package/rules/js-run/js/scope.mdc +11 -0
  96. package/rules/js-run/js/settimeout.mdc +11 -0
  97. package/rules/js-run/js/temporal.mdc +5 -0
  98. package/rules/js-run/main.mdc +16 -218
  99. package/rules/k8s/js/configmap.mdc +41 -0
  100. package/rules/k8s/js/deployment_resources.mdc +49 -0
  101. package/rules/k8s/js/hasura_httproute.mdc +91 -0
  102. package/rules/k8s/js/hpa_apiversion.mdc +27 -0
  103. package/rules/k8s/js/ingress_gateway.mdc +16 -0
  104. package/rules/k8s/js/kustomize_structure.mdc +144 -0
  105. package/rules/k8s/js/lint_k8s.mdc +72 -0
  106. package/rules/k8s/js/multidoc_yaml.mdc +5 -0
  107. package/rules/k8s/js/network_policy.mdc +136 -0
  108. package/rules/k8s/js/schema_modeline.mdc +57 -0
  109. package/rules/k8s/js/service.mdc +44 -0
  110. package/rules/k8s/js/topology_hpa_pdb.mdc +181 -0
  111. package/rules/k8s/main.mdc +30 -843
  112. package/rules/nginx-default-tpl/js/dockerfile.mdc +36 -0
  113. package/rules/nginx-default-tpl/js/http-route.mdc +41 -0
  114. package/rules/nginx-default-tpl/js/ini-keys.mdc +21 -0
  115. package/rules/nginx-default-tpl/js/template-structure.mdc +86 -0
  116. package/rules/nginx-default-tpl/js/vscode.mdc +37 -0
  117. package/rules/nginx-default-tpl/main.mdc +6 -112
  118. package/rules/npm-module/js/docs/index.md +5 -5
  119. package/rules/npm-module/js/docs/rule_meta.md +6 -6
  120. package/rules/npm-module/js/docs/skill_meta.md +8 -8
  121. package/rules/npm-module/js/header_doc_pointer.mdc +18 -0
  122. package/rules/npm-module/js/package_structure.mdc +62 -0
  123. package/rules/npm-module/js/rule_meta.mdc +11 -0
  124. package/rules/npm-module/js/skill_meta.mdc +11 -0
  125. package/rules/npm-module/main.mdc +10 -55
  126. package/rules/php/js/lint_php_yml.mdc +12 -0
  127. package/rules/php/js/tooling.mdc +66 -0
  128. package/rules/php/main.mdc +7 -66
  129. package/rules/python/js/lint_python_yml.mdc +23 -0
  130. package/rules/python/js/pyproject_toml.mdc +32 -0
  131. package/rules/python/js/tooling.mdc +23 -0
  132. package/rules/python/main.mdc +9 -33
  133. package/rules/rego/js/rego-lint.mdc +31 -0
  134. package/rules/rego/js/vscode_extensions.mdc +11 -0
  135. package/rules/rego/js/vscode_settings.mdc +13 -0
  136. package/rules/rego/main.mdc +8 -24
  137. package/rules/rust/js/coverage.mdc +28 -0
  138. package/rules/rust/js/lint.mdc +22 -0
  139. package/rules/rust/js/tauri_composition.mdc +8 -0
  140. package/rules/rust/js/vscode_extensions.mdc +12 -0
  141. package/rules/rust/main.mdc +8 -38
  142. package/rules/security/js/rego_policies.mdc +15 -0
  143. package/rules/security/js/sample_secret.mdc +19 -0
  144. package/rules/security/js/trufflehog.mdc +21 -0
  145. package/rules/security/main.mdc +7 -35
  146. package/rules/style/js/admin-table.mdc +88 -0
  147. package/rules/style/js/colors.mdc +21 -0
  148. package/rules/style/js/gap.mdc +22 -0
  149. package/rules/style/js/quasar-fixes.mdc +32 -0
  150. package/rules/style/js/quasar.mdc +7 -0
  151. package/rules/style/js/tooling.mdc +85 -0
  152. package/rules/style/main.mdc +13 -253
  153. package/rules/tauri/js/cargo_mutants_config.mdc +39 -0
  154. package/rules/tauri/js/tool_surface.mdc +21 -0
  155. package/rules/tauri/js/tooling.mdc +25 -0
  156. package/rules/tauri/main.mdc +8 -78
  157. package/rules/test/js/cargo_mutants_config.mdc +18 -0
  158. package/rules/test/js/docs/index.md +7 -7
  159. package/rules/test/js/location.mdc +52 -0
  160. package/rules/test/js/no-console-store-restore.mdc +11 -0
  161. package/rules/test/js/no-process-chdir.mdc +15 -0
  162. package/rules/test/js/no-relative-fs-path.mdc +22 -0
  163. package/rules/test/js/sandbox-aware-test.mdc +28 -0
  164. package/rules/test/js/stryker_config.mdc +26 -0
  165. package/rules/test/js/vitest-config-pool-forks.mdc +33 -0
  166. package/rules/test/main.mdc +18 -184
  167. package/rules/text/js/ci-lint-text.mdc +15 -0
  168. package/rules/text/js/cspell.mdc +81 -0
  169. package/rules/text/js/dotenv-linter.mdc +16 -0
  170. package/rules/text/js/forbidden-prettier.mdc +13 -0
  171. package/rules/text/js/markdownlint.mdc +25 -0
  172. package/rules/text/js/oxfmt.mdc +35 -0
  173. package/rules/text/js/package-json.mdc +26 -0
  174. package/rules/text/js/shellcheck.mdc +18 -0
  175. package/rules/text/js/v8r.mdc +23 -0
  176. package/rules/text/js/vscode.mdc +86 -0
  177. package/rules/text/main.mdc +20 -237
  178. package/rules/vue/js/composition-api.mdc +82 -0
  179. package/rules/vue/js/nheader-layout.mdc +171 -0
  180. package/rules/vue/js/node-imports.mdc +25 -0
  181. package/rules/vue/js/quasar-ui.mdc +32 -0
  182. package/rules/vue/js/structure.mdc +101 -0
  183. package/rules/vue/js/testing.mdc +32 -0
  184. package/rules/vue/js/tfm-translations.mdc +26 -0
  185. package/rules/vue/js/vite-config.mdc +126 -0
  186. package/rules/vue/js/vite-env.mdc +55 -0
  187. package/rules/vue/js/vue-imports.mdc +25 -0
  188. package/rules/vue/main.mdc +16 -640
  189. package/scripts/auto-rules.mjs +6 -6
  190. package/scripts/auto-skills.mjs +3 -3
  191. package/scripts/docs/auto-rules.md +17 -31
  192. package/scripts/docs/auto-skills.md +18 -163
  193. package/scripts/docs/index.md +16 -16
  194. package/scripts/lib/docs/index.md +36 -36
  195. package/scripts/lib/docs/mirror-parity.md +7 -7
  196. package/scripts/lib/docs/rule-meta.md +12 -12
  197. package/scripts/lib/docs/skill-meta.md +9 -9
  198. package/scripts/lib/docs/worktree-notice.md +10 -8
  199. package/scripts/lib/rule-meta.mjs +6 -6
  200. package/scripts/lib/skill-meta.mjs +6 -6
  201. package/scripts/lib/worktree-notice.mjs +2 -2
  202. package/scripts/utils/docs/index.md +14 -14
@@ -7,660 +7,36 @@ alwaysApply: false
7
7
 
8
8
  # Vue 3 Composition API — правила для .cursorrules
9
9
 
10
- ## Найкращі практики Vue 3 Composition API
10
+ Правило охоплює стандарти написання Vue 3 SFC з Composition API, конфігурацію Vite-проєкту, UI-бібліотеки, заборонені патерни та вимоги до тестування.
11
11
 
12
- ```javascript
13
- const vue3CompositionApiBestPractices = [
14
- 'Використовуй функцію setup() для логіки компонента',
15
- 'Реалізуй computed змінні через $computed()',
16
- 'Реалізуй ref змінні через $ref',
17
- 'Використовуй watch і watchEffect для побічних ефектів',
18
- 'Підключай lifecycle hooks: onMounted, onUpdated тощо',
19
- 'Для глибоко вкладених залежностей використовуй composables, props/emits або store'
20
- 'не використовуй provide/inject для залежностей'
21
- ]
22
- ```
12
+ [vue-composition-api](./js/composition-api.mdc)
23
13
 
24
- ## Quasar як UI-основа
14
+ [vue-quasar-ui](./js/quasar-ui.mdc)
25
15
 
26
- У Vue-проектах використовуй **Quasar** як базовий UI-фреймворк:
16
+ [vue-structure](./js/structure.mdc)
27
17
 
28
- - **Компоненти:** `q-btn`, `q-input`, `q-select`, `q-table`, `q-dialog`, `q-card`, `q-layout`, `q-page`, `q-drawer` тощо — основа UI.
29
- - **Плагіни:** `Notify`, `Dialog`, `Loading` та інші Quasar-плагіни.
30
- - **Кольори:** використовуй Quasar CSS-змінні (`primary`, `secondary`, `accent`, `positive`, `negative`, `warning`, `info`, `dark`) і утиліти (`text-primary`, `bg-accent` тощо).
31
- - **Утиліти:** flex-layout (`row`, `col`, `items-center`), spacing (`q-pa-md`, `q-mt-sm`), shadow (`shadow-2`) — зі стандартної бібліотеки Quasar.
32
- - **Кастомні компоненти** `@nitra/components` — **надбудова** над Quasar, а не заміна; їх слід надавати перевагу лише там, де вони є (див. розділ `@nitra/components` нижче).
18
+ [vue-vite-config](./js/vite-config.mdc)
33
19
 
34
- ## Структура папок
20
+ [vue-vite-env](./js/vite-env.mdc)
35
21
 
36
- ```javascript
37
- const folderStructure = `
38
- src/
39
- components/
40
- composables/
41
- views/
42
- router/
43
- store/
44
- assets/
45
- public/
46
- App.vue
47
- main.mjs
48
- `
49
- ```
22
+ [vue-vue-imports](./js/vue-imports.mdc)
50
23
 
51
- ### Найменування файлів
24
+ [vue-node-imports](./js/node-imports.mdc)
52
25
 
53
- - **SFC:** імена файлів компонентів у **PascalCase** починаючи з букви N(`NMyWidget.vue`).
54
- - **Інші JS-модулі:** узгоджено **kebab-case** (`date-utils.mjs`).
26
+ [vue-testing](./js/testing.mdc)
55
27
 
56
- ### Модулі та архітектура
28
+ [vue-nheader-layout](./js/nheader-layout.mdc)
57
29
 
58
- - **ES modules** (`import`/`export`), один модуль — одна відповідальність, уникай **circular dependencies**.
59
- - **Composition** замість успадкування; логіку для повторного використання винось у **composables** (замість зайвих HOC, де це доречно).
30
+ [vue-tfm-translations](./js/tfm-translations.mdc)
60
31
 
61
- ### Code splitting
32
+ ## Швидкий gate через conftest
62
33
 
63
- - **Route-based:** lazy-імпорти в маршрутах (`() => import('...')`) і async components.
34
+ Rego-перевірки (запускаються через `npx @nitra/cursor fix vue`):
64
35
 
65
- ## Додаткові вказівки
66
-
67
- ```javascript
68
- const additionalInstructions = `
69
- 1. Використовуй JavaScript
70
- 2. Коректно оголошуй props, emits, defineModel
71
- 3. За потреби використовуй компонент Teleport у Vue 3
72
- 4. Застосовуй Suspense для async components
73
- 5. Реалізуй належний error handling
74
- 6. Дотримуйся Vue 3 style guide і naming conventions
75
- 7. Використовуй Vite для швидкої розробки та збірки
76
- `
77
- ```
78
-
79
- ### Детальні вказівки
80
-
81
- 1. **Vue router**: Завжди використовуй Vue router для маршрутизації в проекті версії 5 та його file-based routing.
82
- 2. **Компонент Teleport**: Використовуй Teleport у Vue 3, коли потрібно рендерити поза поточною ієрархією DOM.
83
- 3. **Suspense для async components**: Застосовуй Suspense для асинхронних компонентів і кращого UX.
84
- 4. **Error handling**: Реалізуй глобальний error handling механізмами Vue 3, щоб коректно перехоплювати та обробляти помилки.
85
- 5. **Style guide і naming**: Дотримуйся офіційного Vue 3 style guide і naming conventions для узгодженості кодової бази.
86
- 6. **Vue macros**: Використовуй Vue macros для більш ефективного розробки компонентів.
87
- 7. **Vue Reactivity Transform**: Використовуй Vue Reactivity Transform для змінних в компонентах.
88
-
89
- ### Патерни та антипатерни
90
-
91
- - Для глибоко вкладених залежностей використовуй **composables**, **props/emits** або **store**; **renderless**-компоненти / **slots** — коли логіку відділяєш від розмітки.
92
- - **HTTP:** окремі модулі **services** або **composables** для API; **async/await**.
93
- - **Події:** батько–дитина через **emits**; для не пов’язаних гілок — **store**.
94
- - Не мутуй **props** напряму — оновлення через подію вгору або v-model.
95
- - Обмежуй зайве в глобальному стані; локальний стан у компоненті — за замовчуванням.
96
- - Уникай прямої роботи з **DOM**, якщо достатньо реактивного шаблону та ref.
97
-
98
- ### State management
99
-
100
- - **Single source of truth** для спільних даних: у нових проєктах на Vue 3 **Pinia** (модульні stores, actions)
101
- - Похідний стан — через обчислення в store або **computed** у компонентах, без «тихих» побічних ефектів у getters.
102
-
103
- ### Обробка помилок (додатково)
104
-
105
- - **try/catch** навколо async-операцій; зрозумілі повідомлення для користувача через notifySuccess, notifyError; логування на сервіс моніторингу за потреби.
106
-
107
- ### Продуктивність
108
-
109
- - **v-for** — стабільні унікальні **`:key`**; не плутай **v-if** (умовний mount) і **v-show** (перемикання visibility).
110
- - **debounce/throttle** для частих подій.
111
- - Після ручних **addEventListener** / підписок — прибирай у **onUnmounted**.
112
-
113
- ### Функції в шаблоні
114
-
115
- Виклики функцій у шаблоні дозволені **лише** в обробниках подій (`@click`, `@change` тощо). У всіх інших місцях — `v-if`, `v-show`, атрибутах (`:prop`), інтерполяціях (`{{ }}`) — замінюй функції на `computed`-властивості: функція виконується при **кожному** render-і, тоді як `computed` кешується і перераховується лише при зміні залежностей.
116
-
117
- ```vue
118
- <!-- ❌ функція в умові, атрибуті та інтерполяції -->
119
- <q-item v-if="getItems(order).length" :label="getLabel(item)">
120
- {{ formatName(user) }}
121
- </q-item>
122
-
123
- <!-- ✅ реактивні змінні / computed / props -->
124
- <q-item v-if="itemsMap[order.id].length" :label="item.label">
125
- {{ user.displayName }}
126
- </q-item>
127
- <!-- обробник події — виклик функції дозволений -->
128
- <q-btn @click="doSomething(item)" />
129
- ```
130
-
131
- ### Безпека
132
-
133
- - Не довіряй **v-html** без санітизації; для форм/API — **CSRF**-захист за потреби; валідація **на сервері** обов’язкова.
134
-
135
- ### Тестування
136
-
137
- - **Unit + Component / DOM:** **Vitest** (`vitest`) + **Vue Test Utils** з **happy-dom** як DOM-середовищем. Це канон, узгоджений з `test.mdc` (Stryker з vitest-runner + `perTest`-аналіз покриття). `vitest.config.mjs` повторно використовує `vite.config.js` через `mergeConfig` і перемикає `environment` на `'happy-dom'`:
138
-
139
- ```js title="vitest.config.mjs"
140
- import { defineConfig, mergeConfig } from 'vitest/config'
141
- import viteConfig from './vite.config.js'
142
-
143
- export default mergeConfig(viteConfig, defineConfig({
144
- test: {
145
- include: ['**/*.test.{js,mjs}', 'tests/**/*.test.{js,mjs}'],
146
- environment: 'happy-dom',
147
- coverage: { provider: 'v8', reporter: ['lcov', 'text-summary'] }
148
- }
149
- }))
150
- ```
151
-
152
- `jsdom` не використовуй — happy-dom швидший і достатній для типових Vue-компонентних тестів.
153
-
154
- У `package.json#scripts` тримай `"test": "vitest run"`; vitest devDeps (`vitest`, `@vitest/coverage-v8`, `@stryker-mutator/vitest-runner`) — у кореневому `devDependencies` (npm-module rule забороняє devDeps у published workspace-у).
155
-
156
- - **E2E:** **Playwright** змістовні сценарії користувацьких потоків.
157
-
158
- ### Інструменти (узгоджено з Vite і цим правилом)
159
-
160
- - Якість коду: **ESLint** + **eslint-plugin-vue**; форматування — **oxfmt**, див. `text.mdc`.
161
- - Збірка та dev-сервер — **Vite**
162
- - **Vue Devtools** для дебагу; продакшен-збірка — **`vite build`**, оптимізація асетів і кешування на рівні деплою / CDN.
163
- - **esbuild заборонено:** у проєкті не має бути залежності `esbuild` і згадок `esbuild` у конфігах/коді. Якщо десь є налаштування або інструкції під `esbuild` — заміни на **rolldown**.
164
-
165
- ### CI/CD
166
-
167
- - У pipeline: **install**, **lint**, **test**, **vite build**; артефакти з продакшен-режиму.
168
-
169
- ### Приклад
170
-
171
- ```javascript
172
- // Приклад Vue 3 компонента з Composition API
173
- import { computed, onMounted } from 'vue'
174
-
175
- export default {
176
- setup() {
177
- const count = $ref(0)
178
- const doubleCount = $computed(() => count * 2)
179
-
180
- onMounted(() => {
181
- console.log('Компонент змонтовано')
182
- })
183
-
184
- return {
185
- count,
186
- doubleCount
187
- }
188
- }
189
- }
190
- ```
191
-
192
- в файлі .vscode/extensions.json є налаштування для Vue:
193
-
194
- ```json title=".vscode/extensions.json"
195
- {
196
- "recommendations": ["Vue.volar"]
197
- }
198
- ```
199
-
200
- мінімальний повний `package.json` для Vite + Vue + `vue-macros` (версії підлаштуй під проєкт):
201
-
202
- ```json title="package.json"
203
- {
204
- "name": "my-vue-app",
205
- "private": true,
206
- "type": "module",
207
- "dependencies": {
208
- "vue": "^3.6.12"
209
- },
210
- "devDependencies": {
211
- "vite": "^8.0.0",
212
- "@vitejs/plugin-vue": "^6.0.0",
213
- "vue-macros": "^3.1.2"
214
- }
215
- }
216
- ```
217
-
218
- ```javascript title="vite.config.js"
219
- import Vue from '@vitejs/plugin-vue'
220
- import VueMacros from 'vue-macros/vite'
221
- import { defineConfig } from 'vite'
222
- import AutoImport from 'unplugin-auto-import/vite'
223
- import Layouts from 'vite-plugin-vue-layouts-next'
224
-
225
- export default defineConfig({
226
- plugins: [
227
- AutoImport({
228
- imports: [
229
- // presets
230
- 'vue',
231
- 'vue-router',
232
- 'quasar',
233
- 'pinia',
234
- // custom
235
- {
236
- '@nitra/vite-boot/apollo': [
237
- // named imports
238
- 'gql',
239
- 'useQuery',
240
- 'useMutation',
241
- 'useSubscription'
242
- ],
243
- '@nitra/consola': [
244
- // named imports
245
- 'createLogger' // import { createLogger } from '@nitra/consola'
246
- ]
247
- }
248
- ]
249
- }),
250
- VueMacros({
251
- plugins: {
252
- vue: Vue()
253
- }
254
- }),
255
- Layouts()
256
- ]
257
- })
258
- ```
259
-
260
- ## Vite client types (Volar, імпорти асетів)
261
-
262
- Без типів **Vite** редактор (Volar / TypeScript) не знає, що імпорт статичного файлу (`import url from './hero.avif'`, `*.png`, `*.svg` тощо) відповідає модулю з `string` URL. Тоді у `.vue` з’являється помилка на кшталт **Cannot find module '…' or its corresponding type declarations**.
263
-
264
- У **кожному** workspace-пакеті з **Vue + Vite** обов’язково:
265
-
266
- 1. **`src/vite-env.d.ts`** — рівно з посиланням на клієнтські типи Vite (одного рядка достатньо):
267
-
268
- ```ts title="src/vite-env.d.ts"
269
- /// <reference types="vite/client" />
270
- ```
271
-
272
- Так підтягуються декларації з `vite/client.d.ts` (`declare module '*.avif'`, `*.png`, …).
273
-
274
- 2. **Корінь пакета:** **`jsconfig.json`** із **`include`**, що охоплює `src` (наприклад `"include": ["src/**/*"]`), щоб мова служби бачила `vite-env.d.ts` і SFC.
275
-
276
- Мінімальний приклад для JS-пакета:
277
-
278
- ```json title="jsconfig.json"
279
- {
280
- "compilerOptions": {
281
- "target": "ESNext",
282
- "module": "ESNext",
283
- "moduleResolution": "bundler",
284
- "lib": ["ESNext", "DOM", "DOM.Iterable"],
285
- "jsx": "preserve",
286
- "strict": true,
287
- "noEmit": true,
288
- "skipLibCheck": true,
289
- "resolveJsonModule": true,
290
- "isolatedModules": true,
291
- "allowJs": true
292
- },
293
- "include": ["src/**/*"]
294
- }
295
- ```
296
-
297
- **Не** звужуй без потреби **`compilerOptions.types`** до `["vite/client"]`: це може відрізати інші пакети з `@types` і зламати інші підказки. Достатньо `/// <reference types="vite/client" />` у `vite-env.d.ts` і коректного `include`.
298
-
299
- ## Тести
300
-
301
- Проекту повинен бути покритий тестами E2E за допомогою Playwright.
302
-
303
- ### Висновок
304
-
305
- Дотримуючись цих практик і правил, можна будувати масштабовані, підтримувані та ефективні застосунки на Vue 3 з Composition API. Завжди звіряйся з офіційною документацією Vue 3 щодо оновлень і нових можливостей.
306
-
307
- Потрібно використовувати Vite версії 8 та вище для frontend проекту на Vue.
308
-
309
- Потрібно використовувати unplugin-auto-import для автоматичного імпортування компонентів, composables, utils та інших функцій і прибирати з файлів усередині Vite проектів відповідні ручні імпорти, зокрема рядки виду `import { … } from 'vue'` — API Vue (`ref`, `computed`, `watch` тощо) мають підставлятися через auto-import, а не дублюватися явним імпортом з модуля `vue`.
310
-
311
- **Виняток — бібліотеки компонентів (`vue` у `peerDependencies`).** Увесь стек auto-import застосовується лише коли `vue` підключено як звичайну `dependencies`. Якщо ж пакет оголошує `vue` у `peerDependencies` — це проєкт-бібліотека компонентів: його джерела споживаються Vite-додатками і **не** проходять через `unplugin-auto-import` споживача (auto-import резолвиться лише в коді самого додатка, не в `node_modules`). Тому до таких пакетів **не** застосовуються: заборона явних `import { ref, computed, … } from 'vue'` (вони обовʼязкові), вимога `'vue'` у `AutoImport.imports`, а також вимоги наявності `VueMacros` / `AutoImport` у `vite.config`. Решта перевірок (заборона `esbuild`, `process.env.npm_lifecycle_event` тощо) лишаються. Тригер винятку — `isVueComponentLibraryPkg` у `npm/rules/vue/js/packages.mjs`.
312
-
313
- Потрібно використовувати vite-plugin-vue-layouts-next для автоматичного імпортування layout компонентів.
314
-
315
- ## npm_lifecycle_event
316
-
317
- у більшості проектів в файлі vite.config.js
318
- є конструкція виду
319
-
320
- switch (process.env.npm_lifecycle_event) {
321
- case 'start-remote-tr': {
322
-
323
- вона перестала працювати з новим Bun (і за цього не буде працювати bun start-remote-dev та інші)
324
-
325
- і тепер її нада обрамити в функцію, наприклад
326
-
327
- ```javascript title="vite.config.js"
328
- function getProxy(mode) {
329
- const proxy = {}
330
-
331
- switch (mode) {
332
- case 'remote-tr': {
333
- proxy['^/auth/.*'] = 'https://tr.efes.cloud'
334
- proxy['/file-link/'] = 'https://tr.efes.cloud'
335
- break
336
- }
337
- default: {
338
- proxy['^/auth/.*'] = 'https://dev.efes.cloud'
339
- proxy['/file-link/'] = 'https://dev.efes.cloud'
340
- }
341
- }
342
- return proxy
343
- }
344
- ```
345
-
346
- і викликати всередині
347
-
348
- ```javascript title="vite.config.js"
349
- export default defineConfig(({ mode, command }) => {
350
- ...
351
- server: {
352
- proxy: getProxy(mode)
353
- }
354
- ```
355
-
356
- ## Заборонено імпортувати Node-нативні модулі у `.vue` SFC
357
-
358
- Vue SFC виконується у браузері, тож API Node.js там недоступне. У `<script>` (включно з `<script setup>`)
359
- заборонено будь-які імпорти вбудованих модулів Node — як з префіксом `node:`, так і bare-ім’ям модуля
360
- (включно з підшляхами):
361
-
362
- ```vue title="погано — ламає білд"
363
- <script setup lang="ts">
364
- import { setTimeout as sleep } from 'node:timers/promises'
365
- import fs from 'fs'
366
- import { readFile } from 'fs/promises'
367
- import path from 'node:path'
368
- </script>
369
- ```
370
-
371
- Якщо потрібна логіка з Node API — винеси її у server-side утіліту (наприклад, у backend-пакет монорепо)
372
- та звертайся до неї через HTTP/GraphQL. Браузерні замінники (`window.crypto`, `URL`, `setTimeout` глобальний,
373
- `AbortController` тощо) використовуй напряму, без import.
374
-
375
- Правило стосується саме `.vue` файлів. Допоміжні `.ts`/`.js` модулі, які споживаються лише server-side
376
- (наприклад, окремий пакет утіліт), можуть імпортувати Node-built-ins без обмежень.
377
-
378
- ## @nitra/components — надавай перевагу перед Quasar-компонентами
379
-
380
- При створенні нової функціональності використовуй компоненти `@nitra/components`, якщо логіка компонента дозволяє отримати потрібний функціонал. Заміни:
381
-
382
- | Завдання | `@nitra/components` | Замість Quasar |
383
- | --- | --- | --- |
384
- | Діалоги | `NDialog` | `q-dialog` |
385
- | Multi-вибір | `NSelectMulti` | `q-select` (multiple) |
386
- | Текстовий редактор | `NEditor` | `q-editor` |
387
- | Вибір дати | `NDate` | — |
388
- | Вибір місяця/року | `NDateMonthYear` | — |
389
- | Діапазон дат | `NDateRange` | — |
390
- | Дата і час | `NDateTime` | — |
391
- | Drag&drop список | `NDraggableList` | — |
392
- | Редаговане значення | `NEditableString` | — |
393
- | Повідомлення | `NCallout` | — |
394
- | Хедер проекту | `NHeader` | — |
395
- | Перемикання мов | `NLang` | — (якщо `NHeader` не використовується) |
396
- | Меню проекту | `NMenu` | — |
397
- | Зображення (масив / одиночне) | `NImages` | — |
398
- | Завантаження файлів | `NUploader` | — |
399
- | Вибір колонок `q-table` | `NTableColumns` | — |
400
-
401
- ## NHeader — телепорт-слоти на сторінках
402
-
403
- У проектах з `NHeader` використовуй `<teleport>` для вбудовування контенту сторінки в шапку:
404
-
405
- | Слот | Призначення |
36
+ | Namespace | Що перевіряє |
406
37
  | --- | --- |
407
- | `#header-subtitle` | Заголовок сторінки (якщо потрібно перевизначити subtitle) |
408
- | `#header-center` | Важливі повідомлення та елементи управління |
409
- | `#header-filters` | Фільтри, кнопки та інші елементи управління сторінки |
410
-
411
- Оскільки `NHeader` за замовчуванням темний, додавай до полів і селектів у телепортах: `dark`, `standout="bg-white text-primary"`, `:options-dark="false"`.
412
-
413
- Завжди обгортай `<teleport>` в `v-if="mounted"` (де `mounted` — `$ref(false)`, що встановлюється в `onMounted`), щоб уникнути помилки відсутності target-елемента при SSR / першому рендері.
414
-
415
- ```vue
416
- <template>
417
- <q-page>
418
- <!-- ФІЛЬТРИ В ШАПЦІ -->
419
- <teleport v-if="mounted" to="#header-filters">
420
- <div class="col row items-center n-gap-sm q-pa-sm">
421
- <q-input
422
- v-model="pageStore.filterName"
423
- :label="t`Поиск по названию`"
424
- debounce="200"
425
- standout="bg-white text-primary"
426
- clearable
427
- dense
428
- dark
429
- class="col"
430
- style="min-width: 120px">
431
- <template #prepend>
432
- <q-icon name="search" />
433
- </template>
434
- </q-input>
435
-
436
- <n-select-multi
437
- v-model="pageStore.filterRequestTypes"
438
- :options="requestTypeOptions"
439
- :label="t`Тип запроса`"
440
- dark
441
- standout="bg-white text-primary"
442
- :options-dark="false"
443
- style="min-width: 180px; max-width: 400px"
444
- dense
445
- emit-value
446
- map-options
447
- clearable
448
- searchable
449
- :stack-label="false" />
450
-
451
- <q-btn
452
- @click="addItem"
453
- icon="add"
454
- :label="t`Добавить`"
455
- color="primary"
456
- no-caps
457
- padding="8px 12px"
458
- unelevated />
459
- </div>
460
- </teleport>
461
-
462
- <!-- ОСНОВНИЙ КОНТЕНТ -->
463
- ...
464
- </q-page>
465
- </template>
466
- <script setup>
467
- const mounted = $ref(false)
468
- onMounted(() => { mounted = true })
469
- </script>
470
- ```
471
-
472
- ## @nitra/tfm — переклади
473
-
474
- Використовуй `@nitra/tfm` для всіх текстів. Переклади для всіх мов проекту оголошуй наприкінці `<script setup>` у функції `getTr()`. Змінна `lang` із `@nitra/tfm` — для визначення або зміни поточної мови застосунку.
475
-
476
- ```vue
477
- <template>
478
- {{ lang }}
479
- {{ t`Анкеты` }}
480
- {{ subtitle }}
481
- </template>
482
- <script setup>
483
- import { lang, tf as tfm } from '@nitra/tfm'
484
- const t = tfm.bind({ tr: getTr() })
485
- const subtitle = $computed(() => t`Анкеты`)
486
-
487
- /**
488
- * LOCALIZATION
489
- * @returns {object} translations
490
- */
491
- function getTr() {
492
- return {
493
- Анкеты: { en: 'Surveys', ro: 'Sondaje', tr: 'Anketler' }
494
- }
495
- }
496
- </script>
497
- ```
498
-
499
- ## Layout з NHeader
500
-
501
- Для нових layout-ів використовуй `NHeader` з вбудованими `NLang` і `NMenu`.
502
-
503
- ```vue
504
- <template>
505
- <q-layout view="hHh Lpr lFf">
506
- <n-header
507
- v-model="leftSideOpened"
508
- :logo="baseUrl + 'logo.png'"
509
- :logo-url="homeUrl"
510
- title="My App"
511
- :subtitle="subtitle"
512
- :username="userName"
513
- toolbar-dark>
514
- <template #top-toolbar>
515
- <div class="platform-ios-only q-py-lg" />
516
- </template>
517
- <div>default slot content</div>
518
- </n-header>
519
-
520
- <!-- ЛЕВАЯ КОЛОНКА -->
521
- <q-drawer v-model="leftSideOpened" :breakpoint="700" :width="300" overlay class="col column shadow-5 bg-grey-1">
522
- <div class="platform-ios-only q-py-lg" />
523
- <div v-if="!$q.screen.gt.xs" class="q-pa-sm row items-center">
524
- <q-icon name="account_circle" color="primary" size="32px" class="q-mr-sm" />
525
- <div class="text-subtitle1">{{ user.name }}</div>
526
- </div>
527
- <n-menu v-model="activeMenu" :menu="menu" />
528
- <q-space />
529
- <n-menu :menu="homeMenu" />
530
- <div class="platform-ios-only q-py-md" />
531
- </q-drawer>
532
-
533
- <q-page-container>
534
- <router-view />
535
- </q-page-container>
536
- </q-layout>
537
- </template>
538
- <script setup>
539
- import { lang, tf as tfm } from '@nitra/tfm'
540
- const t = tfm.bind({ tr: getTr() })
541
-
542
- const baseUrl = import.meta.env.BASE_URL
543
- const homeUrl = String.raw`https:\\` + import.meta.env.VITE_DOMAIN
544
-
545
- // Ліва колонка
546
- const leftSideOpened = $ref(false)
547
- const activeMenu = $ref(null)
548
-
549
- // Заголовок у шапці з поточного пункту меню
550
- const subtitle = computed(() => (activeMenu ? getTr()[activeMenu.labelKey]?.[lang.value] || activeMenu?.labelKey : ''))
551
-
552
- // Меню
553
- const menu = computed(() =>
554
- [
555
- {
556
- icon: 'sym_o_store',
557
- label: t`Клиенты`,
558
- labelKey: t`Клиенты`,
559
- routeName: 'customer'
560
- },
561
- {
562
- icon: 'sym_o_route',
563
- label: t`Маршруты и визиты`,
564
- items: [
565
- {
566
- icon: 'sym_o_route',
567
- label: t`Маршруты`,
568
- labelKey: t`Маршруты`,
569
- routeName: 'route'
570
- },
571
- {
572
- icon: 'sym_o_event_upcoming',
573
- label: t`Переносы маршрутов`,
574
- labelKey: t`Переносы маршрутов`,
575
- routeName: 'route_postpone'
576
- }
577
- ]
578
- }
579
- ].filter(item => {
580
- if (item.items?.length) {
581
- item.items = item.items.filter(i => can[i.permissionRoute || i.routeName])
582
- return item.items.length > 0
583
- }
584
- return can[item.routeName]
585
- })
586
- )
587
-
588
- /**
589
- * LOCALIZATION
590
- * @returns {object} translations
591
- */
592
- function getTr() {
593
- return {
594
- Клиенты: { en: 'Customers', ro: 'Clienți', tr: 'Müşteriler' }
595
- }
596
- }
597
- </script>
598
- ```
599
-
600
- ## Pinia store для стану сторінки
601
-
602
- Зберігай у Pinia store:
603
-
604
- - вибрані значення фільтрів
605
- - вибрані для відображення колонки (`NTableColumns`)
606
- - кількість записів на сторінці (pagination)
607
-
608
- Називай store за назвою сторінки або компонента — `customerPageStore`, `routePageStore` тощо. На сторінці звертайся до нього через змінну `pageStore`.
609
-
610
- ```javascript
611
- // store/customerPage.mjs
612
- export const useCustomerPageStore = defineStore('customerPage', {
613
- state: () => ({
614
- filterName: '',
615
- filterStatus: [],
616
- columns: [],
617
- rowsPerPage: 20
618
- })
619
- })
620
- ```
621
-
622
- ```vue
623
- <script setup>
624
- const pageStore = useCustomerPageStore()
625
- </script>
626
- ```
627
-
628
- ## Коментарі в `<template>`
629
-
630
- Додавай коментарі в `<template>` відповідно до логічного призначення блоку. Коментарі допомагають швидко орієнтуватися в розмітці.
631
-
632
- ```vue
633
- <template>
634
- <q-page>
635
- <!-- ФІЛЬТРИ В ШАПЦІ -->
636
- <teleport v-if="mounted" to="#header-filters">...</teleport>
637
-
638
- <!-- ТАБЛИЦЯ -->
639
- <q-table ... />
640
-
641
- <!-- ДІАЛОГ РЕДАГУВАННЯ -->
642
- <n-dialog v-model="editDialog">...</n-dialog>
643
- </q-page>
644
- </template>
645
- ```
646
-
647
- ## Статичні файли — BASE_URL
648
-
649
- Для підключення статичних файлів не використовуй відносні шляхи. Завжди будуй URL через `import.meta.env.BASE_URL`:
650
-
651
- ```vue
652
- <!-- Погано -->
653
- <img src="/logo.png" />
654
- <img src="./assets/logo.png" />
655
-
656
- <!-- Добре -->
657
- <img :src="baseUrl + 'logo.png'" />
658
- ```
659
-
660
- ```javascript
661
- const baseUrl = import.meta.env.BASE_URL
662
- ```
38
+ | `vue.package_json` | `vite` 8 у devDeps, наявність `@vitejs/plugin-vue`, `vue-macros`, `unplugin-auto-import`, `vite-plugin-vue-layouts-next`; відсутність `esbuild`, `vitest`, `jsdom` |
663
39
 
664
40
  ## Перевірка
665
41
 
666
- `npx @nitra/cursor 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/js/packages/vue-forbidden-imports.mjs`).
42
+ `npx @nitra/cursor 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/js/packages/vue-forbidden-imports.mjs`).