@nitra/cursor 1.8.143 → 1.8.144

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/mdc/vue.mdc CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: Vue
3
3
  alwaysApply: true
4
- version: '1.2'
4
+ version: '1.3'
5
5
  ---
6
6
 
7
7
  # Vue 3 Composition API — правила для .cursorrules
@@ -111,6 +111,7 @@ const additionalInstructions = `
111
111
  - Якість коду: **ESLint** + **eslint-plugin-vue**; форматування — **oxfmt**, див. `text.mdc`.
112
112
  - Збірка та dev-сервер — **Vite**
113
113
  - **Vue Devtools** для дебагу; продакшен-збірка — **`vite build`**, оптимізація асетів і кешування на рівні деплою / CDN.
114
+ - **esbuild заборонено:** у проєкті не має бути залежності `esbuild` і згадок `esbuild` у конфігах/коді. Якщо десь є налаштування або інструкції під `esbuild` — заміни на **rolldown**.
114
115
 
115
116
  ### CI/CD
116
117
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.8.143",
3
+ "version": "1.8.144",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -24,6 +24,98 @@ import { walkDir } from './utils/walkDir.mjs'
24
24
  import { getMonorepoPackageRootDirs } from './utils/workspaces.mjs'
25
25
 
26
26
  const MAJOR_VERSION_RE = /(\d+)/
27
+ const ESBUILD_RE = /\besbuild\b/
28
+
29
+ /**
30
+ * Визначає, чи можна сканувати файл як текст на згадки `esbuild`.
31
+ * @param {string} relPosix відносний шлях у posix-форматі
32
+ * @returns {boolean} true якщо файл варто перевірити
33
+ */
34
+ function isEsbuildScanFile(relPosix) {
35
+ if (
36
+ relPosix.startsWith('node_modules/') ||
37
+ relPosix.startsWith('dist/') ||
38
+ relPosix.startsWith('build/') ||
39
+ relPosix.startsWith('coverage/') ||
40
+ relPosix.startsWith('.git/')
41
+ ) {
42
+ return false
43
+ }
44
+
45
+ const lower = relPosix.toLowerCase()
46
+ if (
47
+ lower === 'bun.lock' ||
48
+ lower === 'bun.lockb' ||
49
+ lower === 'package-lock.json' ||
50
+ lower === 'yarn.lock' ||
51
+ lower === 'pnpm-lock.yaml'
52
+ ) {
53
+ return false
54
+ }
55
+
56
+ return (
57
+ lower.endsWith('.js') ||
58
+ lower.endsWith('.mjs') ||
59
+ lower.endsWith('.cjs') ||
60
+ lower.endsWith('.ts') ||
61
+ lower.endsWith('.tsx') ||
62
+ lower.endsWith('.vue') ||
63
+ lower.endsWith('.json') ||
64
+ lower.endsWith('.jsonc') ||
65
+ lower.endsWith('.yaml') ||
66
+ lower.endsWith('.yml') ||
67
+ lower.endsWith('.md') ||
68
+ lower.endsWith('.mdc')
69
+ )
70
+ }
71
+
72
+ /**
73
+ * Сканує дерево пакета на згадки `esbuild` і підказує заміну на `rolldown`.
74
+ * @param {string} rootDir відносний шлях до пакета
75
+ * @param {string} absPackageRoot абсолютний шлях до кореня пакета
76
+ * @param {string} prefix параметр prefix
77
+ * @param {(msg: string) => void} passFn callback при успішній перевірці
78
+ * @param {(msg: string) => void} fail callback при помилці
79
+ */
80
+ async function checkEsbuildMentions(rootDir, absPackageRoot, prefix, passFn, fail) {
81
+ /** @type {{ rel: string; line: number; snippet: string }[]} */
82
+ const hits = []
83
+
84
+ await walkDir(absPackageRoot, absPath => {
85
+ const rel = relative(absPackageRoot, absPath).split('\\').join('/')
86
+ if (!isEsbuildScanFile(rel)) return
87
+ hits.push({ rel, line: 0, snippet: '' })
88
+ })
89
+
90
+ // ми використали hits як буфер шляхів; зараз перетворимо на реальні співпадіння
91
+ /** @type {{ rel: string; line: number; snippet: string }[]} */
92
+ const matches = []
93
+ for (const { rel } of hits) {
94
+ const content = await readFile(join(absPackageRoot, rel), 'utf8')
95
+ if (!ESBUILD_RE.test(content)) continue
96
+
97
+ const lines = content.split('\n')
98
+ for (let i = 0; i < lines.length; i++) {
99
+ if (ESBUILD_RE.test(lines[i])) {
100
+ matches.push({ rel, line: i + 1, snippet: lines[i].trim() })
101
+ if (matches.length >= 30) break
102
+ }
103
+ }
104
+ if (matches.length >= 30) break
105
+ }
106
+
107
+ if (matches.length === 0) {
108
+ passFn(`${prefix}немає згадок 'esbuild' у джерелах пакета (очікується rolldown)`)
109
+ return
110
+ }
111
+
112
+ for (const m of matches) {
113
+ fail(`${prefix}${m.rel}:${m.line} — знайдено 'esbuild'. Замінити на 'rolldown'. Фрагмент: ${m.snippet}`)
114
+ }
115
+ if (matches.length >= 30) {
116
+ fail(`${prefix}показано перші 30 збігів 'esbuild' (замінити на 'rolldown')`)
117
+ }
118
+ }
27
119
 
28
120
  /**
29
121
  * Формує зрозумілий для людини підпис пакета для повідомлень перевірки.
@@ -107,6 +199,9 @@ async function checkViteConfig(rootDir, prefix, passFn, fail) {
107
199
  return
108
200
  }
109
201
  const content = await readFile(join(rootDir, viteConfig), 'utf8')
202
+ if (ESBUILD_RE.test(content)) {
203
+ fail(`${prefix}${viteConfig} містить 'esbuild' — заміни на 'rolldown'`)
204
+ }
110
205
  const checks = [
111
206
  { token: 'VueMacros', ok: `${viteConfig} використовує VueMacros`, err: `${viteConfig} не містить VueMacros` },
112
207
  { token: 'AutoImport', ok: `${viteConfig} використовує AutoImport`, err: `${viteConfig} не містить AutoImport` }
@@ -175,6 +270,10 @@ async function checkVuePackage(rootDir, fail, passFn) {
175
270
  const devDeps = pkg.devDependencies || {}
176
271
  const allDeps = { ...deps, ...devDeps }
177
272
 
273
+ if (allDeps.esbuild) {
274
+ fail(`${prefix}esbuild заборонено (знайдено: ${allDeps.esbuild}). Замінити на rolldown та прибрати esbuild.`)
275
+ }
276
+
178
277
  checkRequiredDep(deps, 'vue', prefix, passFn, fail, 'vue відсутній в dependencies')
179
278
  checkViteVersion(devDeps, prefix, passFn, fail)
180
279
  checkRequiredDep(
@@ -205,6 +304,7 @@ async function checkVuePackage(rootDir, fail, passFn) {
205
304
 
206
305
  await checkViteConfig(rootDir, prefix, passFn, fail)
207
306
  await checkVueImportViolations(rootDir, join(process.cwd(), rootDir), prefix, passFn, fail)
307
+ await checkEsbuildMentions(rootDir, join(process.cwd(), rootDir), prefix, passFn, fail)
208
308
  }
209
309
 
210
310
  /**
@@ -215,17 +315,6 @@ export async function check() {
215
315
  const reporter = createCheckReporter()
216
316
  const { pass, fail } = reporter
217
317
 
218
- if (existsSync('.vscode/extensions.json')) {
219
- const ext = JSON.parse(await readFile('.vscode/extensions.json', 'utf8'))
220
- if (ext.recommendations?.includes('Vue.volar')) {
221
- pass('extensions.json містить Vue.volar')
222
- } else {
223
- fail('extensions.json не містить Vue.volar — додай до recommendations')
224
- }
225
- } else {
226
- fail('.vscode/extensions.json не існує')
227
- }
228
-
229
318
  const roots = await getMonorepoPackageRootDirs()
230
319
  /** @type {string[]} */
231
320
  const vueRoots = []
@@ -237,8 +326,23 @@ export async function check() {
237
326
  }
238
327
  }
239
328
 
329
+ if (vueRoots.length > 0) {
330
+ if (existsSync('.vscode/extensions.json')) {
331
+ const ext = JSON.parse(await readFile('.vscode/extensions.json', 'utf8'))
332
+ if (ext.recommendations?.includes('Vue.volar')) {
333
+ pass('extensions.json містить Vue.volar')
334
+ } else {
335
+ fail('extensions.json не містить Vue.volar — додай до recommendations')
336
+ }
337
+ } else {
338
+ fail('.vscode/extensions.json не існує (для Vue-проєкту потрібна рекомендація Vue.volar)')
339
+ }
340
+ } else {
341
+ pass('Vue.volar: пропущено (у repo немає пакетів з vue у dependencies)')
342
+ }
343
+
240
344
  if (vueRoots.length === 0) {
241
- fail('vue не знайдено в dependencies жодного пакета (корінь репо та каталоги з кореневого workspaces)')
345
+ pass('vue не знайдено в dependencies жодного пакета (перевірка vue пропущена)')
242
346
  return reporter.getExitCode()
243
347
  }
244
348