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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-design-system",
3
- "version": "2.5.27",
3
+ "version": "2.5.29",
4
4
  "description": "Auto-generate design systems for vibe coding projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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:** If components use React context (e.g. `useTimer`, `useSidebar`, `useCircles`, drag-drop context), they will error in Storybook unless wrapped with the right providers. In your project, add decorators in `.storybook/preview.tsx` to wrap all stories (or per-story) with your app’s providers (e.g. `TimerProvider`, `SidebarProvider`, `CirclesProvider`). See [Storybook decorators](https://storybook.js.org/docs/writing-stories/decorators).
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). Returns import path from project root for @/ alias: "contexts/TimerContext" */
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
- return "@/" + withoutExt;
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
- return null;
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 alreadyHas = providersToAdd.every((p) => content.includes(p.name));
148
- if (!alreadyHas) {
149
- const importLines = [...new Map(providersToAdd.map((p) => [p.name, p])).values()]
150
- .map((p) => `import { ${p.name} } from "${p.importPath}";`)
151
- .join("\n");
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 withProvidersCode = buildWithProvidersCode(providersToAdd);
157
- const idxPreview = content.indexOf("const preview");
158
- const idxWithProviders = content.indexOf("const withProviders");
159
- const wrongOrder = idxWithProviders !== -1 && idxPreview !== -1 && idxWithProviders > idxPreview;
160
- if (wrongOrder) {
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
- content = content.replace(/(\s*)(const preview\s*[^=]*=\s*\{)/, withProvidersCode + "\n\n$1$2");
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
- content = content.replace(/decorators:\s*\[/, "decorators: [withProviders, ");
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: " + (wrongOrder ? "fixed withProviders order." : "added " + providersToAdd.map((p) => p.name).join(", ")));
180
+ console.log("[VDS] Storybook preview: " + uniqueProviders.map((p) => p.name).join(", "));
182
181
  }
183
182
 
184
183
  if (hooksWithoutProvider.size > 0) {