vibe-design-system 2.7.4 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/init.js CHANGED
@@ -149,19 +149,187 @@ function readPackageJson(projectRoot) {
149
149
  }
150
150
  }
151
151
 
152
- // ADIM 1 — Bağımlılık kontrolü (Storybook v8 + Tailwind v4 Vite)
153
- function needsStorybook(projectRoot) {
152
+ /**
153
+ * Monorepo kök dizini tespiti.
154
+ * workspaces veya pnpm-workspace.yaml ya da apps/packages klasörü varsa monorepo sayılır.
155
+ * Bulunan paket/uygulama dizinlerini döndürür (boş array = monorepo değil).
156
+ */
157
+ function detectMonorepoPackages(root) {
158
+ const pkg = readPackageJson(root);
159
+ if (pkg && pkg.workspaces) {
160
+ const patterns = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces.packages ?? [];
161
+ const dirs = [];
162
+ for (const p of patterns) {
163
+ const base = p.replace(/\/\*.*$/, "");
164
+ const full = path.join(root, base);
165
+ if (fs.existsSync(full)) {
166
+ const entries = fs.readdirSync(full, { withFileTypes: true });
167
+ for (const e of entries) {
168
+ if (e.isDirectory() && fs.existsSync(path.join(full, e.name, "package.json"))) {
169
+ dirs.push(path.join(full, e.name));
170
+ }
171
+ }
172
+ }
173
+ }
174
+ if (dirs.length) return dirs;
175
+ }
176
+ const pnpmWs = path.join(root, "pnpm-workspace.yaml");
177
+ if (fs.existsSync(pnpmWs)) {
178
+ const content = fs.readFileSync(pnpmWs, "utf-8");
179
+ const dirs = [];
180
+ for (const line of content.split("\n")) {
181
+ const m = line.match(/^\s*-\s*['"]?(.+?)['"]?\s*$/);
182
+ if (m) {
183
+ const base = m[1].replace(/\/\*.*$/, "");
184
+ const full = path.join(root, base);
185
+ if (fs.existsSync(full)) {
186
+ const entries = fs.readdirSync(full, { withFileTypes: true });
187
+ for (const e of entries) {
188
+ if (e.isDirectory() && fs.existsSync(path.join(full, e.name, "package.json"))) {
189
+ dirs.push(path.join(full, e.name));
190
+ }
191
+ }
192
+ }
193
+ }
194
+ }
195
+ if (dirs.length) return dirs;
196
+ }
197
+ for (const candidate of ["apps", "packages"]) {
198
+ const full = path.join(root, candidate);
199
+ if (fs.existsSync(full)) {
200
+ const entries = fs.readdirSync(full, { withFileTypes: true })
201
+ .filter((e) => e.isDirectory() && fs.existsSync(path.join(full, e.name, "package.json")))
202
+ .map((e) => path.join(full, e.name));
203
+ if (entries.length) return entries;
204
+ }
205
+ }
206
+ return [];
207
+ }
208
+
209
+ /**
210
+ * Projenin hangi framework'ü kullandığını tespit eder.
211
+ * @returns {"nextjs"|"remix"|"vite"}
212
+ */
213
+ function detectFramework(pkg) {
214
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
215
+ if (deps["next"]) return "nextjs";
216
+ if (deps["@remix-run/react"] || deps["@remix-run/node"]) return "remix";
217
+ return "vite";
218
+ }
219
+
220
+ /** Framework'e göre Storybook framework paket adı */
221
+ function storybookFrameworkPackage(framework) {
222
+ if (framework === "nextjs") return "@storybook/nextjs";
223
+ return "@storybook/react-vite"; // vite & remix
224
+ }
225
+
226
+ /** Framework'e göre .storybook/main.ts içeriği */
227
+ function buildStorybookMainTs(framework) {
228
+ if (framework === "nextjs") {
229
+ return `import type { StorybookConfig } from "@storybook/nextjs";
230
+
231
+ const config: StorybookConfig = {
232
+ stories: [
233
+ "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)",
234
+ "../app/**/*.stories.@(js|jsx|mjs|ts|tsx)",
235
+ ],
236
+ addons: ["@storybook/addon-essentials"],
237
+ framework: {
238
+ name: "@storybook/nextjs",
239
+ options: {},
240
+ },
241
+ };
242
+
243
+ export default config;
244
+ `;
245
+ }
246
+ // vite (default) & remix
247
+ return `import type { StorybookConfig } from "@storybook/react-vite";
248
+ import { mergeConfig } from "vite";
249
+ import path from "path";
250
+
251
+ const config: StorybookConfig = {
252
+ stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
253
+ addons: ["@storybook/addon-essentials"],
254
+ framework: {
255
+ name: "@storybook/react-vite",
256
+ options: {},
257
+ },
258
+ async viteFinal(config) {
259
+ return mergeConfig(config, {
260
+ resolve: {
261
+ alias: {
262
+ "@": path.resolve(process.cwd(), "src"),
263
+ },
264
+ },
265
+ });
266
+ },
267
+ };
268
+
269
+ export default config;
270
+ `;
271
+ }
272
+
273
+ /** Framework'e göre CSS import path'i */
274
+ function buildStorybookPreviewTs(framework, projectRoot) {
275
+ // CSS dosyasını bul
276
+ const cssCandidates = [
277
+ ["src/index.css", "../src/index.css"],
278
+ ["src/globals.css", "../src/globals.css"],
279
+ ["src/styles/globals.css", "../src/styles/globals.css"],
280
+ ["app/globals.css", "../app/globals.css"],
281
+ ];
282
+ let cssImport = '// CSS bulunamadı — projenizin global CSS dosyasını buraya ekleyin';
283
+ for (const [rel, importPath] of cssCandidates) {
284
+ if (fs.existsSync(path.join(projectRoot, rel))) {
285
+ cssImport = `import "${importPath}";`;
286
+ break;
287
+ }
288
+ }
289
+
290
+ return `import type { Preview } from "@storybook/react";
291
+ import React from "react";
292
+ ${cssImport}
293
+
294
+ const preview: Preview = {
295
+ parameters: {
296
+ layout: "fullscreen",
297
+ options: {
298
+ storySort: {
299
+ order: [
300
+ "Foundations",
301
+ ["Introduction", "Page to Components", "Colors", "Typography", "Brand", "Icons", "Component Suggestions", "Changelog"],
302
+ "Layout",
303
+ "Components",
304
+ "Actions",
305
+ "Data Display",
306
+ "Examples",
307
+ ],
308
+ },
309
+ },
310
+ },
311
+ };
312
+
313
+ export default preview;
314
+ `;
315
+ }
316
+
317
+ // ADIM 1 — Bağımlılık kontrolü
318
+ function needsStorybook(projectRoot, framework) {
154
319
  const pkg = readPackageJson(projectRoot);
155
320
  if (!pkg) return false;
156
321
  const dev = pkg.devDependencies || {};
157
- return !(dev.storybook && dev["@storybook/react-vite"] && dev["@tailwindcss/vite"]);
322
+ const fwPkg = storybookFrameworkPackage(framework);
323
+ return !(dev.storybook && dev[fwPkg]);
158
324
  }
159
325
 
160
326
  // Storybook 8.6.x — aynı minor sürümde tutarak addon uyumluluk uyarısını önler
161
327
  const STORYBOOK_VERSION = "8.6.17";
162
328
 
163
- function installStorybook(projectRoot) {
164
- console.log("📚 Storybook v8 ve @tailwindcss/vite kuruluyor...");
329
+ function installStorybook(projectRoot, framework) {
330
+ const fwPkg = storybookFrameworkPackage(framework);
331
+ console.log(`📚 Storybook v8 (${fwPkg}) kuruluyor...`);
332
+ const extraDeps = framework === "vite" ? ["@tailwindcss/vite"] : [];
165
333
  const r = spawnSync(
166
334
  "npm",
167
335
  [
@@ -169,11 +337,11 @@ function installStorybook(projectRoot) {
169
337
  "--save-dev",
170
338
  "--save-exact",
171
339
  `storybook@${STORYBOOK_VERSION}`,
172
- `@storybook/react-vite@${STORYBOOK_VERSION}`,
340
+ `${fwPkg}@${STORYBOOK_VERSION}`,
173
341
  `@storybook/react@${STORYBOOK_VERSION}`,
174
342
  `@storybook/addon-essentials@${STORYBOOK_VERSION}`,
175
343
  `@storybook/blocks@${STORYBOOK_VERSION}`,
176
- "@tailwindcss/vite",
344
+ ...extraDeps,
177
345
  ],
178
346
  { cwd: projectRoot, stdio: "inherit", shell: false }
179
347
  );
@@ -183,18 +351,21 @@ function installStorybook(projectRoot) {
183
351
  }
184
352
 
185
353
  // ADIM 2 — .storybook/
186
- function ensureStorybook(projectRoot) {
354
+ function ensureStorybook(projectRoot, framework) {
187
355
  const storybookDir = path.join(projectRoot, ".storybook");
188
356
  if (!fs.existsSync(storybookDir)) {
189
357
  fs.mkdirSync(storybookDir, { recursive: true });
190
358
  console.log("📁 .storybook/ oluşturuldu.");
191
359
  }
192
360
 
361
+ const mainTs = buildStorybookMainTs(framework);
193
362
  const mainPath = path.join(storybookDir, "main.ts");
194
- fs.writeFileSync(mainPath, STORYBOOK_MAIN_TS, "utf-8");
195
- console.log("📝 .storybook/main.ts yazıldı.");
363
+ fs.writeFileSync(mainPath, mainTs, "utf-8");
364
+ console.log(`📝 .storybook/main.ts yazıldı (framework: ${framework}).`);
365
+
366
+ const previewTs = buildStorybookPreviewTs(framework, projectRoot);
196
367
 
197
- // preview.ts / preview.tsx — eski VDS template'lerini otomatik olarak yeni router'sız template'e migrate et.
368
+ // preview.ts / preview.tsx — eski VDS template'lerini migrate et; kullanıcıya ait preview'e dokunma.
198
369
  const previewCandidates = ["preview.tsx", "preview.ts", "preview.jsx", "preview.js"];
199
370
  const existingPreview = previewCandidates.find((name) =>
200
371
  fs.existsSync(path.join(storybookDir, name))
@@ -202,11 +373,9 @@ function ensureStorybook(projectRoot) {
202
373
  const previewPath = existingPreview ? path.join(storybookDir, existingPreview) : null;
203
374
 
204
375
  if (!previewPath) {
205
- // Daha önce hiç preview yoksa, yeni template'i yaz.
206
- fs.writeFileSync(path.join(storybookDir, "preview.ts"), STORYBOOK_PREVIEW_TS, "utf-8");
376
+ fs.writeFileSync(path.join(storybookDir, "preview.ts"), previewTs, "utf-8");
207
377
  console.log("📝 .storybook/preview.ts yazıldı.");
208
378
  } else {
209
- // Eski VDS preview'lerini tespit edip otomatik migrate et; kullanıcıya ait preview'lere dokunma.
210
379
  try {
211
380
  const content = fs.readFileSync(previewPath, "utf-8");
212
381
  const looksLikeOldVdsPreview =
@@ -214,8 +383,8 @@ function ensureStorybook(projectRoot) {
214
383
  content.includes("const withRouter") ||
215
384
  content.includes("decorators: [withRouter");
216
385
  if (looksLikeOldVdsPreview) {
217
- fs.writeFileSync(previewPath, STORYBOOK_PREVIEW_TS, "utf-8");
218
- console.log("📝 .storybook/preview.* eski VDS şablonundan yeni router'sız şablona güncellendi.");
386
+ fs.writeFileSync(previewPath, previewTs, "utf-8");
387
+ console.log("📝 .storybook/preview.* eski VDS şablonundan yeni şablona güncellendi.");
219
388
  } else {
220
389
  console.log("ℹ️ .storybook/preview.* bulundu, kullanıcıya ait görünüyor; değiştirilmedi.");
221
390
  }
@@ -294,7 +463,7 @@ function ensureCursorrules(projectRoot) {
294
463
  console.log("📄 .cursorrules yazıldı.");
295
464
  }
296
465
 
297
- // ADIM 6 — src/stories/ (ve scan için gerekli src/components, src/pages)
466
+ // ADIM 6 — src/stories/ oluştur (scan artık klasörü kendisi buluyor; zorla yaratmaya gerek yok)
298
467
  function ensureStoriesDir(projectRoot) {
299
468
  const srcDir = path.join(projectRoot, "src");
300
469
  if (!fs.existsSync(srcDir)) fs.mkdirSync(srcDir, { recursive: true });
@@ -303,15 +472,8 @@ function ensureStoriesDir(projectRoot) {
303
472
  fs.mkdirSync(storiesDir, { recursive: true });
304
473
  console.log("📁 src/stories/ oluşturuldu.");
305
474
  }
306
- const componentsDir = path.join(srcDir, "components");
307
- if (!fs.existsSync(componentsDir)) {
308
- fs.mkdirSync(componentsDir, { recursive: true });
309
- console.log("📁 src/components/ oluşturuldu (VDS taraması için).");
310
- }
311
- const pagesDir = path.join(srcDir, "pages");
312
- if (!fs.existsSync(pagesDir)) {
313
- fs.mkdirSync(pagesDir, { recursive: true });
314
- }
475
+ // scan.mjs artık klasörü kendisi buluyor (src/components components → app/components → ...)
476
+ // Klasör yoksa oluşturma — projeye ait mevcut yapıyı koruyalım.
315
477
  }
316
478
 
317
479
  // ADIM 7 — İlk tarama
@@ -364,6 +526,24 @@ function runStorybookAdapt(projectRoot) {
364
526
  if (r.status !== 0) process.exitCode = r.status ?? 1;
365
527
  }
366
528
 
529
+ // ADIM 9b — vds.config.js oluştur (yoksa)
530
+ function ensureVdsConfig(projectRoot) {
531
+ const configPath = path.join(projectRoot, "vds.config.js");
532
+ if (fs.existsSync(configPath)) return;
533
+ const content = `/**
534
+ * VDS Configuration — auto-created by VDS installer
535
+ * All fields are optional. Remove comments to activate overrides.
536
+ */
537
+ module.exports = {
538
+ // skipList: ["MyHeavyPage"], // Replace the default story skip list entirely
539
+ // extraSkipList: ["OrderCard"], // Add to the default skip list
540
+ // extraIgnoreDirs: ["fixtures"], // Additional dirs to skip during component scan
541
+ };
542
+ `;
543
+ fs.writeFileSync(configPath, content, "utf-8");
544
+ console.log("⚙️ vds.config.js oluşturuldu.");
545
+ }
546
+
367
547
  // ADIM 9 — Storybook örnek dosyalarını sil
368
548
  function removeStorybookExamples(projectRoot) {
369
549
  const storiesDir = path.join(projectRoot, "src", "stories");
@@ -389,15 +569,35 @@ if (!pkg) {
389
569
  process.exit(1);
390
570
  }
391
571
 
572
+ // Monorepo kök dizininde mi?
573
+ const monorepoPackages = detectMonorepoPackages(projectRoot);
574
+ if (monorepoPackages.length > 0 && pkg.workspaces) {
575
+ console.warn("⚠️ Bu dizin bir monorepo kök dizini gibi görünüyor.");
576
+ console.warn(" VDS'yi her uygulama klasöründe ayrı ayrı kurmanız gerekiyor.\n");
577
+ console.warn(" Tespit edilen paketler:");
578
+ for (const p of monorepoPackages) {
579
+ const rel = path.relative(projectRoot, p);
580
+ console.warn(` → ${rel}`);
581
+ }
582
+ console.warn("\n Örnek kullanım:");
583
+ const firstRel = path.relative(projectRoot, monorepoPackages[0]);
584
+ console.warn(` cd ${firstRel} && npx vibe-design-system init`);
585
+ console.warn("\n VDS'yi bu dizinden çalıştırmaya devam etmek istiyorsanız,");
586
+ console.warn(" VDS_COMPONENTS_DIR env değişkeni ile hedef klasörü belirtin.\n");
587
+ }
588
+
589
+ const framework = detectFramework(pkg);
590
+ console.log(`🔍 Framework tespit edildi: ${framework}\n`);
591
+
392
592
  // ADIM 1
393
- if (needsStorybook(projectRoot)) {
394
- installStorybook(projectRoot);
593
+ if (needsStorybook(projectRoot, framework)) {
594
+ installStorybook(projectRoot, framework);
395
595
  } else {
396
596
  console.log("📚 Storybook zaten yüklü.");
397
597
  }
398
598
 
399
599
  // ADIM 2
400
- ensureStorybook(projectRoot);
600
+ ensureStorybook(projectRoot, framework);
401
601
 
402
602
  // ADIM 3
403
603
  ensureVdsCore(projectRoot);
@@ -414,6 +614,9 @@ ensureStoriesDir(projectRoot);
414
614
  // ADIM 9 (örnek dosyaları taramadan önce silebiliriz; scan src/components ve src/pages tarar, src/stories değil)
415
615
  removeStorybookExamples(projectRoot);
416
616
 
617
+ // ADIM 9b
618
+ ensureVdsConfig(projectRoot);
619
+
417
620
  // ADIM 7
418
621
  runScan(projectRoot);
419
622
  runStoryGenerator(projectRoot);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-design-system",
3
- "version": "2.7.4",
3
+ "version": "2.8.1",
4
4
  "description": "Auto-generate design systems for vibe coding projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,4 +38,4 @@
38
38
  "typescript": "^5.8.3",
39
39
  "vite": "^5.4.19"
40
40
  }
41
- }
41
+ }
@@ -26,12 +26,143 @@ const CLI_LOCALES = {
26
26
  };
27
27
  const CLI_LOCALE = (process.env.VDS_LOCALE === "tr" ? "tr" : "en");
28
28
  const cliT = (key, n) => CLI_LOCALES[CLI_LOCALE][key].replace("{n}", String(n));
29
- const SRC_DIR = path.join(PROJECT_ROOT, "src");
30
- const COMPONENTS_DIR = path.join(PROJECT_ROOT, "src", "components");
31
- const PAGES_DIR = path.join(PROJECT_ROOT, "src", "pages");
32
- const APP_DIR = path.join(PROJECT_ROOT, "src", "app");
33
29
 
34
- const IGNORE_DIRS = ["node_modules", "dist", ".next", "build", "vds-generated", ".storybook"];
30
+ // ── Klasör çözücüler ─────────────────────────────────────────────────────────
31
+ function resolveDir(envKey, candidates) {
32
+ if (process.env[envKey]) {
33
+ const d = path.resolve(PROJECT_ROOT, process.env[envKey]);
34
+ if (fs.existsSync(d)) return d;
35
+ console.warn(`[VDS] ${envKey}="${process.env[envKey]}" bulunamadı, fallback deneniyor.`);
36
+ }
37
+ for (const d of candidates) {
38
+ if (fs.existsSync(d)) return d;
39
+ }
40
+ return null;
41
+ }
42
+
43
+ const SRC_DIR = resolveDir("VDS_SRC_DIR", [
44
+ path.join(PROJECT_ROOT, "src"),
45
+ path.join(PROJECT_ROOT, "app"),
46
+ PROJECT_ROOT,
47
+ ]) || path.join(PROJECT_ROOT, "src");
48
+
49
+ const COMPONENTS_DIR = resolveDir("VDS_COMPONENTS_DIR", [
50
+ path.join(PROJECT_ROOT, "src", "components"),
51
+ path.join(PROJECT_ROOT, "components"),
52
+ path.join(PROJECT_ROOT, "app", "components"),
53
+ path.join(PROJECT_ROOT, "src", "views"),
54
+ path.join(PROJECT_ROOT, "src", "modules"),
55
+ ]);
56
+
57
+ const PAGES_DIR = resolveDir("VDS_PAGES_DIR", [
58
+ path.join(PROJECT_ROOT, "src", "pages"),
59
+ path.join(PROJECT_ROOT, "pages"),
60
+ ]);
61
+
62
+ const APP_DIR = resolveDir("VDS_APP_DIR", [
63
+ path.join(PROJECT_ROOT, "src", "app"),
64
+ path.join(PROJECT_ROOT, "app"),
65
+ ]);
66
+
67
+ // ── Monorepo detection ────────────────────────────────────────────────────────
68
+ function detectMonorepoPackages(root) {
69
+ const pkgPath = path.join(root, "package.json");
70
+ if (fs.existsSync(pkgPath)) {
71
+ try {
72
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
73
+ if (pkg.workspaces) {
74
+ const patterns = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces.packages ?? [];
75
+ const dirs = [];
76
+ for (const p of patterns) {
77
+ const base = p.replace(/\/\*.*$/, "");
78
+ const full = path.join(root, base);
79
+ if (fs.existsSync(full)) {
80
+ const entries = fs.readdirSync(full, { withFileTypes: true });
81
+ for (const e of entries) {
82
+ if (e.isDirectory()) dirs.push(path.join(full, e.name));
83
+ }
84
+ }
85
+ }
86
+ if (dirs.length) return dirs;
87
+ }
88
+ } catch (_) {}
89
+ }
90
+ const pnpmWs = path.join(root, "pnpm-workspace.yaml");
91
+ if (fs.existsSync(pnpmWs)) {
92
+ const content = fs.readFileSync(pnpmWs, "utf-8");
93
+ const dirs = [];
94
+ for (const line of content.split("\n")) {
95
+ const m = line.match(/^\s*-\s*['"]?(.+?)['"]?\s*$/);
96
+ if (m) {
97
+ const base = m[1].replace(/\/\*.*$/, "");
98
+ const full = path.join(root, base);
99
+ if (fs.existsSync(full)) {
100
+ const entries = fs.readdirSync(full, { withFileTypes: true });
101
+ for (const e of entries) {
102
+ if (e.isDirectory()) dirs.push(path.join(full, e.name));
103
+ }
104
+ }
105
+ }
106
+ }
107
+ if (dirs.length) return dirs;
108
+ }
109
+ for (const candidate of ["apps", "packages"]) {
110
+ const full = path.join(root, candidate);
111
+ if (fs.existsSync(full)) {
112
+ const entries = fs.readdirSync(full, { withFileTypes: true })
113
+ .filter((e) => e.isDirectory())
114
+ .map((e) => path.join(full, e.name));
115
+ if (entries.length) return entries;
116
+ }
117
+ }
118
+ return [];
119
+ }
120
+
121
+ if (COMPONENTS_DIR) {
122
+ console.log(`[VDS] Components: ${path.relative(PROJECT_ROOT, COMPONENTS_DIR)}`);
123
+ } else {
124
+ const monorepoPackages = detectMonorepoPackages(PROJECT_ROOT);
125
+ if (monorepoPackages.length > 0) {
126
+ console.warn("[VDS] Bileşen klasörü bulunamadı — bu bir monorepo kök dizini gibi görünüyor.");
127
+ console.warn("[VDS] Tespit edilen paketler:");
128
+ for (const p of monorepoPackages) {
129
+ console.warn(" →", path.relative(PROJECT_ROOT, p));
130
+ }
131
+ console.warn("[VDS] Çözüm: VDS'yi her uygulama klasöründen ayrı ayrı çalıştırın:");
132
+ console.warn(" cd apps/web && node ../vds-core/scan.mjs");
133
+ console.warn(" ya da: VDS_COMPONENTS_DIR=apps/web/src/components node vds-core/scan.mjs");
134
+ } else {
135
+ console.warn("[VDS] Bileşen klasörü bulunamadı. Tarama foundations-only modda devam edecek.");
136
+ console.warn("[VDS] Çözüm: VDS_COMPONENTS_DIR env değişkeni ile klasör belirtin.");
137
+ }
138
+ }
139
+
140
+ // ── vds.config.js loader ──────────────────────────────────────────────────────
141
+ function loadVdsConfig() {
142
+ const configPath = path.join(PROJECT_ROOT, "vds.config.js");
143
+ if (!fs.existsSync(configPath)) return {};
144
+ try {
145
+ const raw = fs.readFileSync(configPath, "utf-8");
146
+ const mod = { exports: {} };
147
+ // eslint-disable-next-line no-new-func
148
+ new Function("module", "exports", "require", raw)(mod, mod.exports, () => ({}));
149
+ const cfg = mod.exports.default ?? mod.exports;
150
+ return typeof cfg === "object" && cfg !== null ? cfg : {};
151
+ } catch (e) {
152
+ console.warn("[VDS] vds.config.js yüklenemedi:", e.message);
153
+ return {};
154
+ }
155
+ }
156
+
157
+ const VDS_CONFIG = loadVdsConfig();
158
+ if (Object.keys(VDS_CONFIG).length > 0) {
159
+ console.log("[VDS] vds.config.js yüklendi:", Object.keys(VDS_CONFIG).join(", "));
160
+ }
161
+
162
+ const IGNORE_DIRS = [
163
+ "node_modules", "dist", ".next", "build", "vds-generated", ".storybook",
164
+ ...(VDS_CONFIG.extraIgnoreDirs ?? []),
165
+ ];
35
166
  const OUTPUT_FILE = path.join(PROJECT_ROOT, "vds-output.json");
36
167
  const PUBLIC_MANIFEST = path.join(PROJECT_ROOT, "public", "vds-output.json");
37
168
  const HISTORY_FILE = path.join(PROJECT_ROOT, "vds-history.json");
@@ -521,7 +652,7 @@ function extractFullElement(content, classNameMatchIndex, tagName) {
521
652
  /** Return set of component names already in src/components (so we don't suggest them again). */
522
653
  function getExistingComponentNames() {
523
654
  const names = new Set();
524
- if (!fs.existsSync(COMPONENTS_DIR)) return names;
655
+ if (!COMPONENTS_DIR || !fs.existsSync(COMPONENTS_DIR)) return names;
525
656
  const walk = (dir) => {
526
657
  const entries = fs.readdirSync(dir, { withFileTypes: true });
527
658
  for (const e of entries) {
@@ -537,13 +668,12 @@ function getExistingComponentNames() {
537
668
  return names;
538
669
  }
539
670
 
540
- /** Scan src/pages and src/app for repeated visual-section patterns (className + 2+ children); return component suggestions. */
671
+ /** Scan pages/app/components dirs for repeated visual-section patterns; return component suggestions. */
541
672
  function extractComponentSuggestions() {
542
- const pageDirs = [
543
- { dir: PAGES_DIR, prefix: "src/pages/" },
544
- { dir: APP_DIR, prefix: "src/app/" },
545
- { dir: COMPONENTS_DIR, prefix: "src/components/" },
546
- ];
673
+ const pageDirs = [];
674
+ if (PAGES_DIR) pageDirs.push({ dir: PAGES_DIR, prefix: path.relative(PROJECT_ROOT, PAGES_DIR).replace(/\\/g, "/") + "/" });
675
+ if (APP_DIR && APP_DIR !== PAGES_DIR) pageDirs.push({ dir: APP_DIR, prefix: path.relative(PROJECT_ROOT, APP_DIR).replace(/\\/g, "/") + "/" });
676
+ if (COMPONENTS_DIR) pageDirs.push({ dir: COMPONENTS_DIR, prefix: path.relative(PROJECT_ROOT, COMPONENTS_DIR).replace(/\\/g, "/") + "/" });
547
677
  const allPageFiles = [];
548
678
  for (const { dir, prefix } of pageDirs) {
549
679
  if (!fs.existsSync(dir)) continue;
@@ -707,7 +837,7 @@ function extractUnreleasedSectionCandidates() {
707
837
  }));
708
838
  }
709
839
 
710
- const VDS_GENERATED_DIR = path.join(COMPONENTS_DIR, "vds-generated");
840
+ const VDS_GENERATED_DIR = COMPONENTS_DIR ? path.join(COMPONENTS_DIR, "vds-generated") : null;
711
841
 
712
842
  /** Ensure component name is valid JS: no leading digits/special chars, PascalCase. */
713
843
  function sanitizeComponentName(name) {
@@ -805,6 +935,7 @@ function collectComponentTags(jsx) {
805
935
  /** Write extracted components with full JSX body; add Lucide and UI imports as needed. */
806
936
  function writeVdsGeneratedComponents(suggestions) {
807
937
  if (!Array.isArray(suggestions) || suggestions.length === 0) return [];
938
+ if (!VDS_GENERATED_DIR) return [];
808
939
  if (!fs.existsSync(VDS_GENERATED_DIR)) fs.mkdirSync(VDS_GENERATED_DIR, { recursive: true });
809
940
  const usedNames = new Set();
810
941
  const entries = [];
@@ -1000,45 +1131,64 @@ function getTailwindExtendColors() {
1000
1131
  }
1001
1132
  }
1002
1133
 
1003
- /** Resolve Tailwind theme for boxShadow, spacing, screens, zIndex, motion. Uses resolveConfig when available. */
1134
+ function parseTailwindThemeFromText(raw) {
1135
+ const result = { shadows: {}, spacing: {}, breakpoints: {}, zIndex: {}, transitionDuration: {}, transitionTimingFunction: {}, animation: {}, colors: {} };
1136
+ try {
1137
+ const spacingM = raw.match(/spacing\s*:\s*\{([\s\S]*?)\}/);
1138
+ if (spacingM) {
1139
+ const re = /["']?([\w.-]+)["']?\s*:\s*["']([^"']+)["']/g;
1140
+ let m;
1141
+ while ((m = re.exec(spacingM[1])) !== null) result.spacing[m[1]] = m[2];
1142
+ }
1143
+ const screensM = raw.match(/screens\s*:\s*\{([\s\S]*?)\}/);
1144
+ if (screensM) {
1145
+ const re = /["']?([\w.-]+)["']?\s*:\s*["']([^"']+)["']/g;
1146
+ let m;
1147
+ while ((m = re.exec(screensM[1])) !== null) result.breakpoints[m[1]] = m[2];
1148
+ }
1149
+ } catch (_) {}
1150
+ return result;
1151
+ }
1152
+
1153
+ /** Resolve Tailwind theme for boxShadow, spacing, screens, zIndex, motion. Uses resolveConfig for .js/.mjs, text-parse for .ts. */
1004
1154
  function getTailwindTheme() {
1005
1155
  const empty = { shadows: {}, spacing: {}, breakpoints: {}, zIndex: {}, transitionDuration: {}, transitionTimingFunction: {}, animation: {}, colors: {} };
1006
- try {
1007
- const resolveConfig = projectRequire("tailwindcss/resolveConfig");
1008
- let config = { content: [{ raw: "", extension: "html" }], theme: {} };
1009
- const twPath = path.join(PROJECT_ROOT, "tailwind.config.js");
1010
- const twPathMjs = path.join(PROJECT_ROOT, "tailwind.config.mjs");
1011
- if (fs.existsSync(twPath)) {
1012
- config = projectRequire(twPath);
1013
- if (config && typeof config === "object" && config.default) config = config.default;
1014
- } else if (fs.existsSync(twPathMjs)) {
1015
- config = projectRequire(twPathMjs);
1156
+ const twPath = path.join(PROJECT_ROOT, "tailwind.config.js");
1157
+ const twPathMjs = path.join(PROJECT_ROOT, "tailwind.config.mjs");
1158
+ const twPathTs = path.join(PROJECT_ROOT, "tailwind.config.ts");
1159
+
1160
+ if (fs.existsSync(twPath) || fs.existsSync(twPathMjs)) {
1161
+ try {
1162
+ const resolveConfig = projectRequire("tailwindcss/resolveConfig");
1163
+ const pathToUse = fs.existsSync(twPath) ? twPath : twPathMjs;
1164
+ let config = projectRequire(pathToUse);
1016
1165
  if (config && typeof config === "object" && config.default) config = config.default;
1166
+ const resolved = resolveConfig(config);
1167
+ const theme = resolved.theme || {};
1168
+ const toObj = (v) => (v && typeof v === "object" && !Array.isArray(v) ? v : {});
1169
+ return {
1170
+ shadows: toObj(theme.boxShadow),
1171
+ spacing: toObj(theme.spacing),
1172
+ breakpoints: toObj(theme.screens),
1173
+ zIndex: toObj(theme.zIndex),
1174
+ transitionDuration: toObj(theme.transitionDuration),
1175
+ transitionTimingFunction: toObj(theme.transitionTimingFunction),
1176
+ animation: toObj(theme.animation),
1177
+ colors: toObj(theme.colors),
1178
+ };
1179
+ } catch (_) {
1180
+ return empty;
1017
1181
  }
1018
- const resolved = resolveConfig(config);
1019
- const theme = resolved.theme || {};
1020
- const boxShadow = theme.boxShadow;
1021
- const spacing = theme.spacing;
1022
- const screens = theme.screens;
1023
- const zIndex = theme.zIndex;
1024
- const transitionDuration = theme.transitionDuration;
1025
- const transitionTimingFunction = theme.transitionTimingFunction;
1026
- const animation = theme.animation;
1027
- const themeColors = theme.colors;
1028
- const toObj = (v) => (v && typeof v === "object" && !Array.isArray(v) ? v : {});
1029
- return {
1030
- shadows: toObj(boxShadow),
1031
- spacing: toObj(spacing),
1032
- breakpoints: toObj(screens),
1033
- zIndex: toObj(zIndex),
1034
- transitionDuration: toObj(transitionDuration),
1035
- transitionTimingFunction: toObj(transitionTimingFunction),
1036
- animation: toObj(animation),
1037
- colors: toObj(themeColors),
1038
- };
1039
- } catch (_) {
1040
- return empty;
1041
1182
  }
1183
+
1184
+ if (fs.existsSync(twPathTs)) {
1185
+ try {
1186
+ const raw = fs.readFileSync(twPathTs, "utf-8");
1187
+ return parseTailwindThemeFromText(raw);
1188
+ } catch (_) {}
1189
+ }
1190
+
1191
+ return empty;
1042
1192
  }
1043
1193
 
1044
1194
  function extractFoundations() {
@@ -1364,11 +1514,10 @@ function extractButtonUsage() {
1364
1514
  }
1365
1515
 
1366
1516
  function scan() {
1367
- if (!fs.existsSync(COMPONENTS_DIR)) {
1368
- console.error(CLI_LOCALES[CLI_LOCALE].componentsNotFound);
1369
- process.exit(1);
1517
+ const relativeFiles = COMPONENTS_DIR ? getAllComponentFiles(COMPONENTS_DIR) : [];
1518
+ if (!COMPONENTS_DIR) {
1519
+ console.warn("[VDS] " + CLI_LOCALES[CLI_LOCALE].componentsNotFound);
1370
1520
  }
1371
- const relativeFiles = getAllComponentFiles(COMPONENTS_DIR);
1372
1521
  const results = [];
1373
1522
  for (const rel of relativeFiles) {
1374
1523
  const fullPath = path.join(COMPONENTS_DIR, rel);
@@ -1390,7 +1539,7 @@ function scan() {
1390
1539
  const tokens = extractTailwindTokens(content);
1391
1540
  results.push({ file: rel, name, group, category, description, tokens });
1392
1541
  }
1393
- if (fs.existsSync(PAGES_DIR)) {
1542
+ if (PAGES_DIR && fs.existsSync(PAGES_DIR)) {
1394
1543
  const pageFiles = getAllComponentFiles(PAGES_DIR);
1395
1544
  for (const rel of pageFiles) {
1396
1545
  const fullPath = path.join(PAGES_DIR, rel);
@@ -1398,7 +1547,7 @@ function scan() {
1398
1547
  const name = humanizeName(rel);
1399
1548
  const tokens = extractTailwindTokens(content);
1400
1549
  results.push({
1401
- file: "pages/" + rel,
1550
+ file: path.relative(PROJECT_ROOT, PAGES_DIR).replace(/\\/g, "/") + "/" + rel,
1402
1551
  name,
1403
1552
  group: "Pages",
1404
1553
  category: "Pages",
@@ -1548,10 +1697,7 @@ const isCompare = process.argv.includes("--compare");
1548
1697
  if (isCompare) {
1549
1698
  runCompare();
1550
1699
  } else if (isWatch) {
1551
- if (!fs.existsSync(COMPONENTS_DIR)) {
1552
- console.error(CLI_LOCALES[CLI_LOCALE].componentsNotFound);
1553
- process.exit(1);
1554
- }
1700
+ const watchDir = COMPONENTS_DIR || SRC_DIR;
1555
1701
  let debounceTimer = null;
1556
1702
  let lastChangedFiles = new Set();
1557
1703
 
@@ -1567,23 +1713,23 @@ if (isCompare) {
1567
1713
  }
1568
1714
  }
1569
1715
  scan();
1570
- console.log("[VDS] Watch: src/components (.tsx, .jsx). Scan complete.");
1716
+ console.log(`[VDS] Watch: ${path.relative(PROJECT_ROOT, watchDir)} (.tsx, .jsx). Scan complete.`);
1571
1717
  const url = vdsDashboardUrl();
1572
1718
  console.log("[VDS] Dashboard: " + formatClickableLink(url) + "\n");
1573
1719
  }
1574
1720
 
1575
1721
  function scheduleScan(filename) {
1576
1722
  if (!filename || !/\.(tsx|jsx)$/i.test(filename)) return;
1577
- lastChangedFiles.add(path.join(COMPONENTS_DIR, filename));
1723
+ lastChangedFiles.add(path.join(watchDir, filename));
1578
1724
  clearTimeout(debounceTimer);
1579
1725
  debounceTimer = setTimeout(runScan, 300);
1580
1726
  }
1581
1727
 
1582
- console.log("[VDS] Watching src/components for .tsx / .jsx changes…\n");
1728
+ console.log(`[VDS] Watching ${path.relative(PROJECT_ROOT, watchDir)} for .tsx / .jsx changes…\n`);
1583
1729
  runScan();
1584
1730
 
1585
1731
  try {
1586
- const watcher = fs.watch(COMPONENTS_DIR, { recursive: true }, (eventType, filename) => {
1732
+ const watcher = fs.watch(watchDir, { recursive: true }, (eventType, filename) => {
1587
1733
  if (filename) scheduleScan(filename);
1588
1734
  });
1589
1735
  process.on("SIGINT", () => {
@@ -17,6 +17,25 @@ const PROJECT_ROOT = path.join(__dirname, "..");
17
17
  const SRC_DIR = path.join(PROJECT_ROOT, "src");
18
18
  const STORYBOOK_DIR = path.join(PROJECT_ROOT, ".storybook");
19
19
 
20
+ // ── vds.config.js loader ──────────────────────────────────────────────────────
21
+ function loadVdsConfig() {
22
+ const configPath = path.join(PROJECT_ROOT, "vds.config.js");
23
+ if (!fs.existsSync(configPath)) return {};
24
+ try {
25
+ const raw = fs.readFileSync(configPath, "utf-8");
26
+ const mod = { exports: {} };
27
+ // eslint-disable-next-line no-new-func
28
+ new Function("module", "exports", "require", raw)(mod, mod.exports, () => ({}));
29
+ const cfg = mod.exports.default ?? mod.exports;
30
+ return typeof cfg === "object" && cfg !== null ? cfg : {};
31
+ } catch (e) {
32
+ console.warn("[VDS] vds.config.js yüklenemedi:", e.message);
33
+ return {};
34
+ }
35
+ }
36
+
37
+ const VDS_CONFIG = loadVdsConfig();
38
+
20
39
  const HOOK_TO_PROVIDER = {
21
40
  useTimer: "TimerProvider",
22
41
  useCircles: "CirclesProvider",
@@ -25,8 +44,42 @@ const HOOK_TO_PROVIDER = {
25
44
  useSearch: "SearchProvider",
26
45
  useCms: "CmsProvider",
27
46
  useContent: "ContentProvider",
47
+ ...(VDS_CONFIG.hookProviders ?? {}),
28
48
  };
29
49
 
50
+ const STANDARD_HOOKS = new Set([
51
+ "useState", "useEffect", "useCallback", "useMemo", "useRef", "useContext",
52
+ "useReducer", "useLayoutEffect", "useImperativeHandle", "useDebugValue",
53
+ "useDeferredValue", "useId", "useInsertionEffect", "useSyncExternalStore",
54
+ "useTransition", "useOptimistic", "useFormState", "useFormStatus", "useActionState",
55
+ "useNavigate", "useParams", "useLocation", "useMatch", "useSearchParams",
56
+ "useRouteError", "useLoaderData", "useActionData", "useSubmit", "useFetcher",
57
+ "useNavigation", "useRevalidator", "useRouteLoaderData", "useMatches", "useHref",
58
+ "useOutlet", "useOutletContext", "useResolvedPath", "useBlocker",
59
+ "useQuery", "useMutation", "useInfiniteQuery", "useQueryClient",
60
+ "useSuspenseQuery", "useSuspenseInfiniteQuery", "usePrefetchQuery",
61
+ "useIsFetching", "useIsMutating",
62
+ "useForm", "useFormContext", "useController", "useWatch", "useFieldArray",
63
+ "useStore", "useShallow",
64
+ "useSelector", "useDispatch",
65
+ "useTranslation", "useI18n", "useLocale",
66
+ "useArgs",
67
+ "useMotionValue", "useAnimate", "useAnimation", "useSpring", "useScroll",
68
+ "useInView", "useReducedMotion", "useWillChange", "useVelocity",
69
+ "useMediaQuery", "useBreakpoint", "useTheme",
70
+ "useToast", "useDisclosure", "useBoolean",
71
+ "useDraggable", "useDroppable", "useSortable",
72
+ "useVirtualizer", "useWindowVirtualizer",
73
+ "useHotkeys", "useEventListener", "useOnClickOutside",
74
+ "useLocalStorage", "useSessionStorage", "useCookies",
75
+ "useDebounce", "useThrottle", "useInterval", "useTimeout",
76
+ "useWindowSize", "useResizeObserver", "useIntersectionObserver",
77
+ "useCopyToClipboard", "useClipboard",
78
+ "useSWR", "useSWRMutation",
79
+ "useCombobox", "useSelect", "useMultipleSelection",
80
+ "useFloating", "useInteractions", "useMergeRefs",
81
+ ]);
82
+
30
83
  const IGNORE_DIRS = new Set(["node_modules", "dist", ".next", "build", ".storybook", "stories"]);
31
84
 
32
85
  function getAllSourceFiles(dir, baseDir = dir) {
@@ -48,9 +101,19 @@ function getAllSourceFiles(dir, baseDir = dir) {
48
101
 
49
102
  function detectHooksInFile(content) {
50
103
  const found = new Set();
104
+ // 1. Always check known/configured hook→provider map
51
105
  for (const hook of Object.keys(HOOK_TO_PROVIDER)) {
52
106
  if (new RegExp("\\b" + hook + "\\b").test(content)) found.add(hook);
53
107
  }
108
+ // 2. Auto-detect any custom hook call (use[A-Z]\w+) not in the standard exclusion list.
109
+ const callRe = /\buse([A-Z]\w*)\s*[(<]/g;
110
+ let m;
111
+ while ((m = callRe.exec(content)) !== null) {
112
+ const hookName = "use" + m[1];
113
+ if (!STANDARD_HOOKS.has(hookName) && !found.has(hookName)) {
114
+ found.add(hookName);
115
+ }
116
+ }
54
117
  return found;
55
118
  }
56
119
 
@@ -107,6 +170,40 @@ function getPreviewPath(projectRoot) {
107
170
  return null;
108
171
  }
109
172
 
173
+ // React Router hooks that require a Router wrapper in Storybook
174
+ const ROUTER_HOOKS = new Set([
175
+ "useLocation", "useNavigate", "useParams", "useSearchParams",
176
+ "useMatch", "useHref", "useResolvedPath", "useOutlet", "useOutletContext",
177
+ "useMatches", "useRouteError", "useNavigation", "useRevalidator",
178
+ ]);
179
+
180
+ function detectRouterPackage(projectRoot) {
181
+ const pkgPath = path.join(projectRoot, "package.json");
182
+ if (!fs.existsSync(pkgPath)) return null;
183
+ try {
184
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
185
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
186
+ if (deps["react-router-dom"]) return "react-router-dom";
187
+ if (deps["react-router"]) return "react-router";
188
+ } catch (_) {}
189
+ return null;
190
+ }
191
+
192
+ function projectUsesRouterHooks(projectRoot) {
193
+ const srcDir = path.join(projectRoot, "src");
194
+ if (!fs.existsSync(srcDir)) return false;
195
+ const files = getAllSourceFiles(srcDir, srcDir).map((r) => path.join(projectRoot, "src", r));
196
+ for (const file of files) {
197
+ try {
198
+ const content = fs.readFileSync(file, "utf-8");
199
+ for (const hook of ROUTER_HOOKS) {
200
+ if (new RegExp("\\b" + hook + "\\s*[(<(]").test(content)) return true;
201
+ }
202
+ } catch (_) {}
203
+ }
204
+ return false;
205
+ }
206
+
110
207
  /** Detect if project uses react-dnd (useDrag, useDrop, or import from react-dnd). */
111
208
  function detectReactDnd(projectRoot) {
112
209
  const srcDir = path.join(projectRoot, "src");
@@ -168,6 +265,12 @@ function collectProvidersAndWarnings(projectRoot) {
168
265
  hooksWithoutProvider.add(hook);
169
266
  }
170
267
  }
268
+ // MemoryRouter: wrap components that use React Router hooks
269
+ const routerPackage = detectRouterPackage(projectRoot);
270
+ if (routerPackage && projectUsesRouterHooks(projectRoot) && !providersToAdd.some((p) => p.name === "MemoryRouter")) {
271
+ providersToAdd.unshift({ name: "MemoryRouter", importPath: routerPackage, props: "{ initialEntries: [\"/\"] }" });
272
+ console.log("[VDS] React Router tespit edildi → MemoryRouter decorator ekleniyor.");
273
+ }
171
274
  if (detectReactDnd(projectRoot) && !providersToAdd.some((p) => p.name === "DndProvider")) {
172
275
  providersToAdd.unshift({
173
276
  name: "DndProvider",
@@ -25,8 +25,27 @@ let FOUNDATIONS_DATA = null;
25
25
 
26
26
  // CSS is loaded from .storybook/preview.tsx — never add CSS import to story files.
27
27
 
28
+ // --- vds.config.js loader ---
29
+ function loadVdsConfig() {
30
+ const configPath = path.join(PROJECT_ROOT, "vds.config.js");
31
+ if (!fs.existsSync(configPath)) return {};
32
+ try {
33
+ const raw = fs.readFileSync(configPath, "utf-8");
34
+ const mod = { exports: {} };
35
+ // eslint-disable-next-line no-new-func
36
+ new Function("module", "exports", "require", raw)(mod, mod.exports, () => ({}));
37
+ const cfg = mod.exports.default ?? mod.exports;
38
+ return typeof cfg === "object" && cfg !== null ? cfg : {};
39
+ } catch (e) {
40
+ console.warn("[VDS] vds.config.js yüklenemedi:", e.message);
41
+ return {};
42
+ }
43
+ }
44
+
45
+ const VDS_CONFIG = loadVdsConfig();
46
+
28
47
  // Components we don't want to auto-generate stories for (project-specific dashboards, heavy UIs, etc.)
29
- const SKIP_LIST = [
48
+ const DEFAULT_SKIP_LIST = [
30
49
  "AnalysisDashboard",
31
50
  "ComponentLibrary",
32
51
  "EnterprisePushPanel",
@@ -54,6 +73,12 @@ const SKIP_LIST = [
54
73
  "Button",
55
74
  ];
56
75
 
76
+ // Merge default with project-specific overrides from vds.config.js
77
+ // vds.config.js can set: skipList (array, replaces default) or extraSkipList (array, appends)
78
+ const SKIP_LIST = VDS_CONFIG.skipList
79
+ ? [...VDS_CONFIG.skipList]
80
+ : [...DEFAULT_SKIP_LIST, ...(VDS_CONFIG.extraSkipList ?? [])];
81
+
57
82
  /** shadcn/ui composite component recipes: component name → imports + render. */
58
83
  const RECIPES = {
59
84
  Accordion: {
@@ -1,10 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import { watch } from 'fs';
2
+ import { watch, existsSync } from 'fs';
3
3
  import { execSync } from 'child_process';
4
4
  import { resolve } from 'path';
5
5
 
6
6
  const ROOT = resolve(process.cwd());
7
- const SRC = resolve(ROOT, 'src');
7
+
8
+ // Watch the first directory that exists: src/, app/, or project root
9
+ function resolveWatchDir() {
10
+ for (const candidate of ['src', 'app']) {
11
+ const full = resolve(ROOT, candidate);
12
+ if (existsSync(full)) return full;
13
+ }
14
+ return ROOT;
15
+ }
16
+
17
+ const WATCH_DIR = resolveWatchDir();
8
18
  let timeout = null;
9
19
 
10
20
  function run() {
@@ -14,7 +24,9 @@ function run() {
14
24
  execSync('node vds-core/scan.mjs', { stdio: 'inherit', cwd: ROOT });
15
25
  execSync('node vds-core/story-generator.mjs', { stdio: 'inherit', cwd: ROOT });
16
26
  execSync('node vds-core/setup-storybook-providers.mjs', { stdio: 'inherit', cwd: ROOT });
17
- execSync('node vds-core/storybook-adapt.mjs', { stdio: 'inherit', cwd: ROOT });
27
+ if (existsSync(resolve(ROOT, 'vds-core/storybook-adapt.mjs'))) {
28
+ execSync('node vds-core/storybook-adapt.mjs', { stdio: 'inherit', cwd: ROOT });
29
+ }
18
30
  console.log(`[VDS ${ts}] ✅ Design system güncellendi.`);
19
31
  } catch (e) {
20
32
  console.error(`[VDS ${ts}] ❌ Hata:`, e.message);
@@ -25,7 +37,7 @@ function onFileChange(eventType, filename) {
25
37
  if (!filename) return;
26
38
  if (filename.includes('stories/')) return;
27
39
  if (filename.includes('vds-output')) return;
28
- if (!filename.endsWith('.tsx') && !filename.endsWith('.ts') &&
40
+ if (!filename.endsWith('.tsx') && !filename.endsWith('.ts') &&
29
41
  !filename.endsWith('.css') && !filename.endsWith('.js')) return;
30
42
  console.log(`[VDS] Değişiklik: ${filename}`);
31
43
  clearTimeout(timeout);
@@ -33,6 +45,6 @@ function onFileChange(eventType, filename) {
33
45
  }
34
46
 
35
47
  run();
36
- console.log('[VDS] 👀 src/ klasörü izleniyor...');
48
+ console.log(`[VDS] 👀 ${WATCH_DIR.replace(ROOT + '/', '')}/ klasörü izleniyor...`);
37
49
  console.log('[VDS] Durdurmak için Ctrl+C\n');
38
- watch(SRC, { recursive: true }, onFileChange);
50
+ watch(WATCH_DIR, { recursive: true }, onFileChange);