vibe-design-system 1.8.5 → 1.9.3
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 +105 -9
- package/package.json +1 -1
- package/vds-core-template/story-generator.mjs +179 -27
package/bin/init.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* VDS installer: npx vibe-design-system init
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* -
|
|
7
|
-
*
|
|
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
|
|
8
12
|
*/
|
|
9
13
|
import fs from "fs";
|
|
10
14
|
import path from "path";
|
|
@@ -24,6 +28,66 @@ function getProjectRoot() {
|
|
|
24
28
|
return cwd;
|
|
25
29
|
}
|
|
26
30
|
|
|
31
|
+
function isStorybookInstalled(projectRoot) {
|
|
32
|
+
const storybookDir = path.join(projectRoot, ".storybook");
|
|
33
|
+
if (fs.existsSync(storybookDir)) return true;
|
|
34
|
+
const pkgPath = path.join(projectRoot, "package.json");
|
|
35
|
+
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;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
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
|
+
});
|
|
50
|
+
if (r.status !== 0) {
|
|
51
|
+
console.warn("⚠️ Storybook init tamamlanamadı; manuel: npx storybook@latest init");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const PREVIEW_CONTENT = `import type { Preview } from "@storybook/react-vite";
|
|
56
|
+
import React from "react";
|
|
57
|
+
import "../src/index.css";
|
|
58
|
+
|
|
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
|
+
};
|
|
76
|
+
|
|
77
|
+
export default preview;
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
function ensureStorybookPreview(projectRoot) {
|
|
81
|
+
const storybookDir = path.join(projectRoot, ".storybook");
|
|
82
|
+
if (!fs.existsSync(storybookDir)) fs.mkdirSync(storybookDir, { recursive: true });
|
|
83
|
+
|
|
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).");
|
|
89
|
+
}
|
|
90
|
+
|
|
27
91
|
function addScripts(projectRoot) {
|
|
28
92
|
const pkgPath = path.join(projectRoot, "package.json");
|
|
29
93
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
@@ -32,6 +96,8 @@ function addScripts(projectRoot) {
|
|
|
32
96
|
if (!scripts["vds:watch"]) scripts["vds:watch"] = "node vds-core/scan.mjs --watch";
|
|
33
97
|
if (!scripts["vds:dashboard"]) scripts["vds:dashboard"] = "node vds-core/dashboard-server.mjs";
|
|
34
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";
|
|
35
101
|
pkg.scripts = scripts;
|
|
36
102
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2), "utf-8");
|
|
37
103
|
}
|
|
@@ -41,7 +107,7 @@ function addVdsDependency(projectRoot) {
|
|
|
41
107
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
42
108
|
const dev = pkg.devDependencies || {};
|
|
43
109
|
if (!dev["vibe-design-system"]) {
|
|
44
|
-
dev["vibe-design-system"] = "^1.
|
|
110
|
+
dev["vibe-design-system"] = "^1.9.0";
|
|
45
111
|
pkg.devDependencies = dev;
|
|
46
112
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2), "utf-8");
|
|
47
113
|
const r = spawnSync("npm", ["install"], { cwd: projectRoot, stdio: "inherit", shell: true });
|
|
@@ -64,9 +130,6 @@ function ensureVdsCore(projectRoot) {
|
|
|
64
130
|
const serverSrc = path.join(TEMPLATE_DIR, "dashboard-server.mjs");
|
|
65
131
|
if (fs.existsSync(serverSrc)) {
|
|
66
132
|
fs.copyFileSync(serverSrc, path.join(vdsCoreDest, "dashboard-server.mjs"));
|
|
67
|
-
} else {
|
|
68
|
-
console.error("vds-core-template/dashboard-server.mjs bulunamadı.");
|
|
69
|
-
process.exit(1);
|
|
70
133
|
}
|
|
71
134
|
|
|
72
135
|
const storyGenSrc = path.join(TEMPLATE_DIR, "story-generator.mjs");
|
|
@@ -78,6 +141,7 @@ function ensureVdsCore(projectRoot) {
|
|
|
78
141
|
function runScan(projectRoot) {
|
|
79
142
|
const scanPath = path.join(projectRoot, "vds-core", "scan.mjs");
|
|
80
143
|
if (!fs.existsSync(scanPath)) return;
|
|
144
|
+
console.log("🔍 VDS taraması çalıştırılıyor...");
|
|
81
145
|
const r = spawnSync("node", [scanPath], {
|
|
82
146
|
cwd: projectRoot,
|
|
83
147
|
stdio: "inherit",
|
|
@@ -86,6 +150,18 @@ function runScan(projectRoot) {
|
|
|
86
150
|
if (r.status !== 0) process.exitCode = r.status ?? 1;
|
|
87
151
|
}
|
|
88
152
|
|
|
153
|
+
function runStoryGenerator(projectRoot) {
|
|
154
|
+
const genPath = path.join(projectRoot, "vds-core", "story-generator.mjs");
|
|
155
|
+
if (!fs.existsSync(genPath)) return;
|
|
156
|
+
console.log("📖 Story dosyaları oluşturuluyor...");
|
|
157
|
+
const r = spawnSync("node", [genPath], {
|
|
158
|
+
cwd: projectRoot,
|
|
159
|
+
stdio: "inherit",
|
|
160
|
+
shell: false,
|
|
161
|
+
});
|
|
162
|
+
if (r.status !== 0) process.exitCode = r.status ?? 1;
|
|
163
|
+
}
|
|
164
|
+
|
|
89
165
|
console.log("🎨 VDS kuruluyor...");
|
|
90
166
|
|
|
91
167
|
const projectRoot = getProjectRoot();
|
|
@@ -96,9 +172,29 @@ if (!fs.existsSync(pkgPath)) {
|
|
|
96
172
|
process.exit(1);
|
|
97
173
|
}
|
|
98
174
|
|
|
175
|
+
// 1. Copy vds-core files
|
|
99
176
|
ensureVdsCore(projectRoot);
|
|
177
|
+
|
|
178
|
+
// 2. Install Storybook if not present
|
|
179
|
+
if (!isStorybookInstalled(projectRoot)) {
|
|
180
|
+
installStorybook(projectRoot);
|
|
181
|
+
} else {
|
|
182
|
+
console.log("📚 Storybook zaten mevcut.");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 3. Create/update .storybook/preview with index.css + dark decorator
|
|
186
|
+
ensureStorybookPreview(projectRoot);
|
|
187
|
+
|
|
188
|
+
// 6. Add all scripts to package.json (before scan so scripts are there)
|
|
100
189
|
addScripts(projectRoot);
|
|
101
190
|
addVdsDependency(projectRoot);
|
|
191
|
+
|
|
192
|
+
// 4. Run scan
|
|
102
193
|
runScan(projectRoot);
|
|
103
194
|
|
|
104
|
-
|
|
195
|
+
// 5. Run story generator
|
|
196
|
+
runStoryGenerator(projectRoot);
|
|
197
|
+
|
|
198
|
+
console.log("✅ Kurulum tamamlandı.");
|
|
199
|
+
console.log(" Storybook: npm run storybook");
|
|
200
|
+
console.log(" Dashboard: npm run vds:dashboard");
|
package/package.json
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* VDS Story generator
|
|
4
4
|
*
|
|
5
|
-
* Reads vds-output.json and generates
|
|
6
|
-
* under src/stories for each component
|
|
5
|
+
* Reads vds-output.json and generates:
|
|
6
|
+
* - Storybook .stories.tsx files under src/stories for each component
|
|
7
|
+
* - Foundations .stories.tsx: Colors, Typography, Brand under src/stories/foundations/
|
|
7
8
|
*
|
|
8
9
|
* Usage:
|
|
9
10
|
* node vds-core/story-generator.mjs # generate for all components
|
|
@@ -19,6 +20,12 @@ const VDS_OUTPUT = path.join(PROJECT_ROOT, "vds-output.json");
|
|
|
19
20
|
const SRC_DIR = path.join(PROJECT_ROOT, "src");
|
|
20
21
|
const STORIES_DIR = path.join(SRC_DIR, "stories");
|
|
21
22
|
|
|
23
|
+
// Top of every story file for CSS variables
|
|
24
|
+
const STORY_CSS_HEADER = `// @ts-ignore
|
|
25
|
+
import '../../index.css'
|
|
26
|
+
|
|
27
|
+
`;
|
|
28
|
+
|
|
22
29
|
// Components we don't want to auto-generate stories for (project-specific dashboards, heavy UIs, etc.)
|
|
23
30
|
const SKIP_LIST = [
|
|
24
31
|
"AnalysisDashboard",
|
|
@@ -37,28 +44,41 @@ const SKIP_LIST = [
|
|
|
37
44
|
"TestComponent",
|
|
38
45
|
];
|
|
39
46
|
|
|
40
|
-
// Components that need a router context
|
|
41
|
-
const ROUTER_DECORATOR_LIST = ["Breadcrumb", "NavigationMenu", "Sidebar", "Menubar"];
|
|
42
|
-
|
|
43
47
|
function ensureDir(dir) {
|
|
44
48
|
if (!fs.existsSync(dir)) {
|
|
45
49
|
fs.mkdirSync(dir, { recursive: true });
|
|
46
50
|
}
|
|
47
51
|
}
|
|
48
52
|
|
|
53
|
+
/** Detect if component uses router (useLocation, useNavigate, or Link from react-router-dom). */
|
|
54
|
+
function needsRouter(source) {
|
|
55
|
+
if (!source || typeof source !== "string") return false;
|
|
56
|
+
if (/\buseLocation\b|\buseNavigate\b/.test(source)) return true;
|
|
57
|
+
if (/from\s+['"]react-router-dom['"]/.test(source) && /\bLink\b/.test(source)) return true;
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** If component has <img and accepts children prop, we should omit children from story args (void element). */
|
|
62
|
+
function componentHasImgAndChildren(source) {
|
|
63
|
+
if (!source || typeof source !== "string") return false;
|
|
64
|
+
const hasImg = /return\s+<img|<\s*img\s+/.test(source);
|
|
65
|
+
if (!hasImg) return false;
|
|
66
|
+
return /\bchildren\b/.test(source);
|
|
67
|
+
}
|
|
68
|
+
|
|
49
69
|
function toSafeComponentName(name, file) {
|
|
50
|
-
if (name
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
if (!name || typeof name !== "string") {
|
|
71
|
+
const base = (file || "").replace(/\.[^.]+$/, "");
|
|
72
|
+
const parts = base.split(/[\\/]/g);
|
|
73
|
+
const last = parts[parts.length - 1] || "Component";
|
|
74
|
+
return last.charAt(0).toUpperCase() + last.slice(1);
|
|
75
|
+
}
|
|
76
|
+
return name
|
|
77
|
+
.replace(/[^A-Za-z0-9]+/g, " ")
|
|
78
|
+
.trim()
|
|
79
|
+
.replace(/\s+([a-z])/g, (_, c) => c.toUpperCase())
|
|
80
|
+
.replace(/^\w/, (c) => c.toUpperCase())
|
|
81
|
+
.replace(/\s+/g, "");
|
|
62
82
|
}
|
|
63
83
|
|
|
64
84
|
function parseUnionLiterals(type) {
|
|
@@ -308,6 +328,7 @@ function buildStoryFileContent(comp) {
|
|
|
308
328
|
// ignore
|
|
309
329
|
}
|
|
310
330
|
const exportStyle = detectExportStyle(source, componentName);
|
|
331
|
+
const omitChildren = componentHasImgAndChildren(source);
|
|
311
332
|
|
|
312
333
|
const lines = [];
|
|
313
334
|
lines.push(`import type { Meta, StoryObj } from "@storybook/react";`);
|
|
@@ -347,7 +368,8 @@ function buildStoryFileContent(comp) {
|
|
|
347
368
|
`import { CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card";`,
|
|
348
369
|
);
|
|
349
370
|
}
|
|
350
|
-
|
|
371
|
+
const useRouterDecorator = needsRouter(source);
|
|
372
|
+
if (useRouterDecorator) {
|
|
351
373
|
lines.push(`import { MemoryRouter } from "react-router-dom";`);
|
|
352
374
|
}
|
|
353
375
|
|
|
@@ -356,7 +378,7 @@ function buildStoryFileContent(comp) {
|
|
|
356
378
|
lines.push(` title: ${JSON.stringify(title)},`);
|
|
357
379
|
lines.push(` component: ComponentRef,`);
|
|
358
380
|
lines.push(` tags: ["autodocs"],`);
|
|
359
|
-
if (
|
|
381
|
+
if (useRouterDecorator) {
|
|
360
382
|
lines.push(` decorators: [(Story) => (`);
|
|
361
383
|
lines.push(` <MemoryRouter>`);
|
|
362
384
|
lines.push(` <Story />`);
|
|
@@ -373,14 +395,14 @@ function buildStoryFileContent(comp) {
|
|
|
373
395
|
const specialStories = buildSpecialStories(componentName, variants);
|
|
374
396
|
if (specialStories) {
|
|
375
397
|
lines.push(specialStories);
|
|
376
|
-
return lines.join("\n");
|
|
398
|
+
return STORY_CSS_HEADER + lines.join("\n");
|
|
377
399
|
}
|
|
378
400
|
|
|
379
|
-
// Generic variant-based stories
|
|
401
|
+
// Generic variant-based stories (omit children for img/void components)
|
|
380
402
|
if (!variants.length) {
|
|
381
403
|
lines.push(`export const Default: Story = {`);
|
|
382
404
|
lines.push(` args: {`);
|
|
383
|
-
lines.push(` children: "${componentName}",`);
|
|
405
|
+
if (!omitChildren) lines.push(` children: "${componentName}",`);
|
|
384
406
|
lines.push(` },`);
|
|
385
407
|
lines.push(`};`);
|
|
386
408
|
} else {
|
|
@@ -388,7 +410,7 @@ function buildStoryFileContent(comp) {
|
|
|
388
410
|
lines.push(`export const ${capitalize(defaultVariant)}: Story = {`);
|
|
389
411
|
lines.push(` args: {`);
|
|
390
412
|
lines.push(` variant: "${defaultVariant}",`);
|
|
391
|
-
lines.push(` children: "${componentName}",`);
|
|
413
|
+
if (!omitChildren) lines.push(` children: "${componentName}",`);
|
|
392
414
|
lines.push(` },`);
|
|
393
415
|
lines.push(`};`);
|
|
394
416
|
lines.push("");
|
|
@@ -397,14 +419,130 @@ function buildStoryFileContent(comp) {
|
|
|
397
419
|
lines.push(`export const ${storyName}: Story = {`);
|
|
398
420
|
lines.push(` args: {`);
|
|
399
421
|
lines.push(` variant: "${v}",`);
|
|
400
|
-
lines.push(` children: "${storyName}",`);
|
|
422
|
+
if (!omitChildren) lines.push(` children: "${storyName}",`);
|
|
401
423
|
lines.push(` },`);
|
|
402
424
|
lines.push(`};`);
|
|
403
425
|
lines.push("");
|
|
404
426
|
}
|
|
405
427
|
}
|
|
406
428
|
|
|
407
|
-
return lines.join("\n");
|
|
429
|
+
return STORY_CSS_HEADER + lines.join("\n");
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** Build color entries from foundations.colors (skip _dark; flatten to { name, hex }). */
|
|
433
|
+
function getColorEntries(colors) {
|
|
434
|
+
if (!colors || typeof colors !== "object") return [];
|
|
435
|
+
const entries = [];
|
|
436
|
+
for (const [name, v] of Object.entries(colors)) {
|
|
437
|
+
if (name === "_dark") continue;
|
|
438
|
+
const hex = v && (v.hex ?? (typeof v.value === "string" && v.value.startsWith("#") ? v.value : null));
|
|
439
|
+
const value = v && v.value;
|
|
440
|
+
if (hex) entries.push({ name, hex });
|
|
441
|
+
else if (value) entries.push({ name, hex: value });
|
|
442
|
+
}
|
|
443
|
+
return entries;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function writeFoundationsStories(foundations) {
|
|
447
|
+
const foundationsDir = path.join(STORIES_DIR, "foundations");
|
|
448
|
+
ensureDir(foundationsDir);
|
|
449
|
+
|
|
450
|
+
const colorEntries = getColorEntries(foundations?.colors);
|
|
451
|
+
const colorsContent =
|
|
452
|
+
STORY_CSS_HEADER +
|
|
453
|
+
[
|
|
454
|
+
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
455
|
+
"",
|
|
456
|
+
"const meta = { title: \"Foundations/Colors\" } satisfies Meta;",
|
|
457
|
+
"export default meta;",
|
|
458
|
+
"type Story = StoryObj;",
|
|
459
|
+
"",
|
|
460
|
+
`const colors = ${JSON.stringify(colorEntries)};`,
|
|
461
|
+
"",
|
|
462
|
+
"export const Default: Story = {",
|
|
463
|
+
" render: () => (",
|
|
464
|
+
" <div style={{ display: \"grid\", gridTemplateColumns: \"repeat(auto-fill, minmax(140px, 1fr))\", gap: \"1rem\" }}>",
|
|
465
|
+
" {colors.map(({ name, hex }) => (",
|
|
466
|
+
" <div key={name}>",
|
|
467
|
+
" <div style={{ backgroundColor: hex, height: 80, borderRadius: 8, border: \"1px solid #333\" }} />",
|
|
468
|
+
" <p style={{ marginTop: 8, fontSize: 12 }}>{name}</p>",
|
|
469
|
+
" <code style={{ fontSize: 11 }}>{hex}</code>",
|
|
470
|
+
" </div>",
|
|
471
|
+
" ))}",
|
|
472
|
+
" </div>",
|
|
473
|
+
" ),",
|
|
474
|
+
"};",
|
|
475
|
+
].join("\n");
|
|
476
|
+
fs.writeFileSync(path.join(foundationsDir, "Colors.stories.tsx"), colorsContent, "utf-8");
|
|
477
|
+
console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "Colors.stories.tsx")));
|
|
478
|
+
|
|
479
|
+
const typo = foundations?.typography;
|
|
480
|
+
if (typo && typeof typo === "object" && Object.keys(typo).length > 0) {
|
|
481
|
+
const typoRows = Object.entries(typo).map(([k, v]) => ({
|
|
482
|
+
token: k,
|
|
483
|
+
value: Array.isArray(v) ? v.join(", ") : String(v),
|
|
484
|
+
}));
|
|
485
|
+
const typoContent =
|
|
486
|
+
STORY_CSS_HEADER +
|
|
487
|
+
[
|
|
488
|
+
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
489
|
+
"",
|
|
490
|
+
"const meta = { title: \"Foundations/Typography\" } satisfies Meta;",
|
|
491
|
+
"export default meta;",
|
|
492
|
+
"type Story = StoryObj;",
|
|
493
|
+
"",
|
|
494
|
+
`const typography = ${JSON.stringify(typoRows)};`,
|
|
495
|
+
"",
|
|
496
|
+
"export const Default: Story = {",
|
|
497
|
+
" render: () => (",
|
|
498
|
+
" <div>",
|
|
499
|
+
" <h2 style={{ marginBottom: 16 }}>Font family & scale</h2>",
|
|
500
|
+
" <table style={{ borderCollapse: \"collapse\", width: \"100%\" }}>",
|
|
501
|
+
" <thead><tr><th style={{ textAlign: \"left\", padding: 8, borderBottom: \"1px solid #333\" }}>Token</th><th style={{ textAlign: \"left\", padding: 8, borderBottom: \"1px solid #333\" }}>Value</th></tr></thead>",
|
|
502
|
+
" <tbody>",
|
|
503
|
+
" {typography.map(({ token, value }) => (",
|
|
504
|
+
" <tr key={token}><td style={{ padding: 8, borderBottom: \"1px solid #222\" }}>{token}</td><td style={{ padding: 8, borderBottom: \"1px solid #222\" }}>{value}</td></tr>",
|
|
505
|
+
" ))}",
|
|
506
|
+
" </tbody>",
|
|
507
|
+
" </table>",
|
|
508
|
+
" </div>",
|
|
509
|
+
" ),",
|
|
510
|
+
"};",
|
|
511
|
+
].join("\n");
|
|
512
|
+
fs.writeFileSync(path.join(foundationsDir, "Typography.stories.tsx"), typoContent, "utf-8");
|
|
513
|
+
console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "Typography.stories.tsx")));
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const brandAssets = foundations?.brand?.assets;
|
|
517
|
+
const assets = Array.isArray(brandAssets) ? brandAssets : [];
|
|
518
|
+
const brandContent =
|
|
519
|
+
STORY_CSS_HEADER +
|
|
520
|
+
[
|
|
521
|
+
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
522
|
+
"",
|
|
523
|
+
"const meta = { title: \"Foundations/Brand\" } satisfies Meta;",
|
|
524
|
+
"export default meta;",
|
|
525
|
+
"type Story = StoryObj;",
|
|
526
|
+
"",
|
|
527
|
+
`const assets = ${JSON.stringify(assets)};`,
|
|
528
|
+
"",
|
|
529
|
+
"export const Default: Story = {",
|
|
530
|
+
" render: () => (",
|
|
531
|
+
" <div>",
|
|
532
|
+
" <h2 style={{ marginBottom: 16 }}>Logo and favicon paths</h2>",
|
|
533
|
+
" <ul style={{ listStyle: \"none\", padding: 0 }}>",
|
|
534
|
+
" {assets.length === 0 ? <li>No brand assets found.</li> : assets.map((a, i) => (",
|
|
535
|
+
" <li key={i} style={{ padding: \"8px 0\", borderBottom: \"1px solid #222\" }}>",
|
|
536
|
+
" <strong>{a.type || \"asset\"}</strong>: <code>{a.path || a.name || \"\"}</code>",
|
|
537
|
+
" </li>",
|
|
538
|
+
" ))}",
|
|
539
|
+
" </ul>",
|
|
540
|
+
" </div>",
|
|
541
|
+
" ),",
|
|
542
|
+
"};",
|
|
543
|
+
].join("\n");
|
|
544
|
+
fs.writeFileSync(path.join(foundationsDir, "Brand.stories.tsx"), brandContent, "utf-8");
|
|
545
|
+
console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "Brand.stories.tsx")));
|
|
408
546
|
}
|
|
409
547
|
|
|
410
548
|
function main() {
|
|
@@ -415,14 +553,29 @@ function main() {
|
|
|
415
553
|
const raw = fs.readFileSync(VDS_OUTPUT, "utf-8");
|
|
416
554
|
const data = JSON.parse(raw);
|
|
417
555
|
const components = Array.isArray(data.components) ? data.components : [];
|
|
556
|
+
const foundations = data.foundations || null;
|
|
418
557
|
|
|
419
558
|
const onlyName = process.argv[2] || null;
|
|
420
559
|
|
|
421
560
|
ensureDir(STORIES_DIR);
|
|
422
|
-
|
|
561
|
+
ensureDir(path.join(STORIES_DIR, "foundations"));
|
|
562
|
+
writeFoundationsStories(foundations);
|
|
563
|
+
try {
|
|
564
|
+
const fd = path.join(STORIES_DIR, "foundations");
|
|
565
|
+
if (fs.existsSync(fd)) {
|
|
566
|
+
for (const name of fs.readdirSync(fd)) {
|
|
567
|
+
if (name.endsWith(".stories.mdx")) fs.unlinkSync(path.join(fd, name));
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
} catch {
|
|
571
|
+
// ignore
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Clear existing .stories.* files (except foundations/*.mdx) so only VDS-generated stories remain
|
|
423
575
|
try {
|
|
424
576
|
const existing = fs.readdirSync(STORIES_DIR);
|
|
425
577
|
for (const name of existing) {
|
|
578
|
+
if (name === "foundations") continue;
|
|
426
579
|
if (
|
|
427
580
|
name.endsWith(".stories.tsx") ||
|
|
428
581
|
name.endsWith(".stories.ts") ||
|
|
@@ -450,4 +603,3 @@ function main() {
|
|
|
450
603
|
}
|
|
451
604
|
|
|
452
605
|
main();
|
|
453
|
-
|