vibe-design-system 2.5.20 → 2.5.21

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.20",
3
+ "version": "2.5.21",
4
4
  "description": "Auto-generate design systems for vibe coding projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -538,6 +538,19 @@ function extractComponentSuggestions() {
538
538
  return suggestions;
539
539
  }
540
540
 
541
+ /** src/pages/ içinde tanımlı ama src/components'a çıkarılmamış visual section'ları listele (component adayı raporu). */
542
+ function extractUnreleasedSectionCandidates() {
543
+ if (!fs.existsSync(PAGES_DIR)) return [];
544
+ const suggestions = extractComponentSuggestions();
545
+ return suggestions.map((s) => ({
546
+ suggestedName: s.suggestedName,
547
+ file: Array.isArray(s.foundIn) && s.foundIn[0] ? s.foundIn[0] : null,
548
+ files: s.foundIn || [],
549
+ occurrences: s.occurrences || 0,
550
+ snippet: s.snippet,
551
+ }));
552
+ }
553
+
541
554
  const VDS_GENERATED_DIR = path.join(COMPONENTS_DIR, "vds-generated");
542
555
 
543
556
  /** Ensure component name is valid JS: no leading digits/special chars, PascalCase. */
@@ -1145,6 +1158,7 @@ function scan() {
1145
1158
  foundations.icons = extractLucideIconsUsed(SRC_DIR);
1146
1159
  foundations.brand = { assets: extractBrandAssets() };
1147
1160
  const componentSuggestions = extractComponentSuggestions();
1161
+ const unreleasedSectionCandidates = extractUnreleasedSectionCandidates();
1148
1162
  const output = {
1149
1163
  branch: getGitBranch(),
1150
1164
  engineer: getGitEngineer(),
@@ -1153,6 +1167,7 @@ function scan() {
1153
1167
  components: results,
1154
1168
  foundations,
1155
1169
  componentSuggestions,
1170
+ unreleasedSectionCandidates,
1156
1171
  };
1157
1172
 
1158
1173
  const prevOutput = readPreviousOutputFull();
@@ -462,6 +462,22 @@ const DEFAULT_STRINGS_BY_PROP_NAME = {
462
462
  role: "Team Member",
463
463
  };
464
464
 
465
+ /** Component adına göre contextual placeholder (kullanım yoksa). */
466
+ const COMPONENT_PLACEHOLDERS = {
467
+ ProcessStep: { step: "01", title: "Discovery Phase", description: "We analyze your current setup and requirements." },
468
+ StatBlock: { title: "Metric", value: "100%", description: "Key performance indicator." },
469
+ BenefitCard: { title: "Benefit", description: "Why this matters for you.", value: "Value proposition." },
470
+ TeamCard: { name: "Jane Doe", role: "Team Member", quote: "Quote text here." },
471
+ ManifestoSection: { title: "Our mission", description: "What we believe in." },
472
+ };
473
+
474
+ /** ReactNode prop adına göre kısa placeholder metin (createElement içinde). */
475
+ const REACTNODE_PLACEHOLDER_TEXT = {
476
+ quote: "Quote text here.",
477
+ title: "Title text",
478
+ children: "Example content",
479
+ };
480
+
465
481
  /** Recursive list of .tsx file paths under dir (relative to dir). */
466
482
  function getAllTsxUnderDir(dir) {
467
483
  if (!fs.existsSync(dir)) return [];
@@ -493,6 +509,43 @@ function extractPropsFromJsxTagContent(tagContent) {
493
509
  return props;
494
510
  }
495
511
 
512
+ /** Parse source for interface ItemTypeName { field: type; ... } and return list of field names. */
513
+ function getItemTypeFieldsFromSource(source, itemTypeName) {
514
+ if (!source || !itemTypeName) return [];
515
+ const escaped = itemTypeName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
516
+ let body = null;
517
+ const typeRe = new RegExp(`(?:type|interface)\\s+${escaped}\\s*=\\s*\\{([^}]+)\\}`, "m");
518
+ const ifaceRe = new RegExp(`interface\\s+${escaped}\\s*\\{([^}]+)\\}`, "m");
519
+ const m1 = source.match(typeRe);
520
+ const m2 = source.match(ifaceRe);
521
+ if (m1) body = m1[1];
522
+ else if (m2) body = m2[1];
523
+ if (!body) return [];
524
+ const fieldRe = /(\w+)\s*[:?]/g;
525
+ const fields = [];
526
+ let m;
527
+ while ((m = fieldRe.exec(body)) !== null) fields.push(m[1]);
528
+ return fields;
529
+ }
530
+
531
+ /** Build 2-3 mock items for array prop from component source (item type fields). */
532
+ function buildMockArrayItems(componentSource, itemTypeName, propName) {
533
+ const fields = getItemTypeFieldsFromSource(componentSource, itemTypeName);
534
+ const defaultFields = ["label", "value", "title", "id", "name", "description"];
535
+ const useFields = fields.length ? fields : defaultFields;
536
+ const items = [];
537
+ for (let i = 1; i <= 3; i++) {
538
+ const item = {};
539
+ for (const f of useFields) {
540
+ if (f === "id") item[f] = String(i);
541
+ else if (f === "value") item[f] = String(100 - i * 25);
542
+ else item[f] = `Example ${i}`;
543
+ }
544
+ items.push(item);
545
+ }
546
+ return items;
547
+ }
548
+
496
549
  /** Find first real usage of component in src/pages or src/app; return { propName: "literalValue", ... } or null. */
497
550
  function findComponentUsageInPages(componentName, projectRoot) {
498
551
  const srcDir = path.join(projectRoot, "src");
@@ -520,34 +573,57 @@ function findComponentUsageInPages(componentName, projectRoot) {
520
573
  return null;
521
574
  }
522
575
 
523
- /** Build default args lines and lucide-react imports. usageFromPages: sayfadan çıkarılan gerçek prop değerleri (yoksa fallback). */
524
- function buildDefaultArgsForRequiredProps(props, usageFromPages = null) {
576
+ /** Build default args lines, lucide imports, argTypes for icon (no raw dump), and needReact for ReactNode. */
577
+ function buildDefaultArgsForRequiredProps(props, usageFromPages = null, componentName = "", componentSource = "") {
525
578
  const argLines = [];
526
579
  const lucideImports = new Set();
580
+ const iconPropNames = [];
581
+ let needReact = false;
527
582
  const fromPages = usageFromPages && typeof usageFromPages === "object" ? usageFromPages : {};
583
+ const componentPlaceholders = (componentName && COMPONENT_PLACEHOLDERS[componentName]) || {};
528
584
  const added = new Set();
585
+
586
+ const stringFallback = (name) => {
587
+ if (fromPages[name] !== undefined && fromPages[name] !== null) return JSON.stringify(String(fromPages[name]));
588
+ if (componentPlaceholders[name] !== undefined) return JSON.stringify(componentPlaceholders[name]);
589
+ if (DEFAULT_STRINGS_BY_PROP_NAME[name] !== undefined) return JSON.stringify(DEFAULT_STRINGS_BY_PROP_NAME[name]);
590
+ return JSON.stringify("Example");
591
+ };
592
+
529
593
  if (Array.isArray(props) && props.length > 0) {
530
594
  for (const p of props) {
531
- if (p.required !== true) continue;
532
595
  const type = String(p.type || "").trim();
533
596
  const name = p.name;
597
+ const required = p.required === true;
598
+ if (/ReactNode|React\.ReactNode/.test(type)) {
599
+ const text = REACTNODE_PLACEHOLDER_TEXT[name] || componentPlaceholders[name] || "Content";
600
+ argLines.push(` ${name}: React.createElement('p', null, ${JSON.stringify(text)}),`);
601
+ needReact = true;
602
+ added.add(name);
603
+ continue;
604
+ }
605
+ if (!required) continue;
534
606
  if (/LucideIcon|lucide-react/.test(type)) {
535
607
  const iconName = LUCIDE_ICON_DEFAULT;
536
608
  lucideImports.add(iconName);
537
609
  argLines.push(` ${name}: ${iconName},`);
610
+ iconPropNames.push(name);
538
611
  added.add(name);
539
612
  } else if (/\[\]/.test(type)) {
540
613
  if (/string\s*\[\]/.test(type)) {
541
614
  argLines.push(` ${name}: ["Example"],`);
542
615
  } else {
543
- argLines.push(` ${name}: [],`);
616
+ const itemTypeMatch = type.match(/([A-Za-z0-9_]+)(?=\s*\[\])/);
617
+ const itemType = itemTypeMatch ? itemTypeMatch[1] : "Item";
618
+ const mockItems = buildMockArrayItems(componentSource, itemType, name);
619
+ argLines.push(` ${name}: ${JSON.stringify(mockItems)},`);
544
620
  }
545
621
  added.add(name);
546
622
  } else if (fromPages[name] !== undefined && fromPages[name] !== null) {
547
623
  argLines.push(` ${name}: ${JSON.stringify(String(fromPages[name]))},`);
548
624
  added.add(name);
549
- } else if (/string/.test(type) && DEFAULT_STRINGS_BY_PROP_NAME[name] !== undefined) {
550
- argLines.push(` ${name}: ${JSON.stringify(DEFAULT_STRINGS_BY_PROP_NAME[name])},`);
625
+ } else if (/string/.test(type)) {
626
+ argLines.push(` ${name}: ${stringFallback(name)},`);
551
627
  added.add(name);
552
628
  }
553
629
  }
@@ -559,7 +635,7 @@ function buildDefaultArgsForRequiredProps(props, usageFromPages = null) {
559
635
  argLines.push(` ${name}: ${JSON.stringify(val)},`);
560
636
  }
561
637
  }
562
- return { argLines, lucideImports: [...lucideImports] };
638
+ return { argLines, lucideImports: [...lucideImports], iconPropNames, needReact };
563
639
  }
564
640
 
565
641
  function getExampleItemForArrayType(itemType) {
@@ -678,9 +754,12 @@ function buildSpecialStories(componentName, variants) {
678
754
  return "";
679
755
  }
680
756
 
681
- function buildRecipeStoryContent(comp, componentName, importPath, title, source, exportStyle, recipe, defaultArgLines = [], lucideImports = []) {
757
+ function buildRecipeStoryContent(comp, componentName, importPath, title, source, exportStyle, recipe, defaultArgLines = [], lucideImports = [], iconPropNames = [], needReact = false) {
682
758
  const lines = [];
683
759
  lines.push(`import type { Meta, StoryObj } from "@storybook/react";`);
760
+ if (needReact) {
761
+ lines.push(`import React from "react";`);
762
+ }
684
763
  if (lucideImports.length > 0) {
685
764
  lines.push(`import { ${lucideImports.join(", ")} } from "lucide-react";`);
686
765
  }
@@ -725,6 +804,13 @@ function buildRecipeStoryContent(comp, componentName, importPath, title, source,
725
804
  lines.push(` title: ${JSON.stringify(title)},`);
726
805
  lines.push(` component: ComponentRef,`);
727
806
  lines.push(` tags: ["autodocs"],`);
807
+ if (iconPropNames && iconPropNames.length > 0) {
808
+ lines.push(` argTypes: {`);
809
+ for (const name of iconPropNames) {
810
+ lines.push(` ${name}: { control: false },`);
811
+ }
812
+ lines.push(` },`);
813
+ }
728
814
  lines.push(`} satisfies Meta<typeof ComponentRef>;`);
729
815
  lines.push("");
730
816
  lines.push(`export default meta;`);
@@ -799,10 +885,10 @@ function buildStoryFileContent(comp) {
799
885
  const omitChildren = componentWrapsVoidElement(source);
800
886
  const isPage = comp.file.startsWith("pages/");
801
887
 
802
- // Props: manifest or parse from source for default args (LucideIcon, array types)
888
+ // Props: manifest or parse from source for default args (LucideIcon, array types, ReactNode, contextual placeholders)
803
889
  const effectiveProps = Array.isArray(comp.props) && comp.props.length > 0 ? comp.props : parsePropsFromSource(source);
804
890
  const usageFromPages = findComponentUsageInPages(componentName, PROJECT_ROOT);
805
- const { argLines: defaultArgLines, lucideImports } = buildDefaultArgsForRequiredProps(effectiveProps, usageFromPages);
891
+ const { argLines: defaultArgLines, lucideImports, iconPropNames, needReact } = buildDefaultArgsForRequiredProps(effectiveProps, usageFromPages, componentName, source);
806
892
  const useReactNodeChildrenRender = !omitChildren && hasChildrenPropReactNode(effectiveProps);
807
893
 
808
894
  // Skip story only if not a page and no export found
@@ -811,12 +897,12 @@ function buildStoryFileContent(comp) {
811
897
  }
812
898
 
813
899
  if (RECIPES[componentName]) {
814
- return buildRecipeStoryContent(comp, componentName, importPath, title, source, exportStyle, RECIPES[componentName], defaultArgLines, lucideImports);
900
+ return buildRecipeStoryContent(comp, componentName, importPath, title, source, exportStyle, RECIPES[componentName], defaultArgLines, lucideImports, iconPropNames, needReact);
815
901
  }
816
902
 
817
903
  const lines = [];
818
904
  lines.push(`import type { Meta, StoryObj } from "@storybook/react";`);
819
- if (useReactNodeChildrenRender) {
905
+ if (useReactNodeChildrenRender || needReact) {
820
906
  lines.push(`import React from "react";`);
821
907
  }
822
908
  if (lucideImports.length > 0) {
@@ -846,6 +932,13 @@ function buildStoryFileContent(comp) {
846
932
  lines.push(` title: ${JSON.stringify(title)},`);
847
933
  lines.push(` component: ComponentRef,`);
848
934
  lines.push(` tags: ["autodocs"],`);
935
+ if (iconPropNames && iconPropNames.length > 0) {
936
+ lines.push(` argTypes: {`);
937
+ for (const name of iconPropNames) {
938
+ lines.push(` ${name}: { control: false },`);
939
+ }
940
+ lines.push(` },`);
941
+ }
849
942
  lines.push(`} satisfies Meta<typeof ComponentRef>;`);
850
943
  lines.push("");
851
944
  lines.push(`export default meta;`);