vibe-design-system 2.5.20 → 2.5.22

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.22",
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,56 @@ 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}: ${JSON.stringify(text)},`);
601
+ added.add(name);
602
+ continue;
603
+ }
604
+ if (!required) continue;
534
605
  if (/LucideIcon|lucide-react/.test(type)) {
535
606
  const iconName = LUCIDE_ICON_DEFAULT;
536
607
  lucideImports.add(iconName);
537
608
  argLines.push(` ${name}: ${iconName},`);
609
+ iconPropNames.push(name);
538
610
  added.add(name);
539
611
  } else if (/\[\]/.test(type)) {
540
612
  if (/string\s*\[\]/.test(type)) {
541
613
  argLines.push(` ${name}: ["Example"],`);
542
614
  } else {
543
- argLines.push(` ${name}: [],`);
615
+ const itemTypeMatch = type.match(/([A-Za-z0-9_]+)(?=\s*\[\])/);
616
+ const itemType = itemTypeMatch ? itemTypeMatch[1] : "Item";
617
+ const mockItems = buildMockArrayItems(componentSource, itemType, name);
618
+ argLines.push(` ${name}: ${JSON.stringify(mockItems)},`);
544
619
  }
545
620
  added.add(name);
546
621
  } else if (fromPages[name] !== undefined && fromPages[name] !== null) {
547
622
  argLines.push(` ${name}: ${JSON.stringify(String(fromPages[name]))},`);
548
623
  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])},`);
624
+ } else if (/string/.test(type)) {
625
+ argLines.push(` ${name}: ${stringFallback(name)},`);
551
626
  added.add(name);
552
627
  }
553
628
  }
@@ -559,7 +634,7 @@ function buildDefaultArgsForRequiredProps(props, usageFromPages = null) {
559
634
  argLines.push(` ${name}: ${JSON.stringify(val)},`);
560
635
  }
561
636
  }
562
- return { argLines, lucideImports: [...lucideImports] };
637
+ return { argLines, lucideImports: [...lucideImports], iconPropNames, needReact };
563
638
  }
564
639
 
565
640
  function getExampleItemForArrayType(itemType) {
@@ -678,9 +753,12 @@ function buildSpecialStories(componentName, variants) {
678
753
  return "";
679
754
  }
680
755
 
681
- function buildRecipeStoryContent(comp, componentName, importPath, title, source, exportStyle, recipe, defaultArgLines = [], lucideImports = []) {
756
+ function buildRecipeStoryContent(comp, componentName, importPath, title, source, exportStyle, recipe, defaultArgLines = [], lucideImports = [], iconPropNames = [], needReact = false) {
682
757
  const lines = [];
683
758
  lines.push(`import type { Meta, StoryObj } from "@storybook/react";`);
759
+ if (needReact) {
760
+ lines.push(`import React from "react";`);
761
+ }
684
762
  if (lucideImports.length > 0) {
685
763
  lines.push(`import { ${lucideImports.join(", ")} } from "lucide-react";`);
686
764
  }
@@ -725,6 +803,13 @@ function buildRecipeStoryContent(comp, componentName, importPath, title, source,
725
803
  lines.push(` title: ${JSON.stringify(title)},`);
726
804
  lines.push(` component: ComponentRef,`);
727
805
  lines.push(` tags: ["autodocs"],`);
806
+ if (iconPropNames && iconPropNames.length > 0) {
807
+ lines.push(` argTypes: {`);
808
+ for (const name of iconPropNames) {
809
+ lines.push(` ${name}: { control: false },`);
810
+ }
811
+ lines.push(` },`);
812
+ }
728
813
  lines.push(`} satisfies Meta<typeof ComponentRef>;`);
729
814
  lines.push("");
730
815
  lines.push(`export default meta;`);
@@ -799,10 +884,10 @@ function buildStoryFileContent(comp) {
799
884
  const omitChildren = componentWrapsVoidElement(source);
800
885
  const isPage = comp.file.startsWith("pages/");
801
886
 
802
- // Props: manifest or parse from source for default args (LucideIcon, array types)
887
+ // Props: manifest or parse from source for default args (LucideIcon, array types, ReactNode, contextual placeholders)
803
888
  const effectiveProps = Array.isArray(comp.props) && comp.props.length > 0 ? comp.props : parsePropsFromSource(source);
804
889
  const usageFromPages = findComponentUsageInPages(componentName, PROJECT_ROOT);
805
- const { argLines: defaultArgLines, lucideImports } = buildDefaultArgsForRequiredProps(effectiveProps, usageFromPages);
890
+ const { argLines: defaultArgLines, lucideImports, iconPropNames, needReact } = buildDefaultArgsForRequiredProps(effectiveProps, usageFromPages, componentName, source);
806
891
  const useReactNodeChildrenRender = !omitChildren && hasChildrenPropReactNode(effectiveProps);
807
892
 
808
893
  // Skip story only if not a page and no export found
@@ -811,12 +896,12 @@ function buildStoryFileContent(comp) {
811
896
  }
812
897
 
813
898
  if (RECIPES[componentName]) {
814
- return buildRecipeStoryContent(comp, componentName, importPath, title, source, exportStyle, RECIPES[componentName], defaultArgLines, lucideImports);
899
+ return buildRecipeStoryContent(comp, componentName, importPath, title, source, exportStyle, RECIPES[componentName], defaultArgLines, lucideImports, iconPropNames, needReact);
815
900
  }
816
901
 
817
902
  const lines = [];
818
903
  lines.push(`import type { Meta, StoryObj } from "@storybook/react";`);
819
- if (useReactNodeChildrenRender) {
904
+ if (useReactNodeChildrenRender || needReact) {
820
905
  lines.push(`import React from "react";`);
821
906
  }
822
907
  if (lucideImports.length > 0) {
@@ -846,6 +931,13 @@ function buildStoryFileContent(comp) {
846
931
  lines.push(` title: ${JSON.stringify(title)},`);
847
932
  lines.push(` component: ComponentRef,`);
848
933
  lines.push(` tags: ["autodocs"],`);
934
+ if (iconPropNames && iconPropNames.length > 0) {
935
+ lines.push(` argTypes: {`);
936
+ for (const name of iconPropNames) {
937
+ lines.push(` ${name}: { control: false },`);
938
+ }
939
+ lines.push(` },`);
940
+ }
849
941
  lines.push(`} satisfies Meta<typeof ComponentRef>;`);
850
942
  lines.push("");
851
943
  lines.push(`export default meta;`);