vibe-design-system 2.8.76 → 2.8.79
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
|
@@ -1429,11 +1429,13 @@ function extractFoundations() {
|
|
|
1429
1429
|
}
|
|
1430
1430
|
}
|
|
1431
1431
|
|
|
1432
|
-
|
|
1432
|
+
// body { font-family } — supports nested @layer base { body { } } structure
|
|
1433
|
+
const bodyMatch = css.match(/\bbody\s*\{[\s\S]*?font-family:\s*([^;]+);/) ||
|
|
1434
|
+
css.match(/body\s*\{[^}]*font-family:\s*([^;]+);/s);
|
|
1433
1435
|
if (bodyMatch) typography.body = bodyMatch[1].trim();
|
|
1434
1436
|
const monoMatch = css.match(/code,\s*pre,\s*\.font-mono\s*\{[^}]*font-family:\s*([^;]+);/s);
|
|
1435
1437
|
if (monoMatch) typography.mono = monoMatch[1].trim();
|
|
1436
|
-
//
|
|
1438
|
+
// CSS custom properties for fonts (--font-sans, --font-mono, --font-display, etc.)
|
|
1437
1439
|
const fontVarRe = /--font([\w-]*):\s*([^;\n]+);/g;
|
|
1438
1440
|
let fvm;
|
|
1439
1441
|
while ((fvm = fontVarRe.exec(css)) !== null) {
|
|
@@ -1441,6 +1443,18 @@ function extractFoundations() {
|
|
|
1441
1443
|
const val = fvm[2].trim();
|
|
1442
1444
|
if (!typography[key]) typography[key] = val;
|
|
1443
1445
|
}
|
|
1446
|
+
// Resolve var(--font-*) references in body/mono to actual font names
|
|
1447
|
+
// Key construction must match the fontVarRe loop: --font-sans → "font-sans" → "fontSans"
|
|
1448
|
+
function resolveTypoVar(val) {
|
|
1449
|
+
if (!val || !val.startsWith("var(")) return val;
|
|
1450
|
+
const m = val.match(/var\(--font-([\w-]+)\)/);
|
|
1451
|
+
if (!m) return val;
|
|
1452
|
+
// prepend "-" so camelCase conversion matches: "font-" + "sans" → "fontSans"
|
|
1453
|
+
const key = `font-${m[1]}`.replace(/-([a-zA-Z])/g, (_, c) => c.toUpperCase());
|
|
1454
|
+
return (typography[key] && !typography[key].startsWith("var(")) ? typography[key] : val;
|
|
1455
|
+
}
|
|
1456
|
+
if (typography.body) typography.body = resolveTypoVar(typography.body);
|
|
1457
|
+
if (typography.mono) typography.mono = resolveTypoVar(typography.mono);
|
|
1444
1458
|
|
|
1445
1459
|
// ── Type Scale: extract --text-* font sizes + line-heights from compiled CSS
|
|
1446
1460
|
const textSteps = ["xs","sm","base","lg","xl","2xl","3xl","4xl","5xl","6xl","7xl","8xl","9xl"];
|
|
@@ -1915,6 +1929,33 @@ function extractFoundations() {
|
|
|
1915
1929
|
}
|
|
1916
1930
|
}
|
|
1917
1931
|
|
|
1932
|
+
// ── Scan source files for arbitrary Tailwind [font-family:'FontName'] usage ──
|
|
1933
|
+
// Covers projects like Blurple where the font is only set via arbitrary Tailwind classes
|
|
1934
|
+
if (SRC_DIR && fs.existsSync(SRC_DIR)) {
|
|
1935
|
+
const arbFonts = new Set();
|
|
1936
|
+
const arbRe = /\[font-family:\s*'([^']+)'/g;
|
|
1937
|
+
function scanSrcForFonts(dir) {
|
|
1938
|
+
if (!fs.existsSync(dir)) return;
|
|
1939
|
+
let entries;
|
|
1940
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch (_) { return; }
|
|
1941
|
+
for (const e of entries) {
|
|
1942
|
+
const full = path.join(dir, e.name);
|
|
1943
|
+
if (e.isDirectory()) {
|
|
1944
|
+
if (!IGNORE_DIRS.includes(e.name)) scanSrcForFonts(full);
|
|
1945
|
+
} else if (/\.(tsx|jsx|ts|js|css)$/i.test(e.name)) {
|
|
1946
|
+
try {
|
|
1947
|
+
const content = fs.readFileSync(full, "utf-8");
|
|
1948
|
+
arbRe.lastIndex = 0;
|
|
1949
|
+
let m;
|
|
1950
|
+
while ((m = arbRe.exec(content)) !== null) arbFonts.add(m[1]);
|
|
1951
|
+
} catch (_) {}
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
scanSrcForFonts(SRC_DIR);
|
|
1956
|
+
if (arbFonts.size > 0) typography.arbitraryFonts = Array.from(arbFonts);
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1918
1959
|
return {
|
|
1919
1960
|
colors: foundationsColors,
|
|
1920
1961
|
typography,
|
|
@@ -2106,6 +2147,66 @@ function extractButtonUsage() {
|
|
|
2106
2147
|
};
|
|
2107
2148
|
}
|
|
2108
2149
|
|
|
2150
|
+
/**
|
|
2151
|
+
* Scan all non-component source files and count how many times each variant value
|
|
2152
|
+
* is explicitly used for each component (e.g. <Button variant="destructive">).
|
|
2153
|
+
* Returns a map: { [compName]: { [variantValue]: count } }
|
|
2154
|
+
* Only static string literals are counted; dynamic values are ignored.
|
|
2155
|
+
*/
|
|
2156
|
+
function extractVariantUsage(componentResults) {
|
|
2157
|
+
if (!SRC_DIR || !fs.existsSync(SRC_DIR)) return;
|
|
2158
|
+
const compDir = COMPONENTS_DIR ? path.resolve(COMPONENTS_DIR) : null;
|
|
2159
|
+
|
|
2160
|
+
// Collect all .tsx/.jsx/.ts/.js files in SRC_DIR that are NOT inside COMPONENTS_DIR
|
|
2161
|
+
function collectNonCompFiles(dir, acc = []) {
|
|
2162
|
+
if (!fs.existsSync(dir)) return acc;
|
|
2163
|
+
let entries;
|
|
2164
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch (_) { return acc; }
|
|
2165
|
+
for (const e of entries) {
|
|
2166
|
+
const full = path.join(dir, e.name);
|
|
2167
|
+
if (e.isDirectory()) {
|
|
2168
|
+
if (IGNORE_DIRS.includes(e.name)) continue;
|
|
2169
|
+
if (compDir && path.resolve(full) === compDir) continue; // skip components dir
|
|
2170
|
+
collectNonCompFiles(full, acc);
|
|
2171
|
+
} else if (e.isFile() && /\.(tsx|jsx|ts|js)$/i.test(e.name) &&
|
|
2172
|
+
!e.name.endsWith(".stories.tsx") && !e.name.endsWith(".stories.ts") &&
|
|
2173
|
+
!e.name.endsWith(".test.tsx") && !e.name.endsWith(".test.ts") &&
|
|
2174
|
+
!e.name.endsWith(".spec.tsx") && !e.name.endsWith(".spec.ts")) {
|
|
2175
|
+
acc.push(full);
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
return acc;
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
const nonCompFiles = collectNonCompFiles(SRC_DIR);
|
|
2182
|
+
if (nonCompFiles.length === 0) return;
|
|
2183
|
+
|
|
2184
|
+
for (const comp of componentResults) {
|
|
2185
|
+
const name = comp.name;
|
|
2186
|
+
// Match <ComponentName ...variant="X" or variant='X' anywhere on the same tag
|
|
2187
|
+
// Simple regex: looks for <Name followed (anywhere on the line) by variant="X"
|
|
2188
|
+
const variantRe = new RegExp(
|
|
2189
|
+
`<${name}[\\s\\S]{0,300}?\\bvariant=(?:"([^"]+)"|'([^']+)')`,
|
|
2190
|
+
"g"
|
|
2191
|
+
);
|
|
2192
|
+
const usage = {};
|
|
2193
|
+
for (const file of nonCompFiles) {
|
|
2194
|
+
let content;
|
|
2195
|
+
try { content = fs.readFileSync(file, "utf-8"); } catch (_) { continue; }
|
|
2196
|
+
// Reset lastIndex for global regex per file
|
|
2197
|
+
variantRe.lastIndex = 0;
|
|
2198
|
+
let m;
|
|
2199
|
+
while ((m = variantRe.exec(content)) !== null) {
|
|
2200
|
+
const val = m[1] || m[2];
|
|
2201
|
+
if (val) usage[val] = (usage[val] || 0) + 1;
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
if (Object.keys(usage).length > 0) {
|
|
2205
|
+
comp.variantUsage = usage;
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2109
2210
|
function scan() {
|
|
2110
2211
|
const relativeFiles = COMPONENTS_DIR ? getAllComponentFiles(COMPONENTS_DIR) : [];
|
|
2111
2212
|
if (!COMPONENTS_DIR) {
|
|
@@ -2149,6 +2250,9 @@ function scan() {
|
|
|
2149
2250
|
});
|
|
2150
2251
|
}
|
|
2151
2252
|
}
|
|
2253
|
+
// Attach real-world variant usage (which variant values are actually passed in pages/layouts)
|
|
2254
|
+
extractVariantUsage(results);
|
|
2255
|
+
|
|
2152
2256
|
const foundations = extractFoundations();
|
|
2153
2257
|
foundations.icons = extractLucideIconsUsed(SRC_DIR);
|
|
2154
2258
|
foundations.brand = { assets: extractBrandAssets() };
|
|
@@ -679,7 +679,7 @@ function buildComponentVariantMap(source, effectiveProps) {
|
|
|
679
679
|
/**
|
|
680
680
|
* Generate multi-dimension stories: individual per variant, per size, per state, + AllVariants grid.
|
|
681
681
|
*/
|
|
682
|
-
function buildMultiDimensionStories(variantMap, renderLine, childrenArgLine, defaultArgLines, componentName) {
|
|
682
|
+
function buildMultiDimensionStories(variantMap, renderLine, childrenArgLine, defaultArgLines, componentName, variantUsage) {
|
|
683
683
|
const lines = [];
|
|
684
684
|
const { dimensions, booleanStates, primaryDimension, secondaryDimensions } = variantMap;
|
|
685
685
|
|
|
@@ -690,10 +690,26 @@ function buildMultiDimensionStories(variantMap, renderLine, childrenArgLine, def
|
|
|
690
690
|
return !match || !allDimNames.has(match[1]);
|
|
691
691
|
});
|
|
692
692
|
|
|
693
|
+
// Pre-compute filtered primary values (used in sections A and D).
|
|
694
|
+
// variantUsage is comp.variantUsage: { [variantValue]: count } (only for "variant" prop)
|
|
695
|
+
// If usage data exists, keep only values that were actually used + the default value.
|
|
696
|
+
// If no usage data exists (old scans / dynamic-only usage) → keep all values.
|
|
697
|
+
let filteredPrimaryValues = null;
|
|
698
|
+
if (primaryDimension && dimensions[primaryDimension]) {
|
|
699
|
+
const _pd = dimensions[primaryDimension];
|
|
700
|
+
const _def = _pd.default || _pd.values[0];
|
|
701
|
+
// Only apply filtering when the primary dimension is "variant" (that's what we scan)
|
|
702
|
+
const _umap = (primaryDimension === "variant" && variantUsage && Object.keys(variantUsage).length > 0)
|
|
703
|
+
? variantUsage : null;
|
|
704
|
+
filteredPrimaryValues = _umap
|
|
705
|
+
? _pd.values.filter(v => v === _def || _umap[v])
|
|
706
|
+
: _pd.values;
|
|
707
|
+
}
|
|
708
|
+
|
|
693
709
|
// --- A. Primary dimension stories (one per value) ---
|
|
694
710
|
if (primaryDimension && dimensions[primaryDimension]) {
|
|
695
|
-
const dim = dimensions[primaryDimension];
|
|
696
|
-
const defaultValue = dim.default ||
|
|
711
|
+
const dim = { ...dimensions[primaryDimension], values: filteredPrimaryValues || dimensions[primaryDimension].values };
|
|
712
|
+
const defaultValue = dim.default || dimensions[primaryDimension].values[0];
|
|
697
713
|
|
|
698
714
|
// Default story uses the default value
|
|
699
715
|
lines.push(`export const Default: Story = {`);
|
|
@@ -706,7 +722,7 @@ function buildMultiDimensionStories(variantMap, renderLine, childrenArgLine, def
|
|
|
706
722
|
lines.push(`};`);
|
|
707
723
|
lines.push("");
|
|
708
724
|
|
|
709
|
-
// Remaining values get their own stories
|
|
725
|
+
// Remaining values get their own stories (filtered by actual project usage)
|
|
710
726
|
for (const val of dim.values) {
|
|
711
727
|
if (val === defaultValue) continue;
|
|
712
728
|
const storyName = capitalize(val);
|
|
@@ -778,9 +794,9 @@ function buildMultiDimensionStories(variantMap, renderLine, childrenArgLine, def
|
|
|
778
794
|
lines.push("");
|
|
779
795
|
}
|
|
780
796
|
|
|
781
|
-
// --- D. AllVariants grid story (only if primary has 2+ values) ---
|
|
782
|
-
if (primaryDimension &&
|
|
783
|
-
const primaryValues =
|
|
797
|
+
// --- D. AllVariants grid story (only if primary has 2+ used values) ---
|
|
798
|
+
if (primaryDimension && filteredPrimaryValues && filteredPrimaryValues.length >= 2) {
|
|
799
|
+
const primaryValues = filteredPrimaryValues;
|
|
784
800
|
const secondaryDim = secondaryDimensions.find((d) => dimensions[d] && dimensions[d].values.length >= 2);
|
|
785
801
|
|
|
786
802
|
lines.push(`export const AllVariants: Story = {`);
|
|
@@ -2116,7 +2132,8 @@ function buildStoryFileContent(comp) {
|
|
|
2116
2132
|
|
|
2117
2133
|
if (hasDimensions) {
|
|
2118
2134
|
const multiStories = buildMultiDimensionStories(
|
|
2119
|
-
variantMap, renderLine, childrenArgLine, defaultArgLines, componentName
|
|
2135
|
+
variantMap, renderLine, childrenArgLine, defaultArgLines, componentName,
|
|
2136
|
+
comp.variantUsage || null
|
|
2120
2137
|
);
|
|
2121
2138
|
if (multiStories) {
|
|
2122
2139
|
lines.push(multiStories);
|
|
@@ -2636,15 +2653,34 @@ function writeFoundationsStories(foundations, components) {
|
|
|
2636
2653
|
const usedTextSizes = (foundations?.tokenUsage?.textSizes || []);
|
|
2637
2654
|
const usedTextMap = Object.fromEntries(usedTextSizes.map(({ token, count }) => [token, count]));
|
|
2638
2655
|
|
|
2639
|
-
// Font families
|
|
2656
|
+
// Font families — build from known keys, then add arbitrary fonts found in source
|
|
2640
2657
|
const fontFamilyKeys = ["fontSans", "fontMono", "body", "heading", "tailwindSans", "tailwindMono"];
|
|
2641
|
-
const
|
|
2642
|
-
.filter(([k]) =>
|
|
2658
|
+
const rawFamilyRows = Object.entries(typo)
|
|
2659
|
+
.filter(([k]) => k !== "arbitraryFonts" &&
|
|
2660
|
+
(fontFamilyKeys.includes(k) || (k.toLowerCase().includes("font") && !k.toLowerCase().includes("weight") && k !== "typeScale" && k !== "fontSize")))
|
|
2643
2661
|
.map(([k, v]) => {
|
|
2644
2662
|
const val = typeof v === "object" && v !== null ? (v.family || v.value || "") : Array.isArray(v) ? v.join(", ") : String(v);
|
|
2645
2663
|
return { token: k, value: val };
|
|
2646
2664
|
})
|
|
2647
|
-
.filter((r) => r.value);
|
|
2665
|
+
.filter((r) => r.value && !r.value.startsWith("var(")); // drop unresolved CSS var refs
|
|
2666
|
+
|
|
2667
|
+
// Add arbitrary [font-family:'Name'] fonts found in source files (e.g. Blurple's Urbanist)
|
|
2668
|
+
if (Array.isArray(typo.arbitraryFonts)) {
|
|
2669
|
+
for (const f of typo.arbitraryFonts) {
|
|
2670
|
+
if (!rawFamilyRows.some((r) => r.value.includes(f))) {
|
|
2671
|
+
rawFamilyRows.push({ token: "projectFont", value: `'${f}', sans-serif` });
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
|
|
2676
|
+
// Deduplicate: keep only the first entry per unique primary font name
|
|
2677
|
+
const seenFontNames = new Set();
|
|
2678
|
+
const familyRows = rawFamilyRows.filter((r) => {
|
|
2679
|
+
const primary = r.value.split(",")[0].trim().replace(/['"]/g, "").toLowerCase();
|
|
2680
|
+
if (seenFontNames.has(primary)) return false;
|
|
2681
|
+
seenFontNames.add(primary);
|
|
2682
|
+
return true;
|
|
2683
|
+
});
|
|
2648
2684
|
|
|
2649
2685
|
// Font weights — check fontWeightScale (from twTheme.fontWeight), then legacy specific keys, then Tailwind defaults
|
|
2650
2686
|
const weightKeys = ["fontWeightNormal", "fontWeightMedium", "fontWeightSemibold", "fontWeightBold"];
|
|
@@ -2667,7 +2703,9 @@ function writeFoundationsStories(foundations, components) {
|
|
|
2667
2703
|
// Reverse type scale for display (largest first)
|
|
2668
2704
|
const scaleDesc = [...typeScale].reverse();
|
|
2669
2705
|
|
|
2670
|
-
const
|
|
2706
|
+
const firstArbitraryFont = Array.isArray(typo.arbitraryFonts) && typo.arbitraryFonts.length > 0
|
|
2707
|
+
? `'${typo.arbitraryFonts[0]}', sans-serif` : null;
|
|
2708
|
+
const sansFamily = typo.fontSans || typo.body || firstArbitraryFont || typo.tailwindSans || "system-ui, sans-serif";
|
|
2671
2709
|
|
|
2672
2710
|
const typoContent = [
|
|
2673
2711
|
"import React from \"react\";",
|
|
@@ -262,6 +262,89 @@ function injectViteConfigAliases(projectRoot) {
|
|
|
262
262
|
console.log(`[VDS] Storybook adapt: injected vite aliases: ${missingAliases.map(([k]) => k).join(", ")}`);
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
// ============================================================
|
|
266
|
+
// VERSION-PINNED IMPORT HANDLING (Bolt/Lovable/AI-generated)
|
|
267
|
+
// ============================================================
|
|
268
|
+
|
|
269
|
+
/** Detect if import specifier has a @version suffix (e.g. @radix-ui/react-slot@1.1.2) */
|
|
270
|
+
function isVersionPinned(spec) {
|
|
271
|
+
return /^(?:@[\w.-]+\/)?[\w.-]+@\d+\.\d+/.test(spec) && !spec.startsWith("@/");
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Scan source files for any version-pinned imports */
|
|
275
|
+
function hasVersionPinnedImports() {
|
|
276
|
+
if (!fs.existsSync(SRC_DIR)) return false;
|
|
277
|
+
const files = getAllSourceFiles(SRC_DIR, SRC_DIR).map((r) => path.join(SRC_DIR, r));
|
|
278
|
+
for (const file of files) {
|
|
279
|
+
try {
|
|
280
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
281
|
+
for (const spec of extractImports(content)) {
|
|
282
|
+
if (isVersionPinned(spec)) return true;
|
|
283
|
+
}
|
|
284
|
+
} catch (_) {}
|
|
285
|
+
}
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Remove wrong version alias entries previously injected by a broken VDS run.
|
|
291
|
+
* These look like: "@radix-ui/react-slot@1.1.2": path.resolve(process.cwd(), "@radix-ui/react-slot"),
|
|
292
|
+
* They point to non-existent paths and must be replaced by the plugin approach.
|
|
293
|
+
*/
|
|
294
|
+
function cleanupWrongVersionAliases(projectRoot) {
|
|
295
|
+
const mainPath = getMainPath(projectRoot);
|
|
296
|
+
if (!mainPath) return;
|
|
297
|
+
let content = fs.readFileSync(mainPath, "utf-8");
|
|
298
|
+
const before = content;
|
|
299
|
+
// Remove any alias line where key has @version suffix
|
|
300
|
+
content = content.replace(
|
|
301
|
+
/\n?\s*"(?:@[\w.-]+\/)?[\w.-]+@[\d.]+"\s*:\s*path\.resolve\(process\.cwd\(\),\s*"[^"]+"\),?/g,
|
|
302
|
+
""
|
|
303
|
+
);
|
|
304
|
+
if (content !== before) {
|
|
305
|
+
fs.writeFileSync(mainPath, content, "utf-8");
|
|
306
|
+
console.log("[VDS] Storybook adapt: cleaned up incorrect version-pinned aliases from main.ts.");
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Inject a Vite plugin that strips @version suffixes from package import specifiers.
|
|
312
|
+
* e.g. @radix-ui/react-slot@1.1.2 → @radix-ui/react-slot (resolved from node_modules normally).
|
|
313
|
+
* Only added to viteFinal-based configs (not Next.js @storybook/nextjs).
|
|
314
|
+
*/
|
|
315
|
+
function injectVersionStripPlugin(projectRoot) {
|
|
316
|
+
const mainPath = getMainPath(projectRoot);
|
|
317
|
+
if (!mainPath) return;
|
|
318
|
+
let content = fs.readFileSync(mainPath, "utf-8");
|
|
319
|
+
|
|
320
|
+
if (content.includes("vds-version-pin-strip")) return; // already injected
|
|
321
|
+
if (!content.includes("viteFinal")) return; // only for Vite-based storybook
|
|
322
|
+
|
|
323
|
+
// The plugin block to inject. Double-escaped backslashes become single in the written file.
|
|
324
|
+
const pluginBlock = ` plugins: [
|
|
325
|
+
{
|
|
326
|
+
name: "vds-version-pin-strip",
|
|
327
|
+
enforce: "pre",
|
|
328
|
+
async resolveId(id) {
|
|
329
|
+
// Strip @version suffix: @radix-ui/react-slot@1.1.2 → @radix-ui/react-slot
|
|
330
|
+
const m = id.match(/^((?:@[\\w.-]+\\/)?[\\w.-]+)@[\\d]+\\.[\\d]/);
|
|
331
|
+
if (m) return this.resolve(m[1], undefined, { skipSelf: true });
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
`;
|
|
336
|
+
|
|
337
|
+
// Insert before the resolve: { block inside viteFinal
|
|
338
|
+
const resolvePattern = /([ \t]+)(resolve\s*:\s*\{)/;
|
|
339
|
+
if (resolvePattern.test(content)) {
|
|
340
|
+
content = content.replace(resolvePattern, (_, indent, resolveKw) =>
|
|
341
|
+
`${indent}${pluginBlock}${resolveKw}`
|
|
342
|
+
);
|
|
343
|
+
fs.writeFileSync(mainPath, content, "utf-8");
|
|
344
|
+
console.log("[VDS] Storybook adapt: injected vds-version-pin-strip Vite plugin.");
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
265
348
|
function main() {
|
|
266
349
|
const projectRoot = PROJECT_ROOT;
|
|
267
350
|
if (!fs.existsSync(path.join(projectRoot, ".storybook"))) {
|
|
@@ -272,10 +355,16 @@ function main() {
|
|
|
272
355
|
injectViteConfigAliases(projectRoot);
|
|
273
356
|
reportUnresolvedImports(projectRoot);
|
|
274
357
|
const problematic = collectProblematicImports(projectRoot);
|
|
275
|
-
if (problematic.size
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
358
|
+
if (problematic.size > 0) {
|
|
359
|
+
writeMocks(projectRoot, problematic);
|
|
360
|
+
injectAliases(projectRoot, problematic);
|
|
361
|
+
console.log("[VDS] Storybook adapt: mocked", [...problematic].join(", "));
|
|
362
|
+
}
|
|
363
|
+
// Handle version-pinned imports (AI/Bolt/Lovable-generated projects with @pkg@version syntax)
|
|
364
|
+
if (hasVersionPinnedImports()) {
|
|
365
|
+
cleanupWrongVersionAliases(projectRoot);
|
|
366
|
+
injectVersionStripPlugin(projectRoot);
|
|
367
|
+
}
|
|
279
368
|
}
|
|
280
369
|
|
|
281
370
|
main();
|