vibe-design-system 2.8.30 → 2.8.32
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
|
@@ -415,6 +415,11 @@ function inferTier(rel, content) {
|
|
|
415
415
|
const localImports = (content.match(/from\s+['"](?:\.\.?\/?|@\/components\/)/g) || []).length;
|
|
416
416
|
|
|
417
417
|
if (lines >= 400 || localImports >= 12) return "page";
|
|
418
|
+
// Section/Layout/View/Panel/Screen named components with few imports are view-level feature tier.
|
|
419
|
+
// e.g. BusinessModelSection, HeroSection, CheckoutView, AppLayout, CartPanel — regardless of line count.
|
|
420
|
+
// Threshold localImports <= 3: genuine sections import very little; aggregators already hit lines/imports above.
|
|
421
|
+
const SECTION_FILENAME_PATTERNS = /Section$|Layout$|View$|Panel$|Screen$/;
|
|
422
|
+
if (SECTION_FILENAME_PATTERNS.test(filename) && localImports <= 3) return "feature";
|
|
418
423
|
// Semantic keyword: treat as feature even if small (fetches data via context/hooks)
|
|
419
424
|
if (lines >= 200 || localImports >= 7 || FEATURE_FILENAME_KEYWORDS.test(filename)) return "feature";
|
|
420
425
|
return "component";
|
|
@@ -1219,6 +1219,74 @@ function buildArgTypeEntry(prop) {
|
|
|
1219
1219
|
return `{ control: "object", description: ${JSON.stringify(t.slice(0, 60))} }`;
|
|
1220
1220
|
}
|
|
1221
1221
|
|
|
1222
|
+
// ─── Component Profile System ─────────────────────────────────────────────────
|
|
1223
|
+
// Replaces the 6 scattered boolean flags (omitChildren, useReactNodeChildrenRender,
|
|
1224
|
+
// isNoPropsFeature, useSafeWrapper, argsParam, propsArg) with a single canonical
|
|
1225
|
+
// profile that drives all story generation decisions deterministically.
|
|
1226
|
+
//
|
|
1227
|
+
// Priority order (first match wins):
|
|
1228
|
+
// SAFE — SAFE_WRAPPER_DEFAULTS entry exists (complex required props)
|
|
1229
|
+
// SECTION — no extractable props (context/hook-driven, e.g. HeroSection, Footer)
|
|
1230
|
+
// WRAPPER — has children: ReactNode prop (wraps child content)
|
|
1231
|
+
// VARIANT — has variant prop (cva-based, produces multi-story exports)
|
|
1232
|
+
// CONFIGURED — default: has props, no special pattern
|
|
1233
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1234
|
+
|
|
1235
|
+
/**
|
|
1236
|
+
* Classifies a component into one of 5 story profiles.
|
|
1237
|
+
* @param {string} componentName
|
|
1238
|
+
* @param {string} source — raw component source
|
|
1239
|
+
* @param {Array<{name:string,type:string,required:boolean}>} effectiveProps
|
|
1240
|
+
* @returns {"SAFE"|"SECTION"|"WRAPPER"|"VARIANT"|"CONFIGURED"}
|
|
1241
|
+
*/
|
|
1242
|
+
function getStoryProfile(componentName, source, effectiveProps) {
|
|
1243
|
+
if (componentName && SAFE_WRAPPER_DEFAULTS[componentName]) return "SAFE";
|
|
1244
|
+
if (effectiveProps.length === 0) return "SECTION";
|
|
1245
|
+
if (hasChildrenPropReactNode(effectiveProps)) return "WRAPPER";
|
|
1246
|
+
if (effectiveProps.some(p => p.name === "variant")) return "VARIANT";
|
|
1247
|
+
return "CONFIGURED";
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
/**
|
|
1251
|
+
* Builds the render: line for a story based on its component profile.
|
|
1252
|
+
* @param {"SAFE"|"SECTION"|"WRAPPER"|"VARIANT"|"CONFIGURED"} profile
|
|
1253
|
+
* @param {"SafeWrapper"|"ComponentRef"} RenderTarget
|
|
1254
|
+
* @param {string} argsFallback — extra inline props spread or ""
|
|
1255
|
+
* @returns {string}
|
|
1256
|
+
*/
|
|
1257
|
+
function buildProfileRenderLine(profile, RenderTarget, argsFallback) {
|
|
1258
|
+
const suspense = (inner) =>
|
|
1259
|
+
`React.createElement(React.Suspense, { fallback: null }, ${inner})`;
|
|
1260
|
+
switch (profile) {
|
|
1261
|
+
case "SECTION":
|
|
1262
|
+
return ` render: () => ${suspense(`React.createElement(${RenderTarget})`)},`;
|
|
1263
|
+
case "WRAPPER":
|
|
1264
|
+
return ` render: (args) => ${suspense(
|
|
1265
|
+
`React.createElement(${RenderTarget}, { ...args, children: args.children || React.createElement('span', null, 'Example') })`
|
|
1266
|
+
)},`;
|
|
1267
|
+
case "SAFE":
|
|
1268
|
+
return ` render: (args = {}) => ${suspense(`React.createElement(${RenderTarget}, args)`)},`;
|
|
1269
|
+
default: { // CONFIGURED, VARIANT
|
|
1270
|
+
const argsParam = argsFallback ? "(args = {})" : "(args)";
|
|
1271
|
+
const propsArg = argsFallback ? `{ ...args${argsFallback} }` : "args";
|
|
1272
|
+
return ` render: ${argsParam} => ${suspense(`React.createElement(${RenderTarget}, ${propsArg})`)},`;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
/**
|
|
1278
|
+
* Returns a function that produces a `children: "label",` arg line or null.
|
|
1279
|
+
* Only WRAPPER profile includes a children arg so Storybook shows a children control.
|
|
1280
|
+
* @param {"SAFE"|"SECTION"|"WRAPPER"|"VARIANT"|"CONFIGURED"} profile
|
|
1281
|
+
* @returns {(label: string) => string|null}
|
|
1282
|
+
*/
|
|
1283
|
+
function buildProfileChildrenArgLine(profile) {
|
|
1284
|
+
return (label) => {
|
|
1285
|
+
if (profile === "WRAPPER") return ` children: ${JSON.stringify(label)},`;
|
|
1286
|
+
return null;
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1222
1290
|
function buildStoryFileContent(comp) {
|
|
1223
1291
|
const componentName = toSafeComponentName(comp.name, comp.file);
|
|
1224
1292
|
const fileNoExt = comp.file.replace(/\.(tsx|jsx)$/, "");
|
|
@@ -1283,14 +1351,23 @@ function buildStoryFileContent(comp) {
|
|
|
1283
1351
|
// ignore
|
|
1284
1352
|
}
|
|
1285
1353
|
const exportStyle = detectExportStyle(source, componentName);
|
|
1286
|
-
const omitChildren = componentWrapsVoidElement(source);
|
|
1287
1354
|
const isPage = isPageFile;
|
|
1288
1355
|
|
|
1289
1356
|
// Props: manifest or parse from source for default args (LucideIcon, array types, ReactNode, contextual placeholders)
|
|
1290
1357
|
const effectiveProps = Array.isArray(comp.props) && comp.props.length > 0 ? comp.props : parsePropsFromSource(source);
|
|
1291
1358
|
const usageFromPages = findComponentUsageInPages(componentName, PROJECT_ROOT);
|
|
1292
|
-
|
|
1293
|
-
|
|
1359
|
+
|
|
1360
|
+
// Profile: single source of truth — replaces omitChildren, useReactNodeChildrenRender,
|
|
1361
|
+
// isNoPropsFeature, useSafeWrapper, argsParam, propsArg flags.
|
|
1362
|
+
const profile = getStoryProfile(componentName, source, effectiveProps);
|
|
1363
|
+
const propSummary = effectiveProps.length > 0 ? ` (${effectiveProps.length} props)` : "";
|
|
1364
|
+
console.log(`[VDS] ${componentName} → ${profile}${propSummary}`);
|
|
1365
|
+
|
|
1366
|
+
// For SECTION, argLines are unused; still need lucideImports/iconPropNames/needReact for imports+argTypes.
|
|
1367
|
+
const { argLines: defaultArgLines, lucideImports, iconPropNames, needReact } =
|
|
1368
|
+
profile === "SECTION"
|
|
1369
|
+
? { argLines: [], lucideImports: [], iconPropNames: [], needReact: false }
|
|
1370
|
+
: buildDefaultArgsForRequiredProps(effectiveProps, usageFromPages, componentName, source);
|
|
1294
1371
|
|
|
1295
1372
|
// Skip story only if not a page and no export found
|
|
1296
1373
|
if (exportStyle === "unknown" && !isPage && (!source.includes("export") || !new RegExp(`\\b${componentName}\\b`).test(source))) {
|
|
@@ -1358,24 +1435,24 @@ function buildStoryFileContent(comp) {
|
|
|
1358
1435
|
return lines.join("\n");
|
|
1359
1436
|
}
|
|
1360
1437
|
|
|
1361
|
-
//
|
|
1362
|
-
const useSafeWrapper =
|
|
1363
|
-
const RenderTarget
|
|
1364
|
-
const argsFallback
|
|
1365
|
-
const
|
|
1366
|
-
const
|
|
1367
|
-
const renderLine = useReactNodeChildrenRender
|
|
1368
|
-
? ` render: (args) => React.createElement(React.Suspense, { fallback: null }, React.createElement(${RenderTarget}, { ...args, children: args.children || React.createElement('span', null, 'Example') })),`
|
|
1369
|
-
: ` render: ${argsParam} => React.createElement(React.Suspense, { fallback: null }, React.createElement(${RenderTarget}, ${propsArg})),`;
|
|
1370
|
-
const childrenArgLine = (label) => (!omitChildren && !useReactNodeChildrenRender ? ` children: ${JSON.stringify(label)},` : null);
|
|
1438
|
+
// Profile-driven render + children — single source of truth via getStoryProfile()
|
|
1439
|
+
const useSafeWrapper = profile === "SAFE";
|
|
1440
|
+
const RenderTarget = useSafeWrapper ? "SafeWrapper" : "ComponentRef";
|
|
1441
|
+
const argsFallback = !useSafeWrapper && (componentName && RENDER_ARGS_FALLBACKS[componentName]) || "";
|
|
1442
|
+
const renderLine = buildProfileRenderLine(profile, RenderTarget, argsFallback);
|
|
1443
|
+
const childrenArgLine = buildProfileChildrenArgLine(profile);
|
|
1371
1444
|
|
|
1372
1445
|
if (!variants.length) {
|
|
1373
1446
|
lines.push(`export const Default: Story = {`);
|
|
1374
1447
|
lines.push(renderLine);
|
|
1375
|
-
|
|
1376
|
-
if (childrenArgLine(componentName))
|
|
1377
|
-
for (const line of defaultArgLines)
|
|
1378
|
-
|
|
1448
|
+
const storyArgLines = [];
|
|
1449
|
+
if (childrenArgLine(componentName)) storyArgLines.push(childrenArgLine(componentName));
|
|
1450
|
+
for (const line of defaultArgLines) storyArgLines.push(line);
|
|
1451
|
+
if (storyArgLines.length > 0) {
|
|
1452
|
+
lines.push(` args: {`);
|
|
1453
|
+
for (const line of storyArgLines) lines.push(line);
|
|
1454
|
+
lines.push(` },`);
|
|
1455
|
+
}
|
|
1379
1456
|
lines.push(`};`);
|
|
1380
1457
|
} else {
|
|
1381
1458
|
const defaultVariant = variants[0];
|