vibe-design-system 2.5.27 → 2.5.29
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
|
@@ -56,7 +56,7 @@ To see **live renders** of components in the dashboard (isolated, no app chrome)
|
|
|
56
56
|
"vds:stories": "node vds-core/story-generator.mjs"
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
2. **Context providers:**
|
|
59
|
+
2. **Context providers:** Run `node vds-core/setup-storybook-providers.mjs` to detect hooks (useTimer, useSidebar, useCircles, useAuth, useSearch) and inject the matching providers into `.storybook/preview.ts`. The script prefers the provider from the same file as the hook (e.g. `context/SidebarContext.tsx` for useSidebar over `components/ui/sidebar`). If a component still errors with "Cannot read properties of undefined (reading 'x')", add default args or mock context state in that component’s story.
|
|
60
60
|
|
|
61
61
|
3. **Icons:** The Foundations/Icons story lists only icons that are imported from `lucide-react` in your app code (src/, excluding `src/stories`), so it reflects real usage.
|
|
62
62
|
|
|
@@ -22,6 +22,7 @@ const HOOK_TO_PROVIDER = {
|
|
|
22
22
|
useCircles: "CirclesProvider",
|
|
23
23
|
useSidebar: "SidebarProvider",
|
|
24
24
|
useAuth: "AuthProvider",
|
|
25
|
+
useSearch: "SearchProvider",
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
const IGNORE_DIRS = new Set(["node_modules", "dist", ".next", "build", ".storybook", "stories"]);
|
|
@@ -67,24 +68,29 @@ function detectHooksUsedInProject(projectRoot) {
|
|
|
67
68
|
return byFile;
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
/** Find file that exports providerName (e.g. TimerProvider).
|
|
71
|
-
function findProviderExportPath(projectRoot, providerName) {
|
|
71
|
+
/** Find file that exports providerName (e.g. TimerProvider). If hookName is given, prefer a file that also contains that hook (e.g. context/SidebarContext.tsx for useSidebar over ui/sidebar). */
|
|
72
|
+
function findProviderExportPath(projectRoot, providerName, hookName) {
|
|
72
73
|
const srcDir = path.join(projectRoot, "src");
|
|
73
74
|
const files = getAllSourceFiles(srcDir, srcDir).map((r) => path.join("src", r));
|
|
74
75
|
const exportRe = new RegExp(
|
|
75
76
|
"export\\s+(?:default\\s+)?(?:const|function|class)\\s+" + providerName + "\\b|export\\s*\\{[^}]*\\b" + providerName + "\\b[^}]*\\}"
|
|
76
77
|
);
|
|
78
|
+
const candidates = [];
|
|
77
79
|
for (const rel of files) {
|
|
78
80
|
const full = path.join(projectRoot, rel);
|
|
79
81
|
try {
|
|
80
82
|
const content = fs.readFileSync(full, "utf-8");
|
|
81
83
|
if (exportRe.test(content)) {
|
|
82
84
|
const withoutExt = rel.replace(/\.(tsx?|jsx?)$/i, "").replace(/^src\/?/, "");
|
|
83
|
-
|
|
85
|
+
const pathForImport = "@/" + withoutExt;
|
|
86
|
+
const hasHook = hookName && new RegExp("\\b" + hookName + "\\b").test(content);
|
|
87
|
+
candidates.push({ pathForImport, hasHook });
|
|
84
88
|
}
|
|
85
89
|
} catch (_) {}
|
|
86
90
|
}
|
|
87
|
-
|
|
91
|
+
const preferred = hookName ? candidates.find((c) => c.hasHook) : null;
|
|
92
|
+
const pick = preferred || candidates[0];
|
|
93
|
+
return pick ? pick.pathForImport : null;
|
|
88
94
|
}
|
|
89
95
|
|
|
90
96
|
function getPreviewPath(projectRoot) {
|
|
@@ -110,7 +116,7 @@ function collectProvidersAndWarnings(projectRoot) {
|
|
|
110
116
|
const hooksWithoutProvider = new Set();
|
|
111
117
|
for (const hook of allHooksUsed) {
|
|
112
118
|
const providerName = HOOK_TO_PROVIDER[hook];
|
|
113
|
-
const importPath = findProviderExportPath(projectRoot, providerName);
|
|
119
|
+
const importPath = findProviderExportPath(projectRoot, providerName, hook);
|
|
114
120
|
if (importPath) {
|
|
115
121
|
if (!providersToAdd.some((p) => p.name === providerName)) {
|
|
116
122
|
providersToAdd.push({ name: providerName, importPath });
|
|
@@ -144,41 +150,34 @@ function injectProviderDecorators(projectRoot) {
|
|
|
144
150
|
|
|
145
151
|
if (providersToAdd.length > 0) {
|
|
146
152
|
let content = fs.readFileSync(previewPath, "utf-8");
|
|
147
|
-
const
|
|
148
|
-
if (!
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const lastImportIdx = content.search(/\nimport\s+.+?;\s*$/m);
|
|
153
|
-
const insertAt = lastImportIdx >= 0 ? content.indexOf("\n", lastImportIdx) + 1 : content.indexOf("\n") + 1;
|
|
154
|
-
content = content.slice(0, insertAt) + importLines + "\n" + content.slice(insertAt);
|
|
153
|
+
const uniqueProviders = [...new Map(providersToAdd.map((p) => [p.name, p])).values()];
|
|
154
|
+
if (!content.includes("import React") && !content.includes("import React from")) {
|
|
155
|
+
const firstImport = content.match(/^import\s+/m);
|
|
156
|
+
const insertAt = firstImport ? content.indexOf(firstImport[0]) : 0;
|
|
157
|
+
content = content.slice(0, insertAt) + "import React from \"react\";\n" + content.slice(insertAt);
|
|
155
158
|
}
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const blockMatch = content.match(/const withProviders\s*=[\s\S]+?;\s*\n/);
|
|
162
|
-
if (blockMatch) {
|
|
163
|
-
const block = blockMatch[0].trim();
|
|
164
|
-
content = content.replace(blockMatch[0], "").replace(/\n{3,}/g, "\n\n");
|
|
165
|
-
content = content.replace(/(\s*)(const preview\s*[^=]*=\s*\{)/, block + "\n\n$1$2");
|
|
166
|
-
}
|
|
167
|
-
} else if (!content.includes("withProviders")) {
|
|
168
|
-
if (!content.includes("import React")) {
|
|
169
|
-
const firstImport = content.match(/^import\s+/m);
|
|
170
|
-
const insertAt = firstImport ? content.indexOf(firstImport[0]) : 0;
|
|
171
|
-
content = content.slice(0, insertAt) + "import React from \"react\";\n" + content.slice(insertAt);
|
|
159
|
+
for (const p of uniqueProviders) {
|
|
160
|
+
if (!content.includes(p.name)) {
|
|
161
|
+
const lastImportIdx = content.search(/\nimport\s+.+?;\s*$/m);
|
|
162
|
+
const insertAt = lastImportIdx >= 0 ? content.indexOf("\n", lastImportIdx) + 1 : content.indexOf("\n") + 1;
|
|
163
|
+
content = content.slice(0, insertAt) + `import { ${p.name} } from "${p.importPath}";\n` + content.slice(insertAt);
|
|
172
164
|
}
|
|
173
|
-
|
|
165
|
+
}
|
|
166
|
+
content = content.replace(/const withProviders\s*=\s*\([^)]*\)\s*=>\s*[\s\S]+?;\s*\n?/g, "").replace(/\n{3,}/g, "\n\n");
|
|
167
|
+
const withProvidersCode = buildWithProvidersCode(providersToAdd);
|
|
168
|
+
const insertBefore = content.indexOf("const preview");
|
|
169
|
+
if (insertBefore !== -1) {
|
|
170
|
+
content = content.slice(0, insertBefore) + withProvidersCode + "\n\n" + content.slice(insertBefore);
|
|
174
171
|
if (content.includes("decorators:")) {
|
|
175
|
-
|
|
172
|
+
if (!content.includes("decorators: [withProviders")) {
|
|
173
|
+
content = content.replace(/decorators:\s*\[/, "decorators: [withProviders, ");
|
|
174
|
+
}
|
|
176
175
|
} else {
|
|
177
176
|
content = content.replace(/(const preview\s*[^=]*=\s*\{\s*)/, "$1\n decorators: [withProviders],\n ");
|
|
178
177
|
}
|
|
179
178
|
}
|
|
180
179
|
fs.writeFileSync(previewPath, content, "utf-8");
|
|
181
|
-
console.log("[VDS] Storybook preview: " +
|
|
180
|
+
console.log("[VDS] Storybook preview: " + uniqueProviders.map((p) => p.name).join(", "));
|
|
182
181
|
}
|
|
183
182
|
|
|
184
183
|
if (hooksWithoutProvider.size > 0) {
|