vibe-design-system 1.9.12 → 2.0.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
@@ -1,14 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * VDS installer: npx vibe-design-system init
3
+ * VDS init: npx vibe-design-system init
4
4
  *
5
- * Single command that:
6
- * 1. Copies vds-core files (scan.mjs, story-generator.mjs, dashboard-server.mjs)
7
- * 2. Installs Storybook if not present (npx storybook@latest init --yes)
8
- * 3. Creates/updates .storybook/preview with index.css import and dark decorator
9
- * 4. Runs node vds-core/scan.mjs
10
- * 5. Runs node vds-core/story-generator.mjs
11
- * 6. Adds all scripts to package.json
5
+ * ADIM 1 — Bağımlılık kontrolü (storybook, @storybook/react-vite)
6
+ * ADIM 2 .storybook/ (main.ts, preview.ts)
7
+ * ADIM 3 vds-core/ kopyala
8
+ * ADIM 4 package.json scriptleri
9
+ * ADIM 5 .cursorrules
10
+ * ADIM 6 src/stories/
11
+ * ADIM 7 İlk tarama (scan + story-generator)
12
+ * ADIM 8 — Başarı mesajı
13
+ * ADIM 9 — Storybook örnek dosyalarını sil
12
14
  */
13
15
  import fs from "fs";
14
16
  import path from "path";
@@ -19,125 +21,230 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
21
  const INSTALLER_ROOT = path.join(__dirname, "..");
20
22
  const TEMPLATE_DIR = path.join(INSTALLER_ROOT, "vds-core-template");
21
23
 
22
- function getProjectRoot() {
23
- const cwd = process.cwd();
24
+ const STORYBOOK_MAIN_TS = `import type { StorybookConfig } from "@storybook/react-vite";
25
+ const config: StorybookConfig = {
26
+ stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
27
+ addons: ["@storybook/addon-essentials"],
28
+ framework: {
29
+ name: "@storybook/react-vite",
30
+ options: {},
31
+ },
32
+ };
33
+ export default config;
34
+ `;
35
+
36
+ const STORYBOOK_PREVIEW_TS = `import type { Preview } from "@storybook/react";
37
+ import "../src/index.css";
38
+ const preview: Preview = { parameters: { layout: "centered" } };
39
+ export default preview;
40
+ `;
41
+
42
+ const CURSORRULES = `# VDS — Vibe Design System
43
+ Bu proje VDS kullanıyor. Komutlar:
44
+ - npm run vds → projeyi tara
45
+ - npm run vds:stories → Storybook story'lerini üret
46
+ - npm run vds:watch → değişiklikleri izle
47
+ - npm run storybook → design system'ı aç (localhost:6006)
48
+
49
+ Geliştirme kuralları:
50
+ - UI component'leri src/components/ui/ altına koy
51
+ - Variant'lar için cva() kullan
52
+ - Tailwind CSS kullan
53
+ - Named export veya default export — ikisi de çalışır
54
+ `;
55
+
56
+ const WATCH_MJS = `#!/usr/bin/env node
57
+ import { watch } from 'fs';
58
+ import { execSync } from 'child_process';
59
+ import { resolve } from 'path';
60
+
61
+ const ROOT = resolve(process.cwd());
62
+ const SRC = resolve(ROOT, 'src');
63
+ let timeout = null;
64
+
65
+ function run() {
66
+ const ts = new Date().toLocaleTimeString();
67
+ console.log(\`\\n[VDS \${ts}] Değişiklik algılandı, taranıyor...\`);
24
68
  try {
25
- const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf-8"));
26
- if (pkg.name === "vibe-design-system") return path.join(cwd, "..");
27
- } catch (_) {}
28
- return cwd;
69
+ execSync('node vds-core/scan.mjs', { stdio: 'inherit', cwd: ROOT });
70
+ execSync('node vds-core/story-generator.mjs', { stdio: 'inherit', cwd: ROOT });
71
+ console.log(\`[VDS \${ts}] Design system güncellendi.\`);
72
+ } catch (e) {
73
+ console.error(\`[VDS \${ts}] ❌ Hata:\`, e.message);
74
+ }
29
75
  }
30
76
 
31
- function isStorybookInstalled(projectRoot) {
32
- const storybookDir = path.join(projectRoot, ".storybook");
33
- if (fs.existsSync(storybookDir)) return true;
77
+ function onFileChange(eventType, filename) {
78
+ if (!filename) return;
79
+ if (filename.includes('stories/')) return;
80
+ if (filename.includes('vds-output')) return;
81
+ if (!filename.endsWith('.tsx') && !filename.endsWith('.ts') &&
82
+ !filename.endsWith('.css') && !filename.endsWith('.js')) return;
83
+ console.log(\`[VDS] Değişiklik: \${filename}\`);
84
+ clearTimeout(timeout);
85
+ timeout = setTimeout(run, 2000);
86
+ }
87
+
88
+ run();
89
+ console.log('[VDS] 👀 src/ klasörü izleniyor...');
90
+ console.log('[VDS] Durdurmak için Ctrl+C\\n');
91
+ watch(SRC, { recursive: true }, onFileChange);
92
+ `;
93
+
94
+ const STORYBOOK_EXAMPLE_FILES = [
95
+ "Button.stories.js",
96
+ "Header.stories.js",
97
+ "Page.stories.js",
98
+ "Button.jsx",
99
+ "Header.jsx",
100
+ "Page.jsx",
101
+ ];
102
+
103
+ function getProjectRoot() {
104
+ return process.cwd();
105
+ }
106
+
107
+ function readPackageJson(projectRoot) {
34
108
  const pkgPath = path.join(projectRoot, "package.json");
109
+ if (!fs.existsSync(pkgPath)) return null;
35
110
  try {
36
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
37
- const dev = pkg.devDependencies || {};
38
- if (dev["@storybook/react-vite"] || dev["storybook"]) return true;
39
- } catch (_) {}
40
- return false;
111
+ return JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
112
+ } catch (_) {
113
+ return null;
114
+ }
115
+ }
116
+
117
+ // ADIM 1 — Bağımlılık kontrolü
118
+ function needsStorybook(projectRoot) {
119
+ const pkg = readPackageJson(projectRoot);
120
+ if (!pkg) return false;
121
+ const dev = pkg.devDependencies || {};
122
+ return !(dev.storybook && dev["@storybook/react-vite"]);
41
123
  }
42
124
 
43
125
  function installStorybook(projectRoot) {
44
- console.log("📚 Storybook kuruluyor...");
45
- const r = spawnSync("npx", ["storybook@latest", "init", "--yes"], {
46
- cwd: projectRoot,
47
- stdio: "inherit",
48
- shell: true,
49
- });
126
+ console.log("📚 Storybook bağımlılıkları kuruluyor...");
127
+ const r = spawnSync(
128
+ "npm",
129
+ ["install", "--save-dev", "storybook", "@storybook/react", "@storybook/react-vite"],
130
+ { cwd: projectRoot, stdio: "inherit" }
131
+ );
50
132
  if (r.status !== 0) {
51
- console.warn("⚠️ Storybook init tamamlanamadı; manuel: npx storybook@latest init");
133
+ console.warn("⚠️ npm install storybook tamamlanamadı.");
52
134
  }
53
135
  }
54
136
 
55
- const PREVIEW_CONTENT = `import type { Preview } from "@storybook/react-vite";
56
- import React from "react";
57
- import "../src/index.css";
137
+ // ADIM 2 .storybook/
138
+ function ensureStorybook(projectRoot) {
139
+ const storybookDir = path.join(projectRoot, ".storybook");
140
+ if (!fs.existsSync(storybookDir)) {
141
+ fs.mkdirSync(storybookDir, { recursive: true });
142
+ console.log("📁 .storybook/ oluşturuldu.");
143
+ }
58
144
 
59
- const preview: Preview = {
60
- parameters: {
61
- controls: {
62
- matchers: {
63
- color: /(background|color)$/i,
64
- date: /Date$/i,
65
- },
66
- },
67
- },
68
- decorators: [
69
- (Story) => (
70
- <div className="dark" style={{ minHeight: "100vh", padding: "1rem" }}>
71
- <Story />
72
- </div>
73
- ),
74
- ],
75
- };
145
+ const mainPath = path.join(storybookDir, "main.ts");
146
+ if (!fs.existsSync(mainPath)) {
147
+ fs.writeFileSync(mainPath, STORYBOOK_MAIN_TS, "utf-8");
148
+ console.log("📝 .storybook/main.ts yazıldı.");
149
+ }
76
150
 
77
- export default preview;
78
- `;
151
+ const previewPath = path.join(storybookDir, "preview.ts");
152
+ if (!fs.existsSync(previewPath)) {
153
+ fs.writeFileSync(previewPath, STORYBOOK_PREVIEW_TS, "utf-8");
154
+ console.log("📝 .storybook/preview.ts yazıldı.");
155
+ }
156
+ }
79
157
 
80
- function ensureStorybookPreview(projectRoot) {
81
- const storybookDir = path.join(projectRoot, ".storybook");
82
- if (!fs.existsSync(storybookDir)) fs.mkdirSync(storybookDir, { recursive: true });
158
+ // ADIM 3 — vds-core/ kopyala
159
+ function copyDirRecursive(src, dest) {
160
+ if (!fs.existsSync(src)) return;
161
+ if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
162
+ for (const name of fs.readdirSync(src)) {
163
+ const srcPath = path.join(src, name);
164
+ const destPath = path.join(dest, name);
165
+ if (fs.statSync(srcPath).isDirectory()) {
166
+ copyDirRecursive(srcPath, destPath);
167
+ } else {
168
+ fs.copyFileSync(srcPath, destPath);
169
+ }
170
+ }
171
+ }
83
172
 
84
- const previewTsx = path.join(storybookDir, "preview.tsx");
85
- const previewTs = path.join(storybookDir, "preview.ts");
86
- if (fs.existsSync(previewTs)) try { fs.unlinkSync(previewTs); } catch (_) {}
87
- fs.writeFileSync(previewTsx, PREVIEW_CONTENT, "utf-8");
88
- console.log("📝 .storybook/preview.tsx güncellendi (index.css + dark decorator).");
173
+ function ensureVdsCore(projectRoot) {
174
+ const vdsCoreDest = path.join(projectRoot, "vds-core");
175
+ if (!fs.existsSync(vdsCoreDest)) {
176
+ fs.mkdirSync(vdsCoreDest, { recursive: true });
177
+ }
178
+ copyDirRecursive(TEMPLATE_DIR, vdsCoreDest);
179
+ const watchPath = path.join(vdsCoreDest, "watch.mjs");
180
+ if (!fs.existsSync(watchPath)) {
181
+ fs.writeFileSync(watchPath, WATCH_MJS, "utf-8");
182
+ }
183
+ console.log("📦 vds-core/ hazır.");
89
184
  }
90
185
 
186
+ // ADIM 4 — package.json scriptleri
91
187
  function addScripts(projectRoot) {
92
188
  const pkgPath = path.join(projectRoot, "package.json");
93
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
189
+ const pkg = readPackageJson(projectRoot);
190
+ if (!pkg) return;
94
191
  const scripts = pkg.scripts || {};
95
- if (!scripts.vds) scripts.vds = "node vds-core/scan.mjs";
96
- if (!scripts["vds:watch"]) scripts["vds:watch"] = "node vds-core/scan.mjs --watch";
97
- if (!scripts["vds:dashboard"]) scripts["vds:dashboard"] = "node vds-core/dashboard-server.mjs";
98
- if (!scripts["vds:stories"]) scripts["vds:stories"] = "node vds-core/story-generator.mjs";
99
- if (!scripts.storybook) scripts.storybook = "storybook dev -p 6006";
100
- if (!scripts["build-storybook"]) scripts["build-storybook"] = "storybook build";
101
- pkg.scripts = scripts;
102
- fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2), "utf-8");
103
- }
104
-
105
- function addVdsDependency(projectRoot) {
106
- const pkgPath = path.join(projectRoot, "package.json");
107
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
108
- const dev = pkg.devDependencies || {};
109
- if (!dev["vibe-design-system"]) {
110
- dev["vibe-design-system"] = "^1.9.0";
111
- pkg.devDependencies = dev;
192
+ let changed = false;
193
+ if (!scripts.vds) {
194
+ scripts.vds = "node vds-core/scan.mjs";
195
+ changed = true;
196
+ }
197
+ if (!scripts["vds:stories"]) {
198
+ scripts["vds:stories"] = "node vds-core/story-generator.mjs";
199
+ changed = true;
200
+ }
201
+ if (!scripts["vds:watch"]) {
202
+ scripts["vds:watch"] = "node vds-core/watch.mjs";
203
+ changed = true;
204
+ }
205
+ if (!scripts.storybook) {
206
+ scripts.storybook = "storybook dev -p 6006";
207
+ changed = true;
208
+ }
209
+ if (!scripts["build-storybook"]) {
210
+ scripts["build-storybook"] = "storybook build";
211
+ changed = true;
212
+ }
213
+ if (changed) {
214
+ pkg.scripts = scripts;
112
215
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2), "utf-8");
113
- const r = spawnSync("npm", ["install"], { cwd: projectRoot, stdio: "inherit", shell: true });
114
- if (r.status !== 0) console.warn("npm install uyarı verdi; manuel çalıştırın: npm install");
216
+ console.log("📜 package.json scriptleri eklendi.");
115
217
  }
116
218
  }
117
219
 
118
- function ensureVdsCore(projectRoot) {
119
- const vdsCoreDest = path.join(projectRoot, "vds-core");
120
- if (!fs.existsSync(vdsCoreDest)) fs.mkdirSync(vdsCoreDest, { recursive: true });
220
+ // ADIM 5 — .cursorrules
221
+ function ensureCursorrules(projectRoot) {
222
+ const p = path.join(projectRoot, ".cursorrules");
223
+ fs.writeFileSync(p, CURSORRULES, "utf-8");
224
+ console.log("📄 .cursorrules yazıldı.");
225
+ }
121
226
 
122
- const scanSrc = path.join(TEMPLATE_DIR, "scan.mjs");
123
- if (fs.existsSync(scanSrc)) {
124
- fs.copyFileSync(scanSrc, path.join(vdsCoreDest, "scan.mjs"));
125
- } else {
126
- console.error("vds-core-template/scan.mjs bulunamadı.");
127
- process.exit(1);
227
+ // ADIM 6 — src/stories/ (ve scan için gerekli src/components, src/pages)
228
+ function ensureStoriesDir(projectRoot) {
229
+ const srcDir = path.join(projectRoot, "src");
230
+ if (!fs.existsSync(srcDir)) fs.mkdirSync(srcDir, { recursive: true });
231
+ const storiesDir = path.join(srcDir, "stories");
232
+ if (!fs.existsSync(storiesDir)) {
233
+ fs.mkdirSync(storiesDir, { recursive: true });
234
+ console.log("📁 src/stories/ oluşturuldu.");
128
235
  }
129
-
130
- const serverSrc = path.join(TEMPLATE_DIR, "dashboard-server.mjs");
131
- if (fs.existsSync(serverSrc)) {
132
- fs.copyFileSync(serverSrc, path.join(vdsCoreDest, "dashboard-server.mjs"));
236
+ const componentsDir = path.join(srcDir, "components");
237
+ if (!fs.existsSync(componentsDir)) {
238
+ fs.mkdirSync(componentsDir, { recursive: true });
239
+ console.log("📁 src/components/ oluşturuldu (VDS taraması için).");
133
240
  }
134
-
135
- const storyGenSrc = path.join(TEMPLATE_DIR, "story-generator.mjs");
136
- if (fs.existsSync(storyGenSrc)) {
137
- fs.copyFileSync(storyGenSrc, path.join(vdsCoreDest, "story-generator.mjs"));
241
+ const pagesDir = path.join(srcDir, "pages");
242
+ if (!fs.existsSync(pagesDir)) {
243
+ fs.mkdirSync(pagesDir, { recursive: true });
138
244
  }
139
245
  }
140
246
 
247
+ // ADIM 7 — İlk tarama
141
248
  function runScan(projectRoot) {
142
249
  const scanPath = path.join(projectRoot, "vds-core", "scan.mjs");
143
250
  if (!fs.existsSync(scanPath)) return;
@@ -162,39 +269,61 @@ function runStoryGenerator(projectRoot) {
162
269
  if (r.status !== 0) process.exitCode = r.status ?? 1;
163
270
  }
164
271
 
165
- console.log("🎨 VDS kuruluyor...");
272
+ // ADIM 9 — Storybook örnek dosyalarını sil
273
+ function removeStorybookExamples(projectRoot) {
274
+ const storiesDir = path.join(projectRoot, "src", "stories");
275
+ if (!fs.existsSync(storiesDir)) return;
276
+ for (const name of STORYBOOK_EXAMPLE_FILES) {
277
+ const filePath = path.join(storiesDir, name);
278
+ if (fs.existsSync(filePath)) {
279
+ try {
280
+ fs.unlinkSync(filePath);
281
+ console.log("🗑️ Silindi: src/stories/" + name);
282
+ } catch (_) {}
283
+ }
284
+ }
285
+ }
286
+
287
+ // ——— main ———
288
+ console.log("🎨 VDS kuruluyor...\n");
166
289
 
167
290
  const projectRoot = getProjectRoot();
168
- const pkgPath = path.join(projectRoot, "package.json");
169
- if (!fs.existsSync(pkgPath)) {
291
+ const pkg = readPackageJson(projectRoot);
292
+ if (!pkg) {
170
293
  console.error("❌ Bu klasörde package.json bulunamadı.");
171
- console.error("VDS, mevcut bir React projesine kurulmalıdır.");
172
294
  process.exit(1);
173
295
  }
174
296
 
175
- // 1. Copy vds-core files
176
- ensureVdsCore(projectRoot);
177
-
178
- // 2. Install Storybook if not present
179
- if (!isStorybookInstalled(projectRoot)) {
297
+ // ADIM 1
298
+ if (needsStorybook(projectRoot)) {
180
299
  installStorybook(projectRoot);
181
300
  } else {
182
- console.log("📚 Storybook zaten mevcut.");
301
+ console.log("📚 Storybook zaten yüklü.");
183
302
  }
184
303
 
185
- // 3. Create/update .storybook/preview with index.css + dark decorator
186
- ensureStorybookPreview(projectRoot);
304
+ // ADIM 2
305
+ ensureStorybook(projectRoot);
306
+
307
+ // ADIM 3
308
+ ensureVdsCore(projectRoot);
187
309
 
188
- // 6. Add all scripts to package.json (before scan so scripts are there)
310
+ // ADIM 4
189
311
  addScripts(projectRoot);
190
- addVdsDependency(projectRoot);
191
312
 
192
- // 4. Run scan
193
- runScan(projectRoot);
313
+ // ADIM 5
314
+ ensureCursorrules(projectRoot);
315
+
316
+ // ADIM 6
317
+ ensureStoriesDir(projectRoot);
194
318
 
195
- // 5. Run story generator
319
+ // ADIM 9 (örnek dosyaları taramadan önce silebiliriz; scan src/components ve src/pages tarar, src/stories değil)
320
+ removeStorybookExamples(projectRoot);
321
+
322
+ // ADIM 7
323
+ runScan(projectRoot);
196
324
  runStoryGenerator(projectRoot);
197
325
 
198
- console.log("✅ Kurulum tamamlandı.");
199
- console.log(" Storybook: npm run storybook");
200
- console.log(" Dashboard: npm run vds:dashboard");
326
+ // ADIM 8
327
+ console.log("\n✅ VDS kuruldu!");
328
+ console.log(" npm run storybook ile design system'ını aç");
329
+ console.log("→ npm run vds:watch ile otomatik güncellemeyi başlat\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-design-system",
3
- "version": "1.9.12",
3
+ "version": "2.0.1",
4
4
  "description": "Auto-generate design systems for vibe coding projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -92,6 +92,106 @@ function readPreviousOutput() {
92
92
  return null;
93
93
  }
94
94
 
95
+ function readPreviousOutputFull() {
96
+ try {
97
+ if (fs.existsSync(OUTPUT_FILE)) {
98
+ const raw = fs.readFileSync(OUTPUT_FILE, "utf-8");
99
+ return JSON.parse(raw);
100
+ }
101
+ } catch (_) {}
102
+ return null;
103
+ }
104
+
105
+ const CHANGELOG_MAX_ENTRIES = 20;
106
+
107
+ function getColorHex(val) {
108
+ if (val == null) return null;
109
+ if (typeof val === "string") return val;
110
+ if (typeof val === "object" && (val.hex || val.value)) return val.hex || val.value;
111
+ return null;
112
+ }
113
+
114
+ function compareColors(prevColors, newColors) {
115
+ const changes = [];
116
+ if (!prevColors && !newColors) return changes;
117
+ const prev = prevColors || {};
118
+ const next = newColors || {};
119
+ const allKeys = new Set([...Object.keys(prev), ...Object.keys(next)]);
120
+ for (const key of allKeys) {
121
+ if (key === "_dark") continue;
122
+ const fromHex = getColorHex(prev[key]);
123
+ const toHex = getColorHex(next[key]);
124
+ if (toHex == null) {
125
+ if (fromHex != null) changes.push({ type: "color_removed", name: key });
126
+ continue;
127
+ }
128
+ if (fromHex == null) {
129
+ changes.push({ type: "color_added", name: key, to: toHex });
130
+ continue;
131
+ }
132
+ if (fromHex !== toHex) {
133
+ changes.push({ type: "color_changed", name: key, from: fromHex, to: toHex });
134
+ }
135
+ }
136
+ if (next._dark && typeof next._dark === "object") {
137
+ const prevDark = prev._dark || {};
138
+ for (const key of Object.keys(next._dark)) {
139
+ const fromHex = getColorHex(prevDark[key]);
140
+ const toHex = getColorHex(next._dark[key]);
141
+ if (fromHex != null && toHex != null && fromHex !== toHex) {
142
+ changes.push({ type: "color_changed", name: `_dark.${key}`, from: fromHex, to: toHex });
143
+ } else if (fromHex == null && toHex != null) {
144
+ changes.push({ type: "color_added", name: `_dark.${key}`, to: toHex });
145
+ } else if (fromHex != null && toHex == null) {
146
+ changes.push({ type: "color_removed", name: `_dark.${key}` });
147
+ }
148
+ }
149
+ }
150
+ return changes;
151
+ }
152
+
153
+ function compareTypography(prevTypo, newTypo) {
154
+ const changes = [];
155
+ if (!prevTypo && !newTypo) return changes;
156
+ const prev = prevTypo || {};
157
+ const next = newTypo || {};
158
+ const allKeys = new Set([...Object.keys(prev), ...Object.keys(next)]);
159
+ for (const key of allKeys) {
160
+ const fromVal = prev[key] != null ? String(prev[key]) : null;
161
+ const toVal = next[key] != null ? String(next[key]) : null;
162
+ if (fromVal === toVal) continue;
163
+ if (toVal == null) continue;
164
+ changes.push({
165
+ type: "typography_changed",
166
+ key,
167
+ from: fromVal || "(none)",
168
+ to: toVal,
169
+ });
170
+ }
171
+ return changes;
172
+ }
173
+
174
+ function buildChangelogChanges(prevOutput, newOutput, added, removed) {
175
+ const changes = [];
176
+ const prevFoundations = prevOutput?.foundations;
177
+ const newFoundations = newOutput?.foundations;
178
+
179
+ const colorChanges = compareColors(prevFoundations?.colors, newFoundations?.colors);
180
+ changes.push(...colorChanges);
181
+
182
+ const typoChanges = compareTypography(prevFoundations?.typography, newFoundations?.typography);
183
+ changes.push(...typoChanges);
184
+
185
+ for (const a of added) {
186
+ changes.push({ type: "component_added", name: a.name });
187
+ }
188
+ for (const r of removed) {
189
+ changes.push({ type: "component_removed", name: r.name });
190
+ }
191
+
192
+ return changes;
193
+ }
194
+
95
195
  function readLastVersion() {
96
196
  try {
97
197
  if (fs.existsSync(HISTORY_FILE)) {
@@ -760,21 +860,37 @@ function scan() {
760
860
  componentSuggestions,
761
861
  };
762
862
 
763
- const prevComponents = readPreviousOutput();
863
+ const prevOutput = readPreviousOutputFull();
864
+ const prevComponents = prevOutput?.components ?? readPreviousOutput();
764
865
  const { added, removed, modified } = compareComponents(prevComponents, results);
765
866
  const hasChanges = added.length > 0 || removed.length > 0 || modified.length > 0;
766
867
 
868
+ const prevVersion = prevOutput?.version || readLastVersion() || "1.0.0";
869
+ const changelogChanges = buildChangelogChanges(prevOutput, output, added, removed);
870
+ const prevChangelog = Array.isArray(prevOutput?.changelog) ? prevOutput.changelog : [];
871
+ if (changelogChanges.length > 0) {
872
+ const nextVersion = incPatch(prevVersion);
873
+ const changelogEntry = {
874
+ version: nextVersion,
875
+ date: new Date().toISOString(),
876
+ changes: changelogChanges,
877
+ };
878
+ output.changelog = prevChangelog.concat(changelogEntry).slice(-CHANGELOG_MAX_ENTRIES);
879
+ output.version = nextVersion;
880
+ } else {
881
+ output.changelog = prevChangelog;
882
+ output.version = prevVersion;
883
+ }
884
+
767
885
  fs.writeFileSync(OUTPUT_FILE, JSON.stringify(output, null, 2), "utf-8");
768
886
  const publicDir = path.join(PROJECT_ROOT, "public");
769
887
  if (!fs.existsSync(publicDir)) fs.mkdirSync(publicDir, { recursive: true });
770
888
  fs.writeFileSync(PUBLIC_MANIFEST, JSON.stringify(output, null, 2), "utf-8");
771
889
 
772
890
  if (hasChanges) {
773
- const lastVersion = readLastVersion();
774
- const nextVersion = incPatch(lastVersion);
775
891
  const changes = [...added, ...removed, ...modified];
776
892
  const historyEntry = {
777
- version: nextVersion,
893
+ version: output.version,
778
894
  timestamp: new Date().toISOString(),
779
895
  branch: getGitBranch(),
780
896
  engineer: getGitEngineer(),
@@ -784,7 +900,7 @@ function scan() {
784
900
  entries.push(historyEntry);
785
901
  fs.writeFileSync(HISTORY_FILE, JSON.stringify(entries, null, 2), "utf-8");
786
902
  console.log(
787
- `[VDS] v${nextVersion}: ${added.length} added, ${removed.length} removed, ${modified.length} modified`
903
+ `[VDS] v${output.version}: ${added.length} added, ${removed.length} removed, ${modified.length} modified`
788
904
  );
789
905
  console.log(cliT("scanComplete", results.length));
790
906
  }
@@ -813,6 +813,65 @@ function writeComponentSuggestionsStory(componentSuggestions) {
813
813
  console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "ComponentSuggestions.stories.tsx")));
814
814
  }
815
815
 
816
+ function writeChangelogStory(changelog) {
817
+ if (!Array.isArray(changelog) || changelog.length === 0) return;
818
+ const foundationsDir = path.join(STORIES_DIR, "foundations");
819
+ ensureDir(foundationsDir);
820
+ const entries = changelog.map((e) => ({
821
+ version: e.version,
822
+ date: e.date,
823
+ changes: e.changes || [],
824
+ }));
825
+ const content =
826
+ [
827
+ "import type { Meta, StoryObj } from \"@storybook/react\";",
828
+ "",
829
+ "const meta = { title: \"Foundations/Changelog\" } satisfies Meta;",
830
+ "export default meta;",
831
+ "type Story = StoryObj;",
832
+ "",
833
+ `const changelog = ${JSON.stringify(entries)};`,
834
+ "",
835
+ "function ChangeItem({ c }) {",
836
+ " if (c.type === \"color_changed\")",
837
+ " return (",
838
+ " <div style={{ marginBottom: 8 }}>",
839
+ " <strong>{c.name}</strong>: <span style={{ color: \"#888\" }}>{c.from}</span> → <span style={{ color: \"#0f0\" }}>{c.to}</span>",
840
+ " <span style={{ display: \"inline-block\", width: 16, height: 16, backgroundColor: c.from, marginLeft: 8, border: \"1px solid #333\" }} />",
841
+ " <span style={{ display: \"inline-block\", width: 16, height: 16, backgroundColor: c.to, marginLeft: 4, border: \"1px solid #333\" }} />",
842
+ " </div>",
843
+ " );",
844
+ " if (c.type === \"color_added\") return <div style={{ marginBottom: 8 }}><strong>{c.name}</strong> added: <code>{c.to}</code> <span style={{ display: \"inline-block\", width: 16, height: 16, backgroundColor: c.to, marginLeft: 4, border: \"1px solid #333\" }} /></div>;",
845
+ " if (c.type === \"color_removed\") return <div style={{ marginBottom: 8, color: \"#888\" }}><strong>{c.name}</strong> removed</div>;",
846
+ " if (c.type === \"typography_changed\") return <div style={{ marginBottom: 8 }}><strong>{c.key}</strong>: {c.from} → {c.to}</div>;",
847
+ " if (c.type === \"component_added\") return <div style={{ marginBottom: 8, color: \"#0f0\" }}>+ {c.name}</div>;",
848
+ " if (c.type === \"component_removed\") return <div style={{ marginBottom: 8, color: \"#f88\" }}>− {c.name}</div>;",
849
+ " return <div style={{ marginBottom: 8 }}>{c.type}: {JSON.stringify(c)}</div>;",
850
+ "}",
851
+ "",
852
+ "export const Default: Story = {",
853
+ " render: () => (",
854
+ " <div style={{ padding: 24, fontFamily: \"system-ui, sans-serif\" }}>",
855
+ " <h2 style={{ marginBottom: 16 }}>VDS Changelog</h2>",
856
+ " <div style={{ display: \"flex\", flexDirection: \"column\", gap: 24 }}>",
857
+ " {changelog.map((e, i) => (",
858
+ " <div key={i} style={{ border: \"1px solid #333\", borderRadius: 8, padding: 16, background: \"#111\" }}>",
859
+ " <div style={{ fontWeight: 600, marginBottom: 4 }}>v{e.version}</div>",
860
+ " <div style={{ fontSize: 12, color: \"#888\", marginBottom: 12 }}>{e.date}</div>",
861
+ " {e.changes.length === 0 ? <div style={{ color: \"#666\" }}>No changes</div> : (",
862
+ " <div>{e.changes.map((c, j) => <ChangeItem key={j} c={c} />)}</div>",
863
+ " )}",
864
+ " </div>",
865
+ " ))}",
866
+ " </div>",
867
+ " </div>",
868
+ " ),",
869
+ "};",
870
+ ].join("\n");
871
+ fs.writeFileSync(path.join(foundationsDir, "Changelog.stories.tsx"), content, "utf-8");
872
+ console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "Changelog.stories.tsx")));
873
+ }
874
+
816
875
  function main() {
817
876
  if (!fs.existsSync(VDS_OUTPUT)) {
818
877
  console.error("[VDS] vds-output.json not found. Run `npm run vds` first.");
@@ -832,6 +891,10 @@ function main() {
832
891
  if (componentSuggestions?.length) {
833
892
  writeComponentSuggestionsStory(componentSuggestions);
834
893
  }
894
+ const changelog = data.changelog;
895
+ if (changelog?.length) {
896
+ writeChangelogStory(changelog);
897
+ }
835
898
  try {
836
899
  const fd = path.join(STORIES_DIR, "foundations");
837
900
  if (fs.existsSync(fd)) {
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import { watch } from 'fs';
3
+ import { execSync } from 'child_process';
4
+ import { resolve } from 'path';
5
+
6
+ const ROOT = resolve(process.cwd());
7
+ const SRC = resolve(ROOT, 'src');
8
+ let timeout = null;
9
+
10
+ function run() {
11
+ const ts = new Date().toLocaleTimeString();
12
+ console.log(`\n[VDS ${ts}] Değişiklik algılandı, taranıyor...`);
13
+ try {
14
+ execSync('node vds-core/scan.mjs', { stdio: 'inherit', cwd: ROOT });
15
+ execSync('node vds-core/story-generator.mjs', { stdio: 'inherit', cwd: ROOT });
16
+ console.log(`[VDS ${ts}] ✅ Design system güncellendi.`);
17
+ } catch (e) {
18
+ console.error(`[VDS ${ts}] ❌ Hata:`, e.message);
19
+ }
20
+ }
21
+
22
+ function onFileChange(eventType, filename) {
23
+ if (!filename) return;
24
+ if (filename.includes('stories/')) return;
25
+ if (filename.includes('vds-output')) return;
26
+ if (!filename.endsWith('.tsx') && !filename.endsWith('.ts') &&
27
+ !filename.endsWith('.css') && !filename.endsWith('.js')) return;
28
+ console.log(`[VDS] Değişiklik: ${filename}`);
29
+ clearTimeout(timeout);
30
+ timeout = setTimeout(run, 2000);
31
+ }
32
+
33
+ run();
34
+ console.log('[VDS] 👀 src/ klasörü izleniyor...');
35
+ console.log('[VDS] Durdurmak için Ctrl+C\n');
36
+ watch(SRC, { recursive: true }, onFileChange);