vibe-design-system 2.9.1 → 2.9.2
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/package.json
CHANGED
|
@@ -5356,8 +5356,9 @@ function extractComponentProps(content, componentName) {
|
|
|
5356
5356
|
|
|
5357
5357
|
/**
|
|
5358
5358
|
* Generate a mock value string for a TypeScript type, using available data exports.
|
|
5359
|
+
* Pass sourceContent to enable local interface/type parsing and nested property lookup.
|
|
5359
5360
|
*/
|
|
5360
|
-
function mockValueForType(type, localExports) {
|
|
5361
|
+
function mockValueForType(type, localExports, sourceContent) {
|
|
5361
5362
|
const t = type.trim();
|
|
5362
5363
|
if (t === "boolean") return "false";
|
|
5363
5364
|
if (t === "string") return '""';
|
|
@@ -5373,26 +5374,60 @@ function mockValueForType(type, localExports) {
|
|
|
5373
5374
|
const firstLiteral = t.match(/["']([^"']+)["']/);
|
|
5374
5375
|
if (firstLiteral && (t.startsWith('"') || t.startsWith("'"))) return `"${firstLiteral[1]}"`;
|
|
5375
5376
|
|
|
5376
|
-
//
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5377
|
+
// Named PascalCase type — try multiple resolution strategies
|
|
5378
|
+
if (/^[A-Z][A-Za-z0-9]+$/.test(t)) {
|
|
5379
|
+
const typeLower = t.toLowerCase().replace(/def$|id$|key$|type$|interface$|kind$/i, "");
|
|
5380
|
+
|
|
5381
|
+
// Strategy 1: Direct top-level array match (RouteDef → ROUTES[0])
|
|
5382
|
+
if (typeLower.length >= 3) {
|
|
5383
|
+
for (const [expName, expKind] of Object.entries(localExports)) {
|
|
5384
|
+
if (expKind !== "array") continue;
|
|
5385
|
+
const expBase = expName.toLowerCase().replace(/_/g, "").replace(/s$/, "").replace(/data$/, "");
|
|
5386
|
+
if (expBase.slice(0, 4) === typeLower.slice(0, 4) || typeLower.slice(0, 4) === expBase.slice(0, 4)) {
|
|
5387
|
+
return `${expName}[0]`;
|
|
5388
|
+
}
|
|
5389
|
+
}
|
|
5390
|
+
}
|
|
5391
|
+
|
|
5392
|
+
// Strategy 2: Local interface/type alias → generate inline object
|
|
5393
|
+
// e.g. FilterState → { level1: null, level2: null }
|
|
5394
|
+
if (sourceContent) {
|
|
5395
|
+
const esc = t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5396
|
+
const bodyRe = new RegExp(`(?:interface|type)\\s+${esc}\\s*(?:=\\s*)?\\{([^}]+)\\}`);
|
|
5397
|
+
const m = sourceContent.match(bodyRe);
|
|
5398
|
+
if (m) {
|
|
5399
|
+
const fields = m[1].split(";").map(s => s.trim()).filter(s => s.length > 0 && !s.startsWith("//"));
|
|
5400
|
+
const fieldPairs = fields.map(f => {
|
|
5401
|
+
const ci = f.indexOf(":");
|
|
5402
|
+
if (ci < 0) return null;
|
|
5403
|
+
const fname = f.slice(0, ci).trim().replace(/\?$/, "");
|
|
5404
|
+
const ftype = f.slice(ci + 1).trim();
|
|
5405
|
+
// Resolve field type (no recursion with sourceContent to prevent deep loops)
|
|
5406
|
+
const fmock = mockValueForType(ftype, localExports, null);
|
|
5407
|
+
return ` ${fname}: ${fmock}`;
|
|
5408
|
+
}).filter(Boolean);
|
|
5409
|
+
if (fieldPairs.length > 0) return `{\n${fieldPairs.join(",\n")}\n}`;
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
|
|
5413
|
+
// Strategy 3: Nested array property lookup
|
|
5414
|
+
// SubStation → DATA[0]?.subStations?.[0] (camelCase plural of type name)
|
|
5415
|
+
const camel = t[0].toLowerCase() + t.slice(1);
|
|
5416
|
+
const plural = camel + "s";
|
|
5380
5417
|
for (const [expName, expKind] of Object.entries(localExports)) {
|
|
5381
5418
|
if (expKind !== "array") continue;
|
|
5382
|
-
|
|
5383
|
-
// Direct match: RouteDef → ROUTES (routedef→rout vs routes→rout)
|
|
5384
|
-
if (expBase.slice(0, 4) === typeLower.slice(0, 4) || typeLower.slice(0, 4) === expBase.slice(0, 4)) {
|
|
5385
|
-
return `${expName}[0]`;
|
|
5386
|
-
}
|
|
5419
|
+
return `${expName}[0]?.${plural}?.[0]`;
|
|
5387
5420
|
}
|
|
5388
5421
|
}
|
|
5422
|
+
|
|
5389
5423
|
return "undefined as any";
|
|
5390
5424
|
}
|
|
5391
5425
|
|
|
5392
5426
|
/**
|
|
5393
5427
|
* Build the story file content for a single internal component.
|
|
5428
|
+
* Automatically uses render() format when props require complex/nested mock data.
|
|
5394
5429
|
*/
|
|
5395
|
-
function buildInternalComponentStoryContent(compName, parentImportPath, dataImportPath, dataSymbols, localExports, props, parentComp) {
|
|
5430
|
+
function buildInternalComponentStoryContent(compName, parentImportPath, dataImportPath, dataSymbols, localExports, props, parentComp, sourceContent) {
|
|
5396
5431
|
const lines = [];
|
|
5397
5432
|
lines.push(`// @vds-regenerate — VDS auto-generated. Remove this line to prevent overwrite.`);
|
|
5398
5433
|
lines.push(`import React from "react";`);
|
|
@@ -5418,16 +5453,72 @@ function buildInternalComponentStoryContent(compName, parentImportPath, dataImpo
|
|
|
5418
5453
|
lines.push(`type Story = StoryObj<typeof meta>;`);
|
|
5419
5454
|
lines.push(``);
|
|
5420
5455
|
|
|
5421
|
-
if (props.length
|
|
5422
|
-
|
|
5456
|
+
if (props.length === 0) {
|
|
5457
|
+
lines.push(`export const Default: Story = {};`);
|
|
5458
|
+
return lines.join("\n") + "\n";
|
|
5459
|
+
}
|
|
5460
|
+
|
|
5461
|
+
const mockedProps = props.map(p => ({
|
|
5462
|
+
name: p.name,
|
|
5463
|
+
mock: mockValueForType(p.type, localExports, sourceContent),
|
|
5464
|
+
}));
|
|
5465
|
+
|
|
5466
|
+
// Use render() format when any mock is complex: nested path (?.),
|
|
5467
|
+
// multiline inline object (\n), or unresolved (undefined as any)
|
|
5468
|
+
const needsRender = mockedProps.some(p =>
|
|
5469
|
+
p.mock === "undefined as any" ||
|
|
5470
|
+
p.mock.includes("?.") ||
|
|
5471
|
+
p.mock.includes("\n")
|
|
5472
|
+
);
|
|
5473
|
+
|
|
5474
|
+
if (!needsRender) {
|
|
5475
|
+
// Simple args format — all values are primitives or direct data refs
|
|
5476
|
+
const argLines = mockedProps.map(p => ` ${p.name}: ${p.mock},`).join("\n");
|
|
5423
5477
|
lines.push(`export const Default: Story = {`);
|
|
5424
5478
|
lines.push(` args: {`);
|
|
5425
5479
|
lines.push(argLines);
|
|
5426
5480
|
lines.push(` },`);
|
|
5427
5481
|
lines.push(`};`);
|
|
5428
5482
|
} else {
|
|
5429
|
-
|
|
5483
|
+
// render() format — declare complex vars, guard against undefined, then render
|
|
5484
|
+
const varDecls = [];
|
|
5485
|
+
const propAttrs = [];
|
|
5486
|
+
const guardVarNames = [];
|
|
5487
|
+
|
|
5488
|
+
for (const { name, mock } of mockedProps) {
|
|
5489
|
+
if (mock.includes("?.") || mock === "undefined as any") {
|
|
5490
|
+
// Nested/unknown: declare variable + add to guard list
|
|
5491
|
+
const varName = `mock${name[0].toUpperCase() + name.slice(1)}`;
|
|
5492
|
+
varDecls.push(` const ${varName} = ${mock};`);
|
|
5493
|
+
guardVarNames.push(varName);
|
|
5494
|
+
propAttrs.push(` ${name}={${varName} as any}`);
|
|
5495
|
+
} else if (mock.includes("\n")) {
|
|
5496
|
+
// Multiline inline object (e.g. interface mock)
|
|
5497
|
+
const varName = `mock${name[0].toUpperCase() + name.slice(1)}`;
|
|
5498
|
+
const indented = mock.split("\n").map((l, i) => i === 0 ? l : " " + l).join("\n");
|
|
5499
|
+
varDecls.push(` const ${varName} = ${indented} as any;`);
|
|
5500
|
+
propAttrs.push(` ${name}={${varName}}`);
|
|
5501
|
+
} else {
|
|
5502
|
+
propAttrs.push(` ${name}={${mock}}`);
|
|
5503
|
+
}
|
|
5504
|
+
}
|
|
5505
|
+
|
|
5506
|
+
lines.push(`export const Default: Story = {`);
|
|
5507
|
+
lines.push(` render: () => {`);
|
|
5508
|
+
for (const v of varDecls) lines.push(v);
|
|
5509
|
+
if (guardVarNames.length > 0) {
|
|
5510
|
+
const check = guardVarNames.map(v => `!${v}`).join(" || ");
|
|
5511
|
+
lines.push(` if (${check}) return <div style={{ color: "#888", padding: "1rem" }}>⚠️ Mock data unavailable for <strong>${compName}</strong></div>;`);
|
|
5512
|
+
}
|
|
5513
|
+
lines.push(` return (`);
|
|
5514
|
+
lines.push(` <${compName}`);
|
|
5515
|
+
for (const attr of propAttrs) lines.push(attr);
|
|
5516
|
+
lines.push(` />`);
|
|
5517
|
+
lines.push(` );`);
|
|
5518
|
+
lines.push(` },`);
|
|
5519
|
+
lines.push(`};`);
|
|
5430
5520
|
}
|
|
5521
|
+
|
|
5431
5522
|
return lines.join("\n") + "\n";
|
|
5432
5523
|
}
|
|
5433
5524
|
|
|
@@ -5495,10 +5586,14 @@ function generateInternalComponentStories(comp) {
|
|
|
5495
5586
|
// Step 4: Generate individual story files
|
|
5496
5587
|
for (const name of names) {
|
|
5497
5588
|
const storyFile = path.join(STORIES_DIR, `${name}.stories.tsx`);
|
|
5498
|
-
if (fs.existsSync(storyFile))
|
|
5589
|
+
if (fs.existsSync(storyFile)) {
|
|
5590
|
+
// Preserve user-modified stories; overwrite only VDS-generated ones
|
|
5591
|
+
const existing = fs.readFileSync(storyFile, "utf-8");
|
|
5592
|
+
if (!existing.startsWith("// @vds-regenerate")) continue;
|
|
5593
|
+
}
|
|
5499
5594
|
const props = extractComponentProps(sourceContent, name);
|
|
5500
5595
|
const content = buildInternalComponentStoryContent(
|
|
5501
|
-
name, parentImportPath, dataImportFromStories, bestSymbols, bestExports, props, comp
|
|
5596
|
+
name, parentImportPath, dataImportFromStories, bestSymbols, bestExports, props, comp, sourceContent
|
|
5502
5597
|
);
|
|
5503
5598
|
fs.writeFileSync(storyFile, content, "utf-8");
|
|
5504
5599
|
console.log(`[VDS] Wrote ${path.relative(PROJECT_ROOT, storyFile)} (sub-component of ${comp.name})`);
|