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
|
@@ -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
|
|
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
|
-
|
|
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)
|
|
550
|
-
argLines.push(` ${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;`);
|