vibe-design-system 2.5.1 → 2.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-design-system",
3
- "version": "2.5.1",
3
+ "version": "2.5.3",
4
4
  "description": "Auto-generate design systems for vibe coding projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -620,7 +620,7 @@ function extractComponentSuggestions() {
620
620
  if (count < 2) continue;
621
621
  const suggestedName = suggestNameFromContent(innerText) || suggestNameFromPattern(pattern);
622
622
  suggestions.push({
623
- suggestedName: suggestedName.replace(/\s+/g, ""),
623
+ suggestedName: sanitizeComponentName(suggestedName.replace(/\s+/g, "")),
624
624
  tagName: tagName || "div",
625
625
  pattern,
626
626
  fullJsx,
@@ -635,10 +635,55 @@ function extractComponentSuggestions() {
635
635
 
636
636
  const VDS_GENERATED_DIR = path.join(COMPONENTS_DIR, "vds-generated");
637
637
 
638
+ /** Ensure component name is valid JS: no leading digits/special chars, PascalCase. */
639
+ function sanitizeComponentName(name) {
640
+ if (!name || typeof name !== "string") return "Extracted";
641
+ let s = name.replace(/\s+/g, "").replace(/^[0-9\W_]+/, "");
642
+ if (!s) return "Extracted";
643
+ s = s.charAt(0).toUpperCase() + s.slice(1);
644
+ return s;
645
+ }
646
+
638
647
  function toKebab(str) {
639
648
  return str.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
640
649
  }
641
650
 
651
+ /** Flatten JSX: replace ${...} and {...} (variables/expressions) with placeholders so generated component has no "item is not defined". */
652
+ function flattenJsx(jsx) {
653
+ let result = "";
654
+ let i = 0;
655
+ const len = jsx.length;
656
+ while (i < len) {
657
+ if (jsx.slice(i, i + 2) === "${") {
658
+ i += 2;
659
+ let depth = 1;
660
+ while (i < len && depth > 0) {
661
+ if (jsx[i] === "{") depth++;
662
+ else if (jsx[i] === "}") depth--;
663
+ i++;
664
+ }
665
+ result += '"Sample"';
666
+ continue;
667
+ }
668
+ if (jsx[i] === "{" && (i === 0 || jsx[i - 1] !== "{")) {
669
+ const start = i;
670
+ i++;
671
+ let depth = 1;
672
+ while (i < len && depth > 0) {
673
+ const c = jsx[i];
674
+ if (c === "{") depth++;
675
+ else if (c === "}") depth--;
676
+ i++;
677
+ }
678
+ result += '{"Sample"}';
679
+ continue;
680
+ }
681
+ result += jsx[i];
682
+ i++;
683
+ }
684
+ return result;
685
+ }
686
+
642
687
  /** Collect PascalCase tag names from JSX that are not known HTML. */
643
688
  function collectComponentTags(jsx) {
644
689
  const tags = new Set();
@@ -659,7 +704,7 @@ function writeVdsGeneratedComponents(suggestions) {
659
704
  const entries = [];
660
705
  for (let i = 0; i < suggestions.length; i++) {
661
706
  const s = suggestions[i];
662
- let baseName = (s.suggestedName || "Extracted").replace(/\s+/g, "");
707
+ let baseName = sanitizeComponentName(s.suggestedName || "Extracted");
663
708
  let fileName = baseName + ".tsx";
664
709
  if (usedNames.has(fileName)) fileName = baseName + String(i + 1) + ".tsx";
665
710
  usedNames.add(fileName);
@@ -670,7 +715,7 @@ function writeVdsGeneratedComponents(suggestions) {
670
715
  let bodyJsx;
671
716
  const tokens = [];
672
717
  if (s.fullJsx && s.fullJsx.trim().length > 0) {
673
- bodyJsx = s.fullJsx;
718
+ bodyJsx = flattenJsx(s.fullJsx);
674
719
  const componentTags = collectComponentTags(bodyJsx);
675
720
  const lucideTags = [];
676
721
  const uiTags = [];
@@ -587,9 +587,10 @@ function buildStoryFileContent(comp) {
587
587
  }
588
588
  const exportStyle = detectExportStyle(source, componentName);
589
589
  const omitChildren = componentWrapsVoidElement(source);
590
+ const isPage = comp.file.startsWith("pages/");
590
591
 
591
- // Skip story if no export found (e.g. Join.tsx with broken default export)
592
- if (exportStyle === "unknown" && (!source.includes("export") || !new RegExp(`\\b${componentName}\\b`).test(source))) {
592
+ // Skip story only if not a page and no export found
593
+ if (exportStyle === "unknown" && !isPage && (!source.includes("export") || !new RegExp(`\\b${componentName}\\b`).test(source))) {
593
594
  return null;
594
595
  }
595
596
 
@@ -600,7 +601,10 @@ function buildStoryFileContent(comp) {
600
601
  const lines = [];
601
602
  lines.push(`import type { Meta, StoryObj } from "@storybook/react";`);
602
603
 
603
- if (exportStyle === "default") {
604
+ if (isPage && exportStyle !== "default") {
605
+ lines.push(`import * as Named from "${importPath}";`);
606
+ lines.push(`const ComponentRef = Named["${componentName}"] ?? Named.default;`);
607
+ } else if (exportStyle === "default") {
604
608
  lines.push(`import ${componentName} from "${importPath}";`);
605
609
  lines.push(`const ComponentRef = ${componentName};`);
606
610
  } else if (exportStyle === "named" || exportStyle === "unknown") {