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 +245 -116
- package/package.json +1 -1
- package/vds-core-template/scan.mjs +121 -5
- package/vds-core-template/story-generator.mjs +63 -0
- package/vds-core-template/watch.mjs +36 -0
package/bin/init.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* VDS
|
|
3
|
+
* VDS init: npx vibe-design-system init
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
32
|
-
|
|
33
|
-
if (
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
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(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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("⚠️
|
|
133
|
+
console.warn("⚠️ npm install storybook tamamlanamadı.");
|
|
52
134
|
}
|
|
53
135
|
}
|
|
54
136
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
if (!fs.existsSync(
|
|
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
|
-
|
|
85
|
-
const
|
|
86
|
-
if (fs.existsSync(
|
|
87
|
-
|
|
88
|
-
|
|
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 =
|
|
189
|
+
const pkg = readPackageJson(projectRoot);
|
|
190
|
+
if (!pkg) return;
|
|
94
191
|
const scripts = pkg.scripts || {};
|
|
95
|
-
|
|
96
|
-
if (!scripts
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (!scripts["
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
136
|
-
|
|
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
|
-
|
|
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
|
|
169
|
-
if (!
|
|
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
|
|
176
|
-
|
|
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
|
|
301
|
+
console.log("📚 Storybook zaten yüklü.");
|
|
183
302
|
}
|
|
184
303
|
|
|
185
|
-
//
|
|
186
|
-
|
|
304
|
+
// ADIM 2
|
|
305
|
+
ensureStorybook(projectRoot);
|
|
306
|
+
|
|
307
|
+
// ADIM 3
|
|
308
|
+
ensureVdsCore(projectRoot);
|
|
187
309
|
|
|
188
|
-
//
|
|
310
|
+
// ADIM 4
|
|
189
311
|
addScripts(projectRoot);
|
|
190
|
-
addVdsDependency(projectRoot);
|
|
191
312
|
|
|
192
|
-
//
|
|
193
|
-
|
|
313
|
+
// ADIM 5
|
|
314
|
+
ensureCursorrules(projectRoot);
|
|
315
|
+
|
|
316
|
+
// ADIM 6
|
|
317
|
+
ensureStoriesDir(projectRoot);
|
|
194
318
|
|
|
195
|
-
//
|
|
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
|
-
|
|
199
|
-
console.log("
|
|
200
|
-
console.log("
|
|
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
|
@@ -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
|
|
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:
|
|
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${
|
|
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);
|