-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
React: Add components.json for dev and build with examples snippets #32682
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 10.1
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughAdds a component-manifest pipeline: new manifest types and feature flag, a React renderer preset that generates per-component example snippets from CSF files, a dev HTTP endpoint and static-build writer for Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant CLI as Builder/Dev
participant Presets
participant CoreServer
participant SIG as StoryIndexGenerator
participant Gen as ComponentManifestGenerator
participant FS as File System
rect #f8fbff
note left of CoreServer: Static build flow (feature flag enabled)
CLI->>CoreServer: run build-static
CoreServer->>SIG: init StoryIndexGenerator
CoreServer->>Presets: apply('componentManifestGenerator')
Presets-->>CoreServer: Gen?
alt Gen present && SIG ready
CoreServer->>Gen: generate(SIG)
Gen-->>CoreServer: manifest map
CoreServer->>FS: write outputDir/manifests/components.json
FS-->>CoreServer: OK
else
CoreServer-->>CLI: skip manifest generation
end
end
sequenceDiagram
autonumber
participant Browser
participant DevServer as Core Dev Server
participant Presets
participant SIG as StoryIndexGenerator
participant Gen as ComponentManifestGenerator
rect #f8fbff
note left of DevServer: Dev endpoint /manifests/components.json
Browser->>DevServer: GET /manifests/components.json
DevServer->>Presets: apply('componentManifestGenerator')
Presets-->>DevServer: Gen?
DevServer->>SIG: await initializedStoryIndexGenerator
alt Gen present && SIG ready
DevServer->>Gen: generate(SIG)
Gen-->>DevServer: manifest JSON
DevServer-->>Browser: 200 application/json
else
DevServer-->>Browser: 400/500 error (missing generator/prereqs)
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
View your CI Pipeline Execution ↗ for commit a6f6673
☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
code/core/src/csf-tools/CsfFile.ts (1)
1049-1056
: Bug: writeCsf ignores computed fname and may write to undefinedIf fileName is omitted but csf._options.fileName is set, fname is computed but not used; writeFile(fileName as string, ...) can fail.
Apply this diff:
export const writeCsf = async (csf: CsfFile, fileName?: string) => { const fname = fileName || csf._options.fileName; if (!fname) { throw new Error('Please specify a fileName for writeCsf'); } - await writeFile(fileName as string, printCsf(csf).code); + await writeFile(fname, printCsf(csf).code); };
🧹 Nitpick comments (4)
code/core/src/csf-tools/generateCodeSnippet.test.tsx (1)
14-87
: Add edge-case tests to harden generatorPlease add coverage for:
- Function-declared stories (export function Story() {}) to ensure non-variable exports work.
- Multiple declarators in one export (export const A = {}, B = {}) to ensure correct binding.
- meta.story() with no arguments (Default = meta.story()) to verify zero-arg handling.
- meta.component as member expression (component: DesignSystem.Button) to ensure JSX name building.
Also applies to: 89-158
code/core/src/csf-tools/generateCodeSnippet.ts (3)
85-95
: Component name handling breaks on member expressions (e.g., DesignSystem.Button)Using jsxIdentifier(componentName) fails for dotted names; derive a JSX name from meta.component instead, with fallback.
Apply this diff and helper (see additional code block):
- const name = t.jsxIdentifier(componentName); + const name = + jsxNameFromMeta(metaObj) ?? t.jsxIdentifier('Unknown'); const arrow = t.arrowFunctionExpression(Add helper functions:
// Place near other helpers const jsxNameFromMeta = ( metaObj?: t.ObjectExpression | null ): t.JSXIdentifier | t.JSXMemberExpression | null => { if (!metaObj) return null; const comp = (metaObj.properties as t.ObjectProperty[]) .filter((p): p is t.ObjectProperty => t.isObjectProperty(p)) .find((p) => keyOf(p) === 'component')?.value as t.Node | undefined; if (!comp || !t.isExpression(comp)) return null; return jsxNameFromExpr(comp); }; const jsxNameFromExpr = ( expr: t.Expression ): t.JSXIdentifier | t.JSXMemberExpression | null => { if (t.isIdentifier(expr)) return t.jsxIdentifier(expr.name); if (t.isMemberExpression(expr) && !expr.computed && t.isIdentifier(expr.property)) { const left = jsxNameFromExpr(expr.object as t.Expression); return left ? t.jsxMemberExpression(left, t.jsxIdentifier(expr.property.name)) : null; } return null; };
102-112
: Ordering and selection: iterate entries to pass exportName; ensure named export order is respectedObject.values(csf._storyPaths) loses export names and may not respect __namedExportsOrder. Iterate entries and pass exportName to getCodeSnippet.
Apply this diff:
-export function getAllCodeSnippets(csf: CsfFile) { - const component = csf._meta?.component ?? 'Unknown'; - - const snippets = Object.values(csf._storyPaths) - .map((path: NodePath<t.ExportNamedDeclaration>) => - getCodeSnippet(path, csf._metaNode ?? null, component) - ) - .filter(Boolean); - - return t.program(snippets); -} +export function getAllCodeSnippets(csf: CsfFile) { + const entries = Object.entries(csf._storyPaths); + // If CsfFile sorted exports, prefer that order via _storyExports keys + const orderedNames = Object.keys(csf._storyExports); + const orderedEntries = + orderedNames.length === entries.length + ? orderedNames.map((name) => [name, csf._storyPaths[name]] as const) + : entries; + + const snippets = orderedEntries + .map(([exportName, path]) => getCodeSnippet(path, exportName, csf._metaNode ?? null)) + .filter(Boolean); + + return t.program(snippets); +}
152-161
: Resolve meta.args when it’s an identifier (common pattern)metaArgsRecord only handles inline object. Consider resolving identifiers (e.g., args: defaultArgs) via findVarInitialization and the Program AST.
If acceptable, I can wire in the program AST and reuse findVarInitialization from CsfFile for robust resolution.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
code/core/src/csf-tools/CsfFile.ts
(1 hunks)code/core/src/csf-tools/generateCodeSnippet.test.tsx
(1 hunks)code/core/src/csf-tools/generateCodeSnippet.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Adhere to ESLint and Prettier rules across all JS/TS source files
Files:
code/core/src/csf-tools/generateCodeSnippet.ts
code/core/src/csf-tools/generateCodeSnippet.test.tsx
code/core/src/csf-tools/CsfFile.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Fix type errors and prefer precise typings instead of using any or suppressions, consistent with strict mode
Files:
code/core/src/csf-tools/generateCodeSnippet.ts
code/core/src/csf-tools/generateCodeSnippet.test.tsx
code/core/src/csf-tools/CsfFile.ts
code/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
code/**/*.{test,spec}.{ts,tsx}
: Place all test files under the code/ directory
Name test files as *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx
Files:
code/core/src/csf-tools/generateCodeSnippet.test.tsx
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/spy-mocking.mdc)
**/*.test.{ts,tsx,js,jsx}
: Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests
Place all mocks at the top of the test file before any test cases
Use vi.mocked() to type and access mocked functions
Implement mock behaviors in beforeEach blocks
Mock all required dependencies that the test subject uses
Mock implementations should be placed in beforeEach blocks
Each mock implementation should return a Promise for async functions
Mock implementations should match the expected return type of the original function
Use vi.mocked() to access and implement mock behaviors
Mock all required properties and methods that the test subject uses
Avoid direct function mocking without vi.mocked()
Avoid mock implementations outside of beforeEach blocks
Avoid mocking without the spy: true option
Avoid inline mock implementations within test cases
Avoid mocking only a subset of required dependencies
Mock at the highest level of abstraction needed
Keep mock implementations simple and focused
Use type-safe mocking with vi.mocked()
Document complex mock behaviors
Group related mocks together
Files:
code/core/src/csf-tools/generateCodeSnippet.test.tsx
🧬 Code graph analysis (2)
code/core/src/csf-tools/generateCodeSnippet.ts (1)
code/core/src/csf-tools/CsfFile.ts (1)
CsfFile
(277-1006)
code/core/src/csf-tools/generateCodeSnippet.test.tsx (2)
code/core/src/csf-tools/CsfFile.ts (1)
loadCsf
(1021-1025)code/core/src/csf-tools/generateCodeSnippet.ts (1)
getAllCodeSnippets
(102-112)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Danger JS
- GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (1)
code/core/src/csf-tools/CsfFile.ts (1)
298-299
: Type narrowing of _metaNode is correctLimiting _metaNode to ObjectExpression matches how _parseMeta assigns it and aligns with snippet generation usage.
const declaration = storyExportPath.get('declaration') as NodePath<t.Declaration>; | ||
invariant(declaration.isVariableDeclaration(), 'Expected variable declaration'); | ||
|
||
const declarator = declaration.get('declarations')[0] as NodePath<t.VariableDeclarator>; | ||
const init = declarator.get('init') as NodePath<t.Expression>; | ||
invariant(init.isExpression(), 'Expected story initializer to be an expression'); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handles only variable declarations; breaks on function-declared stories and multi-declarators
The generator assumes ExportNamedDeclaration always contains a VariableDeclaration and then blindly takes the first declarator. This fails for:
- export function Story() {} (FunctionDeclaration)
- export const A = {}, B = {} (selects wrong declarator)
Refactor to:
- Accept exportName to select the matching declarator.
- Handle FunctionDeclaration by converting to a function expression.
Apply these diffs:
-export function getCodeSnippet(
- storyExportPath: NodePath<t.ExportNamedDeclaration>,
- metaObj: t.ObjectExpression | null | undefined,
- componentName: string
-): t.VariableDeclaration {
- const declaration = storyExportPath.get('declaration') as NodePath<t.Declaration>;
- invariant(declaration.isVariableDeclaration(), 'Expected variable declaration');
+export function getCodeSnippet(
+ storyExportPath: NodePath<t.ExportNamedDeclaration>,
+ exportName: string,
+ metaObj: t.ObjectExpression | null | undefined
+): t.VariableDeclaration {
+ const declaration = storyExportPath.get('declaration') as NodePath<t.Declaration>;
+ // Handle `export function Story() {}`
+ if (declaration.isFunctionDeclaration()) {
+ const fn = declaration.node;
+ const expr = t.functionExpression(
+ fn.id ?? t.identifier(exportName),
+ fn.params,
+ fn.body,
+ fn.generator,
+ fn.async
+ );
+ return t.variableDeclaration('const', [
+ t.variableDeclarator(t.identifier(exportName), expr),
+ ]);
+ }
+ invariant(declaration.isVariableDeclaration(), 'Expected variable declaration');
- const declarator = declaration.get('declarations')[0] as NodePath<t.VariableDeclarator>;
+ // Select the matching declarator in `export const A = ..., B = ...`
+ const declarators = declaration.get('declarations') as NodePath<t.VariableDeclarator>[];
+ const declarator =
+ declarators.find((d) => {
+ const id = d.get('id');
+ return id.isIdentifier() && id.node.name === exportName;
+ }) ?? declarators[0];
const init = declarator.get('init') as NodePath<t.Expression>;
invariant(init.isExpression(), 'Expected story initializer to be an expression');
const storyId = declarator.get('id');
invariant(storyId.isIdentifier(), 'Expected named const story export');
const renderPath = storyObjPath | ||
?.get('properties') | ||
.filter((p) => p.isObjectProperty()) | ||
.filter((p) => keyOf(p.node) === 'render') | ||
.map((p) => p.get('value')) | ||
.find((value) => value.isExpression()); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Restrict render to function expressions
Currently accepts any expression as render. Ensure it’s arrow/function for safety.
Apply this diff:
const renderPath = storyObjPath
?.get('properties')
.filter((p) => p.isObjectProperty())
.filter((p) => keyOf(p.node) === 'render')
.map((p) => p.get('value'))
- .find((value) => value.isExpression());
+ .find(
+ (value) => value.isArrowFunctionExpression() || value.isFunctionExpression()
+ );
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const renderPath = storyObjPath | |
?.get('properties') | |
.filter((p) => p.isObjectProperty()) | |
.filter((p) => keyOf(p.node) === 'render') | |
.map((p) => p.get('value')) | |
.find((value) => value.isExpression()); | |
const renderPath = storyObjPath | |
?.get('properties') | |
.filter((p) => p.isObjectProperty()) | |
.filter((p) => keyOf(p.node) === 'render') | |
.map((p) => p.get('value')) | |
.find( | |
(value) => value.isArrowFunctionExpression() || value.isFunctionExpression() | |
); |
🤖 Prompt for AI Agents
In code/core/src/csf-tools/generateCodeSnippet.ts around lines 50 to 56, the
current logic finds any expression for the story "render" property but should
only accept function expressions; change the final predicate in the chain so the
found value must be a function — i.e., replace the .find((value) =>
value.isExpression()) with a predicate that checks
value.isArrowFunctionExpression() || value.isFunctionExpression(), so only arrow
or function expressions are accepted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
code/renderers/react/src/component-manifest/generateCodeSnippet.ts (2)
18-31
: Blocker: breaks on function‑declared stories and multi‑declarators; add exportName and handle FunctionDeclaration.Current code assumes a single VariableDeclaration and always takes the first declarator. It fails for:
- export function Story() {}
- export const A = {...}, B = {...}
Apply:
-export function getCodeSnippet( - storyExportPath: NodePath<t.ExportNamedDeclaration>, - metaObj: t.ObjectExpression | null | undefined, - componentName: string -): t.VariableDeclaration { +export function getCodeSnippet( + storyExportPath: NodePath<t.ExportNamedDeclaration>, + exportName: string, + metaObj: t.ObjectExpression | null | undefined, + componentName: string +): t.VariableDeclaration { const declaration = storyExportPath.get('declaration') as NodePath<t.Declaration>; - invariant(declaration.isVariableDeclaration(), 'Expected variable declaration'); + // Handle `export function Story() {}` + if (declaration.isFunctionDeclaration()) { + const fn = declaration.node; + const expr = t.functionExpression( + fn.id ?? t.identifier(exportName), + fn.params, + fn.body, + fn.generator, + fn.async + ); + return t.variableDeclaration('const', [ + t.variableDeclarator(t.identifier(exportName), expr), + ]); + } + invariant(declaration.isVariableDeclaration(), 'Expected variable declaration'); - const declarator = declaration.get('declarations')[0] as NodePath<t.VariableDeclarator>; + // Select the matching declarator in `export const A = ..., B = ...` + const declarators = declaration.get('declarations') as NodePath<t.VariableDeclarator>[]; + const declarator = + declarators.find((d) => { + const id = d.get('id'); + return id.isIdentifier() && id.node.name === exportName; + }) ?? declarators[0]; const init = declarator.get('init') as NodePath<t.Expression>; invariant(init.isExpression(), 'Expected story initializer to be an expression'); const storyId = declarator.get('id'); invariant(storyId.isIdentifier(), 'Expected named const story export');This unblocks both patterns.
76-83
: Constrain render to functions (reject arbitrary expressions).Only accept arrow/function expressions for render, not any expression.
const renderPath = storyObjPath ?.get('properties') .filter((p) => p.isObjectProperty()) .filter((p) => keyOf(p.node) === 'render') .map((p) => p.get('value')) - .find((value) => value.isExpression()); + .find( + (value) => value.isArrowFunctionExpression() || value.isFunctionExpression() + );
🧹 Nitpick comments (8)
code/core/src/types/modules/indexer.ts (1)
73-79
: Avoid re-declaring componentPath on StoryIndexEntry; keep a single source of truthcomponentPath already exists on BaseIndexEntry (as Path). Re-declaring it on StoryIndexEntry (as string) is redundant and can confuse types. Remove the duplicate field and rely on BaseIndexEntry.
export type StoryIndexEntry = BaseIndexEntry & { type: 'story'; subtype: 'story' | 'test'; - componentPath?: string; exportName?: string; parent?: StoryId; // exists only on tests parentName?: StoryName; // exists only on tests };
code/core/src/core-server/build-static.ts (1)
168-179
: Reuse existing features; add error handling and pretty JSON output
- Redundant re-apply of features; reuse the earlier features to avoid shadowing.
- Guard manifest generation with try/catch and log a warning; don’t fail the build on generator errors.
- Pretty-print JSON for easier inspection.
- const features = await presets.apply('features'); - - if (features?.componentManifestGenerator) { - const componentManifestGenerator: ComponentManifestGenerator = await presets.apply( - 'componentManifestGenerator' - ); - const indexGenerator = await initializedStoryIndexGenerator; - if (componentManifestGenerator && indexGenerator) { - const manifests = await componentManifestGenerator(indexGenerator); - await writeFile(join(options.outputDir, 'components.json'), JSON.stringify(manifests)); - } - } + if (features?.componentManifestGenerator) { + try { + const componentManifestGenerator = + await presets.apply<ComponentManifestGenerator>('componentManifestGenerator'); + const indexGenerator = await initializedStoryIndexGenerator; + if (componentManifestGenerator && indexGenerator) { + const manifests = await componentManifestGenerator(indexGenerator); + await writeFile( + join(options.outputDir, 'components.json'), + JSON.stringify(manifests, null, 2) + ); + } + } catch (e) { + logger.warn('Failed to generate components.json', e as Error); + } + }code/core/src/core-server/dev-server.ts (1)
139-154
: Serve via GET, handle errors, and return 404 when disabled
- Use app.get for the JSON endpoint.
- Add try/catch to avoid unhandled rejections crashing the server.
- Return 404 when the feature/generator is not configured (500 implies server fault).
- Set JSON content type.
- app.use('/components.json', async (req, res) => { - const componentManifestGenerator: ComponentManifestGenerator = await options.presets.apply( - 'componentManifestGenerator' - ); - const indexGenerator = await initializedStoryIndexGenerator; - const features = await options.presets.apply('features'); - if (features?.componentManifestGenerator && componentManifestGenerator && indexGenerator) { - const manifest = await componentManifestGenerator(indexGenerator); - res.setHeader('Content-Type', 'application/json'); - res.end(JSON.stringify(manifest)); - } else { - res.statusCode = 500; - res.end('No component manifest generator configured.'); - } - }); + app.get('/components.json', async (req, res) => { + try { + const componentManifestGenerator = + await options.presets.apply<ComponentManifestGenerator>('componentManifestGenerator'); + const indexGenerator = await initializedStoryIndexGenerator; + const features = await options.presets.apply('features'); + if (features?.componentManifestGenerator && componentManifestGenerator && indexGenerator) { + const manifest = await componentManifestGenerator(indexGenerator); + res.setHeader('Content-Type', 'application/json; charset=utf-8'); + res.end(JSON.stringify(manifest)); + } else { + res.statusCode = 404; + res.end('Component manifest not enabled.'); + } + } catch (err) { + logger.warn('Failed to generate component manifest', err as Error); + res.statusCode = 500; + res.end('Failed to generate component manifest.'); + } + });code/renderers/react/src/preset.ts (2)
18-49
: Type as PresetProperty, add per-component error handling, and minor cleanups
- Export as PresetProperty<'componentManifestGenerator'> for consistency with presets.
- Protect the Promise.all with per-item try/catch; skip failing components and log a warning.
- Use null when meta.component is missing; remove redundant .filter(Boolean) on mapped objects.
-import { type ComponentManifestGenerator } from 'storybook/internal/types'; +import { type ComponentManifestGenerator } from 'storybook/internal/types'; +import { logger } from 'storybook/internal/node-logger'; @@ -export const componentManifestGenerator = async () => { - return (async (storyIndexGenerator) => { +export const componentManifestGenerator: PresetProperty<'componentManifestGenerator'> = async () => { + const generator: ComponentManifestGenerator = async (storyIndexGenerator) => { const index = await storyIndexGenerator.getIndex(); const groupByComponentId = groupBy( Object.values(index.entries).filter( (entry) => entry.type === 'story' && entry.subtype === 'story' && entry.componentPath ), (it) => it.id.split('--')[0] ); - const singleEntryPerComponent = Object.values(groupByComponentId).flatMap((group) => - group && group?.length > 0 ? [group[0]] : [] - ); + const singleEntryPerComponent = Object.values(groupByComponentId) + .map((group) => group?.[0]) + .filter(Boolean); const components = await Promise.all( singleEntryPerComponent.map(async (entry) => { - const code = await readFile(path.join(process.cwd(), entry.importPath), 'utf-8'); - const csf = loadCsf(code, { makeTitle: (title) => title }).parse(); - const component = csf._meta?.component ?? 'Unknown'; - return { - id: entry.id.split('--')[0], - examples: Object.entries(csf._storyPaths) - .map(([name, path]) => ({ - name, - snippet: recast.print(getCodeSnippet(path, csf._metaNode ?? null, component)).code, - })) - .filter(Boolean), - }; + try { + const code = await readFile(path.join(process.cwd(), entry.importPath), 'utf-8'); + const csf = loadCsf(code, { makeTitle: (title) => title }).parse(); + const component = csf._meta?.component ?? null; + return { + id: entry.id.split('--')[0], + examples: Object.entries(csf._storyPaths).map(([name, path]) => ({ + name, + snippet: recast.print(getCodeSnippet(path, csf._metaNode ?? null, component)).code, + })), + }; + } catch (err) { + logger.warn(`Skipping manifest generation for ${entry.id}: ${(err as Error).message}`); + return null as any; + } }) ); - return Object.fromEntries(components.map((component) => [component.id, component])); - }) satisfies ComponentManifestGenerator; -}; + return Object.fromEntries( + components.filter(Boolean).map((component) => [component!.id, component!]) + ); + }; + return generator; +};
51-63
: groupBy polyfill is fine; optional micro‑nitLooks good. If desired, remove the reducer’s default param value since an explicit initial value is already provided.
code/renderers/react/src/component-manifest/generateCodeSnippet.test.tsx (2)
174-195
: Remove unnecessary async from tests.These tests don’t await anything. Drop async to reduce noise.
Also applies to: 197-219, 221-231, 233-242, 244-253, 255-264, 266-281, 471-489
137-164
: Add missing cases: function exports and multi‑declarators.Please add:
- export function Story() { return }
- export const A = {...}, B = {...}
These catch current generator edge cases.
code/renderers/react/src/component-manifest/generateCodeSnippet.ts (1)
221-236
: Support dotted component names (e.g., UI.Button) when creating JSX.Build JSX name from a possibly dotted string to avoid invalid identifiers:
- const name = t.jsxIdentifier(componentName); + const name = toJsxName(componentName); const arrow = t.arrowFunctionExpression( [], t.jsxElement( t.jsxOpeningElement(name, openingElAttrs, false), t.jsxClosingElement(name), toJsxChildren(merged.children), false ) );Add helper (place near other helpers):
function toJsxName(name: string): t.JSXIdentifier | t.JSXMemberExpression { if (name.includes('.')) { const parts = name.split('.'); let acc: t.JSXIdentifier | t.JSXMemberExpression = t.jsxIdentifier(parts[0]!); for (let i = 1; i < parts.length; i++) { acc = t.jsxMemberExpression(acc as any, t.jsxIdentifier(parts[i]!)); } return acc as t.JSXMemberExpression; } return t.jsxIdentifier(name); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
code/core/src/core-server/build-static.ts
(3 hunks)code/core/src/core-server/dev-server.ts
(2 hunks)code/core/src/types/modules/core-common.ts
(4 hunks)code/core/src/types/modules/indexer.ts
(1 hunks)code/renderers/react/src/component-manifest/generateCodeSnippet.test.tsx
(1 hunks)code/renderers/react/src/component-manifest/generateCodeSnippet.ts
(1 hunks)code/renderers/react/src/preset.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,json,html,ts,tsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{js,jsx,json,html,ts,tsx,mjs}
: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (useyarn lint:js:cmd <file>
)
Files:
code/core/src/core-server/dev-server.ts
code/renderers/react/src/preset.ts
code/core/src/types/modules/core-common.ts
code/renderers/react/src/component-manifest/generateCodeSnippet.test.tsx
code/core/src/types/modules/indexer.ts
code/renderers/react/src/component-manifest/generateCodeSnippet.ts
code/core/src/core-server/build-static.ts
**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Export functions from modules when they need to be unit-tested
Files:
code/core/src/core-server/dev-server.ts
code/renderers/react/src/preset.ts
code/core/src/types/modules/core-common.ts
code/renderers/react/src/component-manifest/generateCodeSnippet.test.tsx
code/core/src/types/modules/indexer.ts
code/renderers/react/src/component-manifest/generateCodeSnippet.ts
code/core/src/core-server/build-static.ts
code/**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
In application code, use Storybook loggers instead of
console.*
(client code:storybook/internal/client-logger
; server code:storybook/internal/node-logger
)
Files:
code/core/src/core-server/dev-server.ts
code/renderers/react/src/preset.ts
code/core/src/types/modules/core-common.ts
code/renderers/react/src/component-manifest/generateCodeSnippet.test.tsx
code/core/src/types/modules/indexer.ts
code/renderers/react/src/component-manifest/generateCodeSnippet.ts
code/core/src/core-server/build-static.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Do not use
console.log
,console.warn
, orconsole.error
directly unless in isolated files where importing loggers would significantly increase bundle size
Files:
code/core/src/core-server/dev-server.ts
code/renderers/react/src/preset.ts
code/core/src/types/modules/core-common.ts
code/renderers/react/src/component-manifest/generateCodeSnippet.test.tsx
code/core/src/types/modules/indexer.ts
code/renderers/react/src/component-manifest/generateCodeSnippet.ts
code/core/src/core-server/build-static.ts
code/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
code/**/*.{test,spec}.{ts,tsx}
: Place all test files under the code/ directory
Name test files as *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx
Files:
code/renderers/react/src/component-manifest/generateCodeSnippet.test.tsx
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/spy-mocking.mdc)
**/*.test.{ts,tsx,js,jsx}
: Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests
Place all mocks at the top of the test file before any test cases
Use vi.mocked() to type and access mocked functions
Implement mock behaviors in beforeEach blocks
Mock all required dependencies that the test subject uses
Mock implementations should be placed in beforeEach blocks
Each mock implementation should return a Promise for async functions
Mock implementations should match the expected return type of the original function
Use vi.mocked() to access and implement mock behaviors
Mock all required properties and methods that the test subject uses
Avoid direct function mocking without vi.mocked()
Avoid mock implementations outside of beforeEach blocks
Avoid mocking without the spy: true option
Avoid inline mock implementations within test cases
Avoid mocking only a subset of required dependencies
Mock at the highest level of abstraction needed
Keep mock implementations simple and focused
Use type-safe mocking with vi.mocked()
Document complex mock behaviors
Group related mocks together
Files:
code/renderers/react/src/component-manifest/generateCodeSnippet.test.tsx
**/*.@(test|spec).{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.@(test|spec).{ts,tsx,js,jsx}
: Unit tests should import and execute the functions under test rather than only asserting on syntax patterns
Mock external dependencies in tests usingvi.mock()
(e.g., filesystem, loggers)
Files:
code/renderers/react/src/component-manifest/generateCodeSnippet.test.tsx
🔇 Additional comments (5)
code/core/src/types/modules/core-common.ts (2)
348-356
: Types look good; feature flag wiring aligns with usageNew ComponentManifest/Generator types and config/feature flags are coherent with server usage.
Also applies to: 370-371, 469-470
65-110
: Add Presets.apply overload for componentManifestGeneratorTyping presets.apply('componentManifestGenerator') currently falls back to the generic signature, yielding unknown. Add a dedicated overload to ensure correct return typing.
apply( extension: 'staticDirs', config?: StorybookConfigRaw['staticDirs'], args?: any ): Promise<StorybookConfigRaw['staticDirs']>; + apply( + extension: 'componentManifestGenerator', + config?: StorybookConfigRaw['componentManifestGenerator'], + args?: any + ): Promise<NonNullable<StorybookConfigRaw['componentManifestGenerator']>>; apply<T>(extension: string, config?: T, args?: unknown): Promise<T>;Please also verify that ComponentManifest and ComponentManifestGenerator are re-exported from the package types entrypoint used by core-server imports.
code/renderers/react/src/component-manifest/generateCodeSnippet.ts (2)
53-64
: Good fix: zero‑arg story factories no longer access args[0].Guarding args[0] behind args.length resolves the crash for meta.story() calls.
246-246
: Tighten attribute name validation; '.' and ':' produce invalid JSXIdentifier.Exclude '.' and ':' to avoid generating which is invalid.
-const isValidJsxAttrName = (n: string) => /^[A-Za-z_][A-Za-z0-9_.:-]*$/.test(n); +const isValidJsxAttrName = (n: string) => /^[A-Za-z_][A-Za-z0-9_-]*$/.test(n);code/renderers/react/src/component-manifest/generateCodeSnippet.test.tsx (1)
12-23
: Pass export name to getCodeSnippet (prep for multi‑declarator/function exports).To support selecting the correct declarator and function‑declared stories, update generateExample to pass the export name:
- const snippets = Object.values(csf._storyPaths) - .map((path: NodePath<t.ExportNamedDeclaration>) => - getCodeSnippet(path, csf._metaNode ?? null, component) - ) + const snippets = Object.entries(csf._storyPaths) + .map(([exportName, path]: [string, NodePath<t.ExportNamedDeclaration>]) => + getCodeSnippet(path, exportName, csf._metaNode ?? null, component) + ) .filter(Boolean);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
code/core/src/preview-api/modules/preview-web/render/mount-utils.ts (1)
3-5
: Constrain or document loosened mountDestructured signature
- mountDestructured’s parameter was changed to
(...args: any[]) => any
, removing the originalPlayFunction<TRenderer>
constraint and weakening compile-time checks.- Usages found in csf-factories.ts:206 and prepareStory.ts:128.
Either restore a tighter
PlayFunction<TRenderer>
signature or add a comment in mount-utils.ts explaining why a broaderany
-typed signature is necessary.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
code/core/src/preview-api/modules/preview-web/render/mount-utils.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,json,html,ts,tsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{js,jsx,json,html,ts,tsx,mjs}
: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (useyarn lint:js:cmd <file>
)
Files:
code/core/src/preview-api/modules/preview-web/render/mount-utils.ts
**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Export functions from modules when they need to be unit-tested
Files:
code/core/src/preview-api/modules/preview-web/render/mount-utils.ts
code/**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
In application code, use Storybook loggers instead of
console.*
(client code:storybook/internal/client-logger
; server code:storybook/internal/node-logger
)
Files:
code/core/src/preview-api/modules/preview-web/render/mount-utils.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Do not use
console.log
,console.warn
, orconsole.error
directly unless in isolated files where importing loggers would significantly increase bundle size
Files:
code/core/src/preview-api/modules/preview-web/render/mount-utils.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: normal
- GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (1)
code/core/src/preview-api/modules/preview-web/render/mount-utils.ts (1)
7-31
: Awareness: String parsing approach is fragile in production.The
getUsedProps
function usesfn.toString()
and string parsing to extract destructured props. While this is pre-existing code, be aware that this approach:
- May break with code minification/uglification
- Can produce different results across JavaScript engines
- Could fail in production builds where function representations differ
Consider verifying this works correctly in your production build pipeline, or exploring more robust alternatives like static analysis at build time if this becomes problematic.
Package BenchmarksCommit: The following packages have significant changes to their size or dependencies:
|
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 2 | 🚨 +2 🚨 |
Self size | 0 B | 508 KB | 🚨 +508 KB 🚨 |
Dependency size | 0 B | 2.97 MB | 🚨 +2.97 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/addon-docs
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 18 | 🚨 +18 🚨 |
Self size | 0 B | 2.07 MB | 🚨 +2.07 MB 🚨 |
Dependency size | 0 B | 10.21 MB | 🚨 +10.21 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/addon-jest
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 2 | 🚨 +2 🚨 |
Self size | 0 B | 47 KB | 🚨 +47 KB 🚨 |
Dependency size | 0 B | 53 KB | 🚨 +53 KB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/addon-links
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 1 | 🚨 +1 🚨 |
Self size | 0 B | 15 KB | 🚨 +15 KB 🚨 |
Dependency size | 0 B | 5 KB | 🚨 +5 KB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/addon-onboarding
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 0 | 0 |
Self size | 0 B | 333 KB | 🚨 +333 KB 🚨 |
Dependency size | 0 B | 670 B | 🚨 +670 B 🚨 |
Bundle Size Analyzer | Link | Link |
storybook-addon-pseudo-states
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 0 | 0 |
Self size | 0 B | 23 KB | 🚨 +23 KB 🚨 |
Dependency size | 0 B | 689 B | 🚨 +689 B 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/addon-themes
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 1 | 🚨 +1 🚨 |
Self size | 0 B | 20 KB | 🚨 +20 KB 🚨 |
Dependency size | 0 B | 28 KB | 🚨 +28 KB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/addon-vitest
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 6 | 🚨 +6 🚨 |
Self size | 0 B | 501 KB | 🚨 +501 KB 🚨 |
Dependency size | 0 B | 1.53 MB | 🚨 +1.53 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/builder-vite
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 11 | 🚨 +11 🚨 |
Self size | 0 B | 330 KB | 🚨 +330 KB 🚨 |
Dependency size | 0 B | 1.30 MB | 🚨 +1.30 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/builder-webpack5
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 187 | 🚨 +187 🚨 |
Self size | 0 B | 68 KB | 🚨 +68 KB 🚨 |
Dependency size | 0 B | 31.87 MB | 🚨 +31.87 MB 🚨 |
Bundle Size Analyzer | Link | Link |
storybook
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 43 | 🚨 +43 🚨 |
Self size | 0 B | 30.20 MB | 🚨 +30.20 MB 🚨 |
Dependency size | 0 B | 17.36 MB | 🚨 +17.36 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/angular
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 187 | 🚨 +187 🚨 |
Self size | 0 B | 126 KB | 🚨 +126 KB 🚨 |
Dependency size | 0 B | 30.00 MB | 🚨 +30.00 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/ember
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 191 | 🚨 +191 🚨 |
Self size | 0 B | 17 KB | 🚨 +17 KB 🚨 |
Dependency size | 0 B | 28.58 MB | 🚨 +28.58 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/html-vite
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 14 | 🚨 +14 🚨 |
Self size | 0 B | 23 KB | 🚨 +23 KB 🚨 |
Dependency size | 0 B | 1.67 MB | 🚨 +1.67 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/nextjs
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 532 | 🚨 +532 🚨 |
Self size | 0 B | 950 KB | 🚨 +950 KB 🚨 |
Dependency size | 0 B | 58.59 MB | 🚨 +58.59 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/nextjs-vite
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 124 | 🚨 +124 🚨 |
Self size | 0 B | 4.10 MB | 🚨 +4.10 MB 🚨 |
Dependency size | 0 B | 21.66 MB | 🚨 +21.66 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/preact-vite
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 14 | 🚨 +14 🚨 |
Self size | 0 B | 14 KB | 🚨 +14 KB 🚨 |
Dependency size | 0 B | 1.65 MB | 🚨 +1.65 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/react-native-web-vite
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 157 | 🚨 +157 🚨 |
Self size | 0 B | 31 KB | 🚨 +31 KB 🚨 |
Dependency size | 0 B | 23.04 MB | 🚨 +23.04 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/react-vite
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 114 | 🚨 +114 🚨 |
Self size | 0 B | 37 KB | 🚨 +37 KB 🚨 |
Dependency size | 0 B | 19.60 MB | 🚨 +19.60 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/react-webpack5
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 272 | 🚨 +272 🚨 |
Self size | 0 B | 25 KB | 🚨 +25 KB 🚨 |
Dependency size | 0 B | 43.55 MB | 🚨 +43.55 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/server-webpack5
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 199 | 🚨 +199 🚨 |
Self size | 0 B | 17 KB | 🚨 +17 KB 🚨 |
Dependency size | 0 B | 33.12 MB | 🚨 +33.12 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/svelte-vite
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 19 | 🚨 +19 🚨 |
Self size | 0 B | 59 KB | 🚨 +59 KB 🚨 |
Dependency size | 0 B | 26.79 MB | 🚨 +26.79 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/sveltekit
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 20 | 🚨 +20 🚨 |
Self size | 0 B | 58 KB | 🚨 +58 KB 🚨 |
Dependency size | 0 B | 26.85 MB | 🚨 +26.85 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/vue3-vite
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 109 | 🚨 +109 🚨 |
Self size | 0 B | 38 KB | 🚨 +38 KB 🚨 |
Dependency size | 0 B | 43.78 MB | 🚨 +43.78 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/web-components-vite
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 15 | 🚨 +15 🚨 |
Self size | 0 B | 20 KB | 🚨 +20 KB 🚨 |
Dependency size | 0 B | 1.70 MB | 🚨 +1.70 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/cli
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 187 | 🚨 +187 🚨 |
Self size | 0 B | 921 KB | 🚨 +921 KB 🚨 |
Dependency size | 0 B | 79.95 MB | 🚨 +79.95 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/codemod
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 169 | 🚨 +169 🚨 |
Self size | 0 B | 35 KB | 🚨 +35 KB 🚨 |
Dependency size | 0 B | 76.38 MB | 🚨 +76.38 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/core-webpack
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 1 | 🚨 +1 🚨 |
Self size | 0 B | 12 KB | 🚨 +12 KB 🚨 |
Dependency size | 0 B | 28 KB | 🚨 +28 KB 🚨 |
Bundle Size Analyzer | Link | Link |
create-storybook
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 44 | 🚨 +44 🚨 |
Self size | 0 B | 1.55 MB | 🚨 +1.55 MB 🚨 |
Dependency size | 0 B | 47.56 MB | 🚨 +47.56 MB 🚨 |
Bundle Size Analyzer | node | node |
@storybook/csf-plugin
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 9 | 🚨 +9 🚨 |
Self size | 0 B | 9 KB | 🚨 +9 KB 🚨 |
Dependency size | 0 B | 1.27 MB | 🚨 +1.27 MB 🚨 |
Bundle Size Analyzer | Link | Link |
eslint-plugin-storybook
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 35 | 🚨 +35 🚨 |
Self size | 0 B | 139 KB | 🚨 +139 KB 🚨 |
Dependency size | 0 B | 3.15 MB | 🚨 +3.15 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/react-dom-shim
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 0 | 0 |
Self size | 0 B | 22 KB | 🚨 +22 KB 🚨 |
Dependency size | 0 B | 788 B | 🚨 +788 B 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/preset-create-react-app
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 68 | 🚨 +68 🚨 |
Self size | 0 B | 36 KB | 🚨 +36 KB 🚨 |
Dependency size | 0 B | 5.98 MB | 🚨 +5.98 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/preset-react-webpack
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 170 | 🚨 +170 🚨 |
Self size | 0 B | 21 KB | 🚨 +21 KB 🚨 |
Dependency size | 0 B | 31.02 MB | 🚨 +31.02 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/preset-server-webpack
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 10 | 🚨 +10 🚨 |
Self size | 0 B | 8 KB | 🚨 +8 KB 🚨 |
Dependency size | 0 B | 1.20 MB | 🚨 +1.20 MB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/html
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 2 | 🚨 +2 🚨 |
Self size | 0 B | 30 KB | 🚨 +30 KB 🚨 |
Dependency size | 0 B | 32 KB | 🚨 +32 KB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/preact
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 2 | 🚨 +2 🚨 |
Self size | 0 B | 17 KB | 🚨 +17 KB 🚨 |
Dependency size | 0 B | 32 KB | 🚨 +32 KB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/react
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 2 | 🚨 +2 🚨 |
Self size | 0 B | 917 KB | 🚨 +917 KB 🚨 |
Dependency size | 0 B | 28 KB | 🚨 +28 KB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/server
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 3 | 🚨 +3 🚨 |
Self size | 0 B | 9 KB | 🚨 +9 KB 🚨 |
Dependency size | 0 B | 716 KB | 🚨 +716 KB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/svelte
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 2 | 🚨 +2 🚨 |
Self size | 0 B | 48 KB | 🚨 +48 KB 🚨 |
Dependency size | 0 B | 230 KB | 🚨 +230 KB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/vue3
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 3 | 🚨 +3 🚨 |
Self size | 0 B | 61 KB | 🚨 +61 KB 🚨 |
Dependency size | 0 B | 211 KB | 🚨 +211 KB 🚨 |
Bundle Size Analyzer | Link | Link |
@storybook/web-components
Before | After | Difference | |
---|---|---|---|
Dependency count | 0 | 3 | 🚨 +3 🚨 |
Self size | 0 B | 43 KB | 🚨 +43 KB 🚨 |
Dependency size | 0 B | 47 KB | 🚨 +47 KB 🚨 |
Bundle Size Analyzer | Link | Link |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love it!
Co-authored-by: Jeppe Reinhold <[email protected]>
Co-authored-by: Jeppe Reinhold <[email protected]>
Co-authored-by: Jeppe Reinhold <[email protected]>
Co-authored-by: Jeppe Reinhold <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
code/core/src/core-server/dev-server.ts (2)
139-139
: Typo in URL path:mainfests
should bemanifests
.This typo was already flagged in previous reviews.
140-145
: Move generator retrieval inside the feature flag check.For consistency with the static build implementation and to avoid unnecessary work when the feature is disabled, retrieve the generators only after confirming the feature flag is enabled. This was already noted in previous reviews.
🧹 Nitpick comments (2)
code/core/src/core-server/dev-server.ts (2)
150-151
: Return JSON error responses for consistency.The endpoint returns
application/json
for success but plain text for errors. For API consistency, error responses should also be JSON.This is addressed in the previous comment's diff, which returns JSON-formatted error responses.
139-153
: Consider caching the generated manifest.The manifest is regenerated on every request, which could be expensive for large codebases with many components. Since the manifest depends on the story index (which may change during development), consider implementing a caching strategy that invalidates when the index changes, similar to how the story index itself is cached and invalidated on file changes.
Example approach: store the manifest and a reference to the index version, regenerate only when the index version changes.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
code/core/src/core-server/build-static.ts
(3 hunks)code/core/src/core-server/dev-server.ts
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- code/core/src/core-server/build-static.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,json,html,ts,tsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{js,jsx,json,html,ts,tsx,mjs}
: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (useyarn lint:js:cmd <file>
)
Files:
code/core/src/core-server/dev-server.ts
**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Export functions from modules when they need to be unit-tested
Files:
code/core/src/core-server/dev-server.ts
code/**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
In application code, use Storybook loggers instead of
console.*
(client code:storybook/internal/client-logger
; server code:storybook/internal/node-logger
)
Files:
code/core/src/core-server/dev-server.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Do not use
console.log
,console.warn
, orconsole.error
directly unless in isolated files where importing loggers would significantly increase bundle size
Files:
code/core/src/core-server/dev-server.ts
🧬 Code graph analysis (1)
code/core/src/core-server/dev-server.ts (3)
code/renderers/react/src/preset.ts (1)
componentManifestGenerator
(18-49)code/core/src/types/modules/core-common.ts (1)
ComponentManifestGenerator
(353-355)code/core/src/core-server/presets/common-preset.ts (1)
features
(197-210)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: normal
- GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (1)
code/core/src/core-server/dev-server.ts (1)
11-11
: LGTM!The type-only import of
ComponentManifestGenerator
is appropriate and follows TypeScript best practices.
…r/code-snippets # Conflicts: # code/core/src/core-server/dev-server.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
code/core/src/core-server/dev-server.ts
(2 hunks)code/core/src/types/modules/core-common.ts
(4 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,json,html,ts,tsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{js,jsx,json,html,ts,tsx,mjs}
: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (useyarn lint:js:cmd <file>
)
Files:
code/core/src/core-server/dev-server.ts
code/core/src/types/modules/core-common.ts
**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Export functions from modules when they need to be unit-tested
Files:
code/core/src/core-server/dev-server.ts
code/core/src/types/modules/core-common.ts
code/**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
In application code, use Storybook loggers instead of
console.*
(client code:storybook/internal/client-logger
; server code:storybook/internal/node-logger
)
Files:
code/core/src/core-server/dev-server.ts
code/core/src/types/modules/core-common.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Do not use
console.log
,console.warn
, orconsole.error
directly unless in isolated files where importing loggers would significantly increase bundle size
Files:
code/core/src/core-server/dev-server.ts
code/core/src/types/modules/core-common.ts
🧬 Code graph analysis (2)
code/core/src/core-server/dev-server.ts (3)
code/core/src/core-server/presets/common-preset.ts (1)
features
(197-210)code/renderers/react/src/preset.ts (1)
componentManifestGenerator
(18-49)code/core/src/types/modules/core-common.ts (1)
ComponentManifestGenerator
(353-355)
code/core/src/types/modules/core-common.ts (1)
code/core/src/core-server/utils/StoryIndexGenerator.ts (1)
StoryIndexGenerator
(103-878)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: normal
- GitHub Check: Core Unit Tests, windows-latest
console.error(e); | ||
res.statusCode = 500; | ||
res.end(e instanceof Error ? e.toString() : String(e)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Use Storybook logger instead of console.error
.
Server-side code should log through storybook/internal/node-logger
per our guidelines. Please swap in logger.error
(optionally with a contextual message) so the log stream stays consistent.
- } catch (e) {
- console.error(e);
+ } catch (e) {
+ logger.error('Failed to generate component manifest', e);
res.statusCode = 500;
res.end(e instanceof Error ? e.toString() : String(e));
[As per coding guidelines]
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
console.error(e); | |
res.statusCode = 500; | |
res.end(e instanceof Error ? e.toString() : String(e)); | |
} catch (e) { | |
logger.error('Failed to generate component manifest', e); | |
res.statusCode = 500; | |
res.end(e instanceof Error ? e.toString() : String(e)); |
🤖 Prompt for AI Agents
In code/core/src/core-server/dev-server.ts around lines 158 to 160, replace the
direct console.error usage with the Storybook node logger: import logger from
'storybook/internal/node-logger' (or add to existing imports) and call
logger.error with a contextual message and the error (e.g. logger.error('Dev
server error', e)) instead of console.error(e); keep the response handling the
same (res.statusCode = 500; res.end(...)). Ensure the import is added if missing
and use the logger API consistently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (1)
code/core/src/core-server/build-static.ts (1)
178-178
: Critical: Fix typo in directory path.The path contains a typo: "mainfests" should be "manifests". This issue was already identified in previous review comments.
Apply this diff:
- join(options.outputDir, 'mainfests', 'components.json'), + join(options.outputDir, 'manifests', 'components.json'),
🧹 Nitpick comments (1)
code/core/src/core-server/build-static.ts (1)
179-179
: Consider pretty-printing the JSON output.For better developer experience when inspecting the generated manifest file, consider formatting the JSON with indentation.
Apply this diff:
- JSON.stringify(manifests) + JSON.stringify(manifests, null, 2)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
code/core/src/core-server/build-static.ts
(3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,json,html,ts,tsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{js,jsx,json,html,ts,tsx,mjs}
: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (useyarn lint:js:cmd <file>
)
Files:
code/core/src/core-server/build-static.ts
**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Export functions from modules when they need to be unit-tested
Files:
code/core/src/core-server/build-static.ts
code/**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
In application code, use Storybook loggers instead of
console.*
(client code:storybook/internal/client-logger
; server code:storybook/internal/node-logger
)
Files:
code/core/src/core-server/build-static.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Do not use
console.log
,console.warn
, orconsole.error
directly unless in isolated files where importing loggers would significantly increase bundle size
Files:
code/core/src/core-server/build-static.ts
🧬 Code graph analysis (1)
code/core/src/core-server/build-static.ts (3)
code/core/src/core-server/presets/common-preset.ts (1)
features
(197-210)code/renderers/react/src/preset.ts (1)
componentManifestGenerator
(18-49)code/core/src/types/modules/core-common.ts (1)
ComponentManifestGenerator
(353-355)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: normal
- GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (1)
code/core/src/core-server/build-static.ts (1)
1-1
: LGTM: Import additions are correct.The
writeFile
import andComponentManifestGenerator
type import are properly used in the manifest generation logic below.Also applies to: 21-21
|
||
const features = await presets.apply('features'); | ||
|
||
if (features?.experimental_componentsManifest) { | ||
const componentManifestGenerator: ComponentManifestGenerator = await presets.apply( | ||
'componentManifestGenerator' | ||
); | ||
const indexGenerator = await initializedStoryIndexGenerator; | ||
if (componentManifestGenerator && indexGenerator) { | ||
const manifests = await componentManifestGenerator(indexGenerator); | ||
await writeFile( | ||
join(options.outputDir, 'mainfests', 'components.json'), | ||
JSON.stringify(manifests) | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for manifest generation.
The manifest generation and file write operations have no error handling. If any step fails, it will crash the entire build process. Consider wrapping this block in a try-catch to log errors gracefully without breaking the build.
Apply this diff:
if (features?.experimental_componentsManifest) {
- const componentManifestGenerator: ComponentManifestGenerator = await presets.apply(
- 'componentManifestGenerator'
- );
- const indexGenerator = await initializedStoryIndexGenerator;
- if (componentManifestGenerator && indexGenerator) {
- const manifests = await componentManifestGenerator(indexGenerator);
- const manifestsDir = join(options.outputDir, 'manifests');
- await mkdir(manifestsDir, { recursive: true });
- await writeFile(
- join(manifestsDir, 'components.json'),
- JSON.stringify(manifests)
- );
+ try {
+ const componentManifestGenerator: ComponentManifestGenerator = await presets.apply(
+ 'componentManifestGenerator'
+ );
+ const indexGenerator = await initializedStoryIndexGenerator;
+ if (componentManifestGenerator && indexGenerator) {
+ const manifests = await componentManifestGenerator(indexGenerator);
+ const manifestsDir = join(options.outputDir, 'manifests');
+ await mkdir(manifestsDir, { recursive: true });
+ await writeFile(
+ join(manifestsDir, 'components.json'),
+ JSON.stringify(manifests)
+ );
+ }
+ } catch (error) {
+ logger.warn('Failed to generate component manifests:', error);
}
}
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In code/core/src/core-server/build-static.ts around lines 167 to 182, the
manifest generation and write operations are unguarded and can crash the build;
wrap the entire conditional block that calls componentManifestGenerator, awaits
indexGenerator, and writes the file in a try-catch, log any caught error with
context (including whether generation or file write failed) using the existing
logger (or processLogger), ensure the target directory is created before writing
(or handle write errors), and do not rethrow so the build continues gracefully
on failure.
throw indexError; | ||
} | ||
|
||
app.use('/manifests/components.json', async (req, res) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should call this app.use
if the feature flag is not enabled.
app.use('/manifests/components.json', async (req, res) => { | ||
try { | ||
const features = await options.presets.apply('features'); | ||
if (!features?.experimental_componentsManifest) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't this condition backwards?
if (!features?.experimental_componentsManifest) { | |
if (features?.experimental_componentsManifest) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
code/core/src/core-server/build-static.ts
(3 hunks)code/core/src/core-server/dev-server.ts
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- code/core/src/core-server/build-static.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,json,html,ts,tsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{js,jsx,json,html,ts,tsx,mjs}
: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (useyarn lint:js:cmd <file>
)
Files:
code/core/src/core-server/dev-server.ts
**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Export functions from modules when they need to be unit-tested
Files:
code/core/src/core-server/dev-server.ts
code/**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
In application code, use Storybook loggers instead of
console.*
(client code:storybook/internal/client-logger
; server code:storybook/internal/node-logger
)
Files:
code/core/src/core-server/dev-server.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Do not use
console.log
,console.warn
, orconsole.error
directly unless in isolated files where importing loggers would significantly increase bundle size
Files:
code/core/src/core-server/dev-server.ts
🧠 Learnings (2)
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx,mjs} : In application code, use Storybook loggers instead of `console.*` (client code: `storybook/internal/client-logger`; server code: `storybook/internal/node-logger`)
Applied to files:
code/core/src/core-server/dev-server.ts
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to scripts/**/*.{ts,js,mjs} : In Node.js scripts, use `storybook/internal/node-logger` instead of `console.*`
Applied to files:
code/core/src/core-server/dev-server.ts
🧬 Code graph analysis (1)
code/core/src/core-server/dev-server.ts (3)
code/core/src/core-server/presets/common-preset.ts (1)
features
(197-210)code/renderers/react/src/preset.ts (1)
componentManifestGenerator
(18-49)code/core/src/types/modules/core-common.ts (1)
ComponentManifestGenerator
(353-355)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: normal
- GitHub Check: Core Unit Tests, windows-latest
app.use('/manifests/components.json', async (req, res) => { | ||
try { | ||
const componentManifestGenerator: ComponentManifestGenerator = await options.presets.apply( | ||
'componentManifestGenerator' | ||
); | ||
const indexGenerator = await initializedStoryIndexGenerator; | ||
if (componentManifestGenerator && indexGenerator) { | ||
const manifest = await componentManifestGenerator(indexGenerator); | ||
res.setHeader('Content-Type', 'application/json'); | ||
res.end(JSON.stringify(manifest)); | ||
return; | ||
} | ||
res.statusCode = 400; | ||
res.end('No component manifest generator configured.'); | ||
return; | ||
} catch (e) { | ||
logger.error(e instanceof Error ? e : String(e)); | ||
res.statusCode = 500; | ||
res.end(e instanceof Error ? e.toString() : String(e)); | ||
return; | ||
} | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure consistent JSON response format and appropriate status codes.
The endpoint has good error handling, but there are inconsistencies:
-
Response format inconsistency: Success path returns JSON (line 150), but error paths return plain text (lines 154, 159). For a JSON API endpoint, all responses should use JSON format with appropriate Content-Type headers.
-
Inappropriate 400 status code: Line 153 returns 400 (client error) for "No component manifest generator configured," but this is a server configuration issue, not a client request problem. Use 500 or 503 instead.
-
Logger lacks context: Line 157's
logger.error(e instanceof Error ? e : String(e))
doesn't explain what operation failed. Add a descriptive message like'Failed to generate component manifest'
.
Apply this diff to improve consistency and clarity:
- res.statusCode = 400;
- res.end('No component manifest generator configured.');
+ res.statusCode = 500;
+ res.setHeader('Content-Type', 'application/json');
+ res.end(JSON.stringify({ error: 'No component manifest generator configured.' }));
return;
} catch (e) {
- logger.error(e instanceof Error ? e : String(e));
+ logger.error('Failed to generate component manifest:', e instanceof Error ? e : String(e));
res.statusCode = 500;
- res.end(e instanceof Error ? e.toString() : String(e));
+ res.setHeader('Content-Type', 'application/json');
+ res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
return;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
app.use('/manifests/components.json', async (req, res) => { | |
try { | |
const componentManifestGenerator: ComponentManifestGenerator = await options.presets.apply( | |
'componentManifestGenerator' | |
); | |
const indexGenerator = await initializedStoryIndexGenerator; | |
if (componentManifestGenerator && indexGenerator) { | |
const manifest = await componentManifestGenerator(indexGenerator); | |
res.setHeader('Content-Type', 'application/json'); | |
res.end(JSON.stringify(manifest)); | |
return; | |
} | |
res.statusCode = 400; | |
res.end('No component manifest generator configured.'); | |
return; | |
} catch (e) { | |
logger.error(e instanceof Error ? e : String(e)); | |
res.statusCode = 500; | |
res.end(e instanceof Error ? e.toString() : String(e)); | |
return; | |
} | |
}); | |
} | |
app.use('/manifests/components.json', async (req, res) => { | |
try { | |
const componentManifestGenerator: ComponentManifestGenerator = await options.presets.apply( | |
'componentManifestGenerator' | |
); | |
const indexGenerator = await initializedStoryIndexGenerator; | |
if (componentManifestGenerator && indexGenerator) { | |
const manifest = await componentManifestGenerator(indexGenerator); | |
res.setHeader('Content-Type', 'application/json'); | |
res.end(JSON.stringify(manifest)); | |
return; | |
} | |
res.statusCode = 500; | |
res.setHeader('Content-Type', 'application/json'); | |
res.end(JSON.stringify({ error: 'No component manifest generator configured.' })); | |
return; | |
} catch (e) { | |
logger.error('Failed to generate component manifest:', e instanceof Error ? e : String(e)); | |
res.statusCode = 500; | |
res.setHeader('Content-Type', 'application/json'); | |
res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) })); | |
return; | |
} | |
}); |
🤖 Prompt for AI Agents
In code/core/src/core-server/dev-server.ts around lines 141 to 163, the endpoint
mixes JSON and plain-text responses, uses an incorrect 400 status for a
server/configuration problem, and logs errors without context; update the
handler to always return JSON with Content-Type: application/json, return a 500
(or 503) status for the missing componentManifestGenerator case (use 500 unless
you prefer 503 for temporary service unavailability), and change logger.error to
include a descriptive message like "Failed to generate component manifest" along
with the error; ensure both success and error bodies are JSON objects (e.g., {
ok: true, manifest: ... } and { ok: false, error: "message" }), and set
res.statusCode accordingly before res.end(JSON.stringify(...)).
closes #32706, closes #32705
Create components.json file for dev and build with examples snippets generated from the stories files
What I did
Checklist for Contributors
Testing
The changes in this PR are covered in the following automated tests:
Manual testing
This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!
Documentation
MIGRATION.MD
Checklist for Maintainers
When this PR is ready for testing, make sure to add
ci:normal
,ci:merged
orci:daily
GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found incode/lib/cli-storybook/src/sandbox-templates.ts
Make sure this PR contains one of the labels below:
Available labels
bug
: Internal changes that fixes incorrect behavior.maintenance
: User-facing maintenance tasks.dependencies
: Upgrading (sometimes downgrading) dependencies.build
: Internal-facing build tooling & test updates. Will not show up in release changelog.cleanup
: Minor cleanup style change. Will not show up in release changelog.documentation
: Documentation only changes. Will not show up in release changelog.feature request
: Introducing a new feature.BREAKING CHANGE
: Changes that break compatibility in some way with current major version.other
: Changes that don't fit in the above categories.🦋 Canary release
This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the
@storybookjs/core
team here.core team members can create a canary release here or locally with
gh workflow run --repo storybookjs/storybook publish.yml --field pr=<PR_NUMBER>
Summary by CodeRabbit
New Features
Configuration
Tests
Refactor