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