Skip to content

Conversation

@kasperpeulen
Copy link
Contributor

@kasperpeulen kasperpeulen commented Oct 27, 2025

Closes #32848

What I did

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/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 pull request has been released as version 0.0.0-pr-32855-sha-350cba2a. Try it out in a new sandbox by running npx [email protected] sandbox or in an existing project with npx [email protected] upgrade.

More information
Published version 0.0.0-pr-32855-sha-350cba2a
Triggered by @kasperpeulen
Repository storybookjs/storybook
Branch kasper/error-manifest
Commit 350cba2a
Datetime Tue Oct 28 14:14:32 UTC 2025 (1761660872)
Workflow run 18877200892

To request a new release of this pull request, mention the @storybookjs/core team.

core team members can create a new canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=32855

Summary by CodeRabbit

  • New Features

    • Manifests now include component file path; examples may have optional snippets and per-entry/top-level error info.
    • Snippet generation accepts story identifiers and can emit either function- or variable-style snippets; improved code-location utilities and path helpers.
    • Added a runtime assertion helper.
  • Bug Fixes

    • Improved metadata synchronization and more robust, error-captured snippet generation and parsing.
  • Tests

    • Updated tests for manifest shape, error paths, meta-level and top-level export scenarios.
  • Chores

    • Added .junie to .gitignore.

@kasperpeulen kasperpeulen changed the base branch from next to 10.1-with-canary-release October 27, 2025 16:13
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 27, 2025

📝 Walkthrough

Walkthrough

Adds NodePath-backed tracking to CSF parsing (meta and story declaration paths), tightens BabelFile.path typing, changes ComponentManifest shape (adds path, makes example snippet optional and adds per-entry/top-level error), rewrites getCodeSnippet to accept (csf, storyName) and return AST nodes, and updates generators, enrichers, tests, and utilities to the CSF-driven flow.

Changes

Cohort / File(s) Summary
Core AST / CSF parsing
code/core/src/csf-tools/CsfFile.ts
Tighten BabelFile.path to NodePath<t.Program>; add _storyDeclarationPath: Record<string, NodePath<t.VariableDeclarator | t.FunctionDeclaration>> and `_metaStatementPath: NodePath<t.Statement>
Manifest types
code/core/src/types/modules/core-common.ts
ComponentManifest now includes path: string; examples become { name: string; snippet?: string; error?: { message: string } }[]; adds optional top-level error?: { message: string }.
Snippet generation (runtime)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts
Change signature to getCodeSnippet(csf: CsfFile, storyName: string, componentName?: string) returning `VariableDeclaration
Snippet tests
code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
Update tests to iterate csf._storyExports and call getCodeSnippet(csf, name, component); adjust snapshots; add tests for meta-level render and top-level exported functions; consolidate imports.
Manifest generator & tests
code/renderers/react/src/componentManifest/generator.ts, code/renderers/react/src/componentManifest/generator.test.ts
Filter entries by subtype === 'story'; build base manifest entries with id, path, examples, jsDocTags; generate per-story snippets with getCodeSnippet(csf, storyName, component) inside try/catch to capture per-example error; attach error to entries for missing componentPath, read/docgen failures; tests updated for new manifest shape.
Utilities
code/renderers/react/src/componentManifest/utils.ts
Add exported invariant(condition, message?) runtime assertion helper (supports lazy messages).
CSF enrichment
code/renderers/react/src/enrichCsf.ts
Switch to iterating csf._stories keys; obtain AST node via getCodeSnippet(csf, name, csf._meta?.component) then recast.print(node).code in guarded blocks; use csf._metaIsFactory and preserve per-story error handling.
Misc / Tests wiring
.gitignore, test mocks
Add .junie to .gitignore; adjust tsconfig-paths mock and test scaffolding (return null load results) and helper functions for manifest tests.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Parser as CsfFile
    participant Generator as ManifestGenerator
    participant Snippet as getCodeSnippet
    participant Enricher as EnrichCsf

    Generator->>Parser: parse file (BabelFile.path: NodePath)
    Parser-->>Generator: return CSF with _metaStatementPath & _storyDeclarationPath

    Generator->>Snippet: getCodeSnippet(csf, storyName, component?)
    Snippet->>Snippet: lookup declaration via csf._storyDeclarationPath[storyName]
    Snippet->>Snippet: normalize initializer (.bind, TS wrappers), merge meta/story args
    alt unresolved / invalid
        Snippet-->>Generator: throw code-frame error
    else
        Snippet-->>Generator: return AST node (VariableDeclaration | FunctionDeclaration)
    end

    Generator->>Enricher: provide AST node for formatting
    Enricher->>Enricher: recast.print(node).code -> format(...)
    alt format/print fails
        Enricher-->>Generator: record per-example error
    else
        Enricher-->>Generator: attach formatted snippet to docs.source.code
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • code/core/src/csf-tools/CsfFile.ts — correctness of _metaStatement_metaStatementPath synchronization and discovery across default/named/export shapes.
    • code/renderers/react/src/componentManifest/generateCodeSnippet.ts — initializer normalization (.bind, TS wrappers), arg merging/inlining, and code-frame error framing.
    • code/renderers/react/src/componentManifest/generator.ts & enrichCsf.ts — per-entry error propagation, manifest shape changes (path, example error), and formatting fallbacks.
    • code/core/src/types/modules/core-common.ts — callers must handle added path and optional example snippet/error and top-level error.

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link

nx-cloud bot commented Oct 27, 2025

View your CI Pipeline Execution ↗ for commit 74728f2

Command Status Duration Result
nx run-many -t build --parallel=3 ✅ Succeeded 47s View ↗

☁️ Nx Cloud last updated this comment at 2025-10-29 14:07:35 UTC

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 (12)
code/renderers/react/src/enrichCsf.ts (1)

21-27: Consider logging snippet generation failures for observability.

The try-catch silently swallows errors during code snippet generation. While avoiding user-facing errors for non-critical features is reasonable, consider logging failures (using Storybook's node-logger) to help maintainers identify and fix issues in snippet generation. This is especially valuable during development and debugging.

+import { logger } from 'storybook/internal/node-logger';
+
 export const enrichCsf: PresetPropertyFn<'experimental_enrichCsf'> = async (input, options) => {
   const features = await options.presets.apply('features');
   if (!features.experimentalCodeExamples) {
     return;
   }
   return async (csf: CsfFile, csfSource: CsfFile) => {
     const promises = Object.entries(csf._storyPaths).map(async ([key, storyExport]) => {
       if (!csfSource._meta?.component) {
         return;
       }
       const { format } = await getPrettier();
       let node;
       try {
         node = getCodeSnippet(storyExport, csfSource._metaNode, csfSource._meta?.component);
       } catch (e) {
-        // don't bother the user if we can't generate a snippet
+        logger.debug(`Failed to generate snippet for story "${key}":`, e);
         return;
       }

As per coding guidelines.

code/renderers/react/src/componentManifest/generator.test.ts (1)

218-218: Verify: Should successful cases show error: undefined?

The snapshots now include error: undefined for successful manifest generation. Consider whether the error field should be omitted entirely when there's no error (using optional field semantics) rather than explicitly setting it to undefined. This would make the successful case output cleaner and reduce payload size.

Current behavior:

{ error: undefined, name: "Button", ... }

Alternative:

{ name: "Button", ... }  // error field omitted when undefined

Also applies to: 322-322

code/renderers/react/src/componentManifest/generateCodeSnippet.ts (5)

30-33: Avoid eager code-frame construction in invariant for performance

Wrap the buildCodeFrameError(...).message in a lazy function, as elsewhere, to avoid doing work on the happy path.

-  invariant(
-    declarations.length === 1,
-    storyExportPath.buildCodeFrameError('Expected one story declaration').message
-  );
+  invariant(
+    declarations.length === 1,
+    () => storyExportPath.buildCodeFrameError('Expected one story declaration').message
+  );

84-89: Unwrap TS non‑null expressions during normalization

Handle TSNonNullExpression (expr!) like other TS wrappers so downstream checks see the inner expression.

-  normalizedInit = normalizedInit.isTSSatisfiesExpression()
-    ? normalizedInit.get('expression')
-    : normalizedInit.isTSAsExpression()
-      ? normalizedInit.get('expression')
-      : normalizedInit;
+  normalizedInit = normalizedInit.isTSSatisfiesExpression()
+    ? normalizedInit.get('expression')
+    : normalizedInit.isTSAsExpression()
+      ? normalizedInit.get('expression')
+      : normalizedInit.isTSNonNullExpression?.()
+        ? normalizedInit.get('expression')
+        : normalizedInit;

253-255: Add code‑frame context when component name is missing

Surface a code‑framed message to improve debugging; you have storyExportPath in scope.

-  invariant(componentName, 'Could not generate snippet without component name.');
+  invariant(
+    componentName,
+    () =>
+      storyExportPath.buildCodeFrameError(
+        'Could not generate snippet without component name. Did you set meta.component?'
+      ).message
+  );

326-342: Correct handling of namespaced JSX attributes (e.g., xlink:href)

toAttr builds t.jsxIdentifier(key) for all keys. For namespaced attrs containing :, this should be a JSXNamespacedName; otherwise the AST can be semantically off. Keep your existing validation; just construct the right node.

-const toAttr = (key: string, value: t.Node): t.JSXAttribute | null => {
-  if (t.isBooleanLiteral(value)) {
-    // Keep falsy boolean attributes by rendering an explicit expression container
-    return value.value
-      ? t.jsxAttribute(t.jsxIdentifier(key), null)
-      : t.jsxAttribute(t.jsxIdentifier(key), t.jsxExpressionContainer(value));
-  }
-
-  if (t.isStringLiteral(value)) {
-    return t.jsxAttribute(t.jsxIdentifier(key), t.stringLiteral(value.value));
-  }
-
-  if (t.isExpression(value)) {
-    return t.jsxAttribute(t.jsxIdentifier(key), t.jsxExpressionContainer(value));
-  }
-  return null; // non-expression nodes are not valid as attribute values
-};
+const toAttr = (key: string, value: t.Node): t.JSXAttribute | null => {
+  const makeName = (): t.JSXIdentifier | t.JSXNamespacedName | null => {
+    if (key.includes(':')) {
+      const [ns, local] = key.split(':');
+      if (!ns || !local) return null;
+      return t.jsxNamespacedName(t.jsxIdentifier(ns), t.jsxIdentifier(local));
+    }
+    return t.jsxIdentifier(key);
+  };
+  const name = makeName();
+  if (!name) return null;
+
+  if (t.isBooleanLiteral(value)) {
+    // Keep falsy boolean attributes by rendering an explicit expression container
+    return value.value
+      ? t.jsxAttribute(name, null)
+      : t.jsxAttribute(name, t.jsxExpressionContainer(value));
+  }
+  if (t.isStringLiteral(value)) {
+    return t.jsxAttribute(name, t.stringLiteral(value.value));
+  }
+  if (t.isExpression(value)) {
+    return t.jsxAttribute(name, t.jsxExpressionContainer(value));
+  }
+  return null; // non-expression nodes are not valid as attribute values
+};

496-522: Minor duplication of injection logic

makeInjectedPieces duplicates attr filtering logic already computed earlier. Consider extracting a small helper to compute injected attrs and invalid spread once to keep behavior consistent.

code/renderers/react/src/componentManifest/generator.ts (5)

33-34: Use map instead of flatMap with async callback

flatMap provides no benefit here (callbacks return non-array Promises) and is misleading.

-    const components = await Promise.all(
-      singleEntryPerComponent.flatMap(async (entry): Promise<ReactComponentManifest> => {
+    const components = await Promise.all(
+      singleEntryPerComponent.map(async (entry): Promise<ReactComponentManifest> => {

60-66: Rename loop variable to avoid shadowing path import

The [storyName, path] binding shadows the top‑level path import and hurts readability.

-          .map(([storyName, path]) => {
+          .map(([storyName, storyExportPath]) => {
             try {
               return {
                 name: storyName,
-                snippet: recast.print(getCodeSnippet(path, csf._metaNode ?? null, name)).code,
+                snippet: recast.print(getCodeSnippet(storyExportPath, csf._metaNode ?? null, name)).code,
               };

78-86: Likely unreachable !entry.componentPath branch

You filter entry.componentPath truthy earlier (Line 26), so this guard should never trigger. If intentional for future resilience, keep it; otherwise remove to reduce noise.


92-101: Preserve original I/O error in message

Including the cause eases debugging.

-            error: {
-              message: `Could not read the component file located at ${entry.componentPath}`,
-            },
+            error: {
+              message: `Could not read the component file located at ${entry.componentPath}: ${e.message}`,
+            },

110-111: Enrich docgen error with file context

Add the component path (and/or name) to make the error actionable.

-        const error = !docgen ? { message: 'Docgen could not find a component' } : undefined;
+        const error = !docgen
+          ? { message: `Docgen could not find a component in ${entry.componentPath} for "${name}"` }
+          : undefined;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26d8df6 and 40c0476.

📒 Files selected for processing (8)
  • code/core/src/csf-tools/CsfFile.ts (4 hunks)
  • code/core/src/types/modules/core-common.ts (1 hunks)
  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx (1 hunks)
  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts (5 hunks)
  • code/renderers/react/src/componentManifest/generator.test.ts (6 hunks)
  • code/renderers/react/src/componentManifest/generator.ts (3 hunks)
  • code/renderers/react/src/componentManifest/utils.ts (1 hunks)
  • code/renderers/react/src/enrichCsf.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 (use yarn lint:js:cmd <file>)

Files:

  • code/renderers/react/src/enrichCsf.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
  • code/renderers/react/src/componentManifest/utils.ts
  • code/renderers/react/src/componentManifest/generator.test.ts
  • code/core/src/types/modules/core-common.ts
  • code/renderers/react/src/componentManifest/generator.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts
  • code/core/src/csf-tools/CsfFile.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/renderers/react/src/enrichCsf.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
  • code/renderers/react/src/componentManifest/utils.ts
  • code/renderers/react/src/componentManifest/generator.test.ts
  • code/core/src/types/modules/core-common.ts
  • code/renderers/react/src/componentManifest/generator.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts
  • code/core/src/csf-tools/CsfFile.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/renderers/react/src/enrichCsf.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
  • code/renderers/react/src/componentManifest/utils.ts
  • code/renderers/react/src/componentManifest/generator.test.ts
  • code/core/src/types/modules/core-common.ts
  • code/renderers/react/src/componentManifest/generator.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts
  • code/core/src/csf-tools/CsfFile.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/renderers/react/src/enrichCsf.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
  • code/renderers/react/src/componentManifest/utils.ts
  • code/renderers/react/src/componentManifest/generator.test.ts
  • code/core/src/types/modules/core-common.ts
  • code/renderers/react/src/componentManifest/generator.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts
  • 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/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
  • code/renderers/react/src/componentManifest/generator.test.ts
**/*.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/componentManifest/generateCodeSnippet.test.tsx
  • code/renderers/react/src/componentManifest/generator.test.ts
**/*.@(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 using vi.mock() (e.g., filesystem, loggers)

Files:

  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
  • code/renderers/react/src/componentManifest/generator.test.ts
🧠 Learnings (6)
📚 Learning: 2025-09-17T08:11:47.197Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.197Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Document complex mock behaviors

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.ts
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mocked() to access and implement mock behaviors

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.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 **/*.@(test|spec).{ts,tsx,js,jsx} : Mock external dependencies in tests using `vi.mock()` (e.g., filesystem, loggers)

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.ts
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mocked() to type and access mocked functions

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.ts
📚 Learning: 2025-09-17T08:11:47.197Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.197Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use type-safe mocking with vi.mocked()

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.ts
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.ts
🧬 Code graph analysis (4)
code/renderers/react/src/enrichCsf.ts (1)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (1)
  • getCodeSnippet (18-275)
code/renderers/react/src/componentManifest/generator.test.ts (1)
code/renderers/react/src/componentManifest/generator.ts (1)
  • componentManifestGenerator (20-141)
code/renderers/react/src/componentManifest/generator.ts (6)
code/core/src/csf-tools/CsfFile.ts (1)
  • loadCsf (1058-1062)
code/core/src/types/modules/core-common.ts (1)
  • ComponentManifest (348-358)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (1)
  • getCodeSnippet (18-275)
code/renderers/react/src/componentManifest/utils.ts (1)
  • invariant (14-19)
code/core/src/csf-tools/enrichCsf.ts (1)
  • extractDescription (158-178)
code/renderers/react/src/componentManifest/jsdocTags.ts (1)
  • extractJSDocInfo (5-21)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (1)
code/renderers/react/src/componentManifest/utils.ts (1)
  • invariant (14-19)
⏰ 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 (10)
code/core/src/csf-tools/CsfFile.ts (3)

36-36: LGTM: Type safety improvement.

Changing from any to NodePath<t.Program> provides better type safety and aligns with Babel's actual API.


304-304: LGTM: NodePath tracking for enhanced error reporting.

The addition of _metaStatementPath complements the existing _metaStatement field and provides access to Babel's NodePath API for generating better error messages with code frames. The implementation correctly maintains consistency by setting both fields together in all code paths.

Also applies to: 490-497, 504-504


1052-1055: LGTM: Appropriate configuration for error handling context.

Adding highlightCode: false to the BabelFileClass options is reasonable for server-side tooling where automatic syntax highlighting may not be needed or where custom error formatting is preferred.

code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx (1)

80-94: LGTM! Improved error handling test.

The test now properly validates that invalid story exports throw descriptive errors with code frames, making debugging easier for developers.

code/renderers/react/src/enrichCsf.ts (1)

32-34: LGTM! Cleaner code with extracted node variable.

The refactoring improves readability by separating snippet generation from formatting.

code/renderers/react/src/componentManifest/utils.ts (1)

14-19: LGTM! Well-designed invariant helper.

The invariant function properly supports lazy message evaluation for performance and uses TypeScript's assertion signature for type narrowing. This is a clean, reusable utility for runtime checks.

code/renderers/react/src/componentManifest/generator.test.ts (3)

12-12: Simplified mock implementation.

The change from a spy-based mock to a direct implementation is cleaner and more explicit about the mock behavior (returning null result type).


418-482: LGTM! Well-structured test helpers.

The getManifestForStory and withCSF3 helper functions significantly improve test readability and maintainability by reducing duplication.


484-615: LGTM! Comprehensive error scenario coverage.

The three new test cases effectively cover important edge cases:

  1. Missing component field in meta
  2. Component exported from another file (re-export)
  3. Unknown/invalid story expressions

Each test validates that appropriate error messages with code frames are generated, which will greatly improve the developer experience when debugging manifest generation issues.

code/core/src/types/modules/core-common.ts (1)

348-358: Design is intentional and correctly typed; no changes required.

The optional name field is intentionally paired with the error field as an error-reporting mechanism. The code in generator.ts shows that name is only absent when componentName cannot be extracted (meta.component not specified), and in that case, the error field explains why. All other error scenarios include name. This is a valid pattern where the manifest can report processing failures while still providing a path reference for debugging. The type definition correctly reflects this design.

Copy link
Contributor

@JReinhold JReinhold left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM apart from a few comments.

I'll let @shilman judge the AST stuff, I don't know enough about it to approve it.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (2)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (2)

24-41: Solid error handling with room for minor optimization.

The normalization logic correctly handles both FunctionDeclaration and VariableDeclarator paths, and the use of code-frame errors significantly improves debugging.

Consider simplifying the invariant pattern at lines 30–35 to avoid constructing an Error object just to extract its message:

-    invariant(
-      init.isExpression(),
-      () =>
-        storyDeclaration.buildCodeFrameError('Expected story initializer to be an expression')
-          .message
-    );
+    if (!init.isExpression()) {
+      throw storyDeclaration.buildCodeFrameError('Expected story initializer to be an expression');
+    }

This pattern can be applied to similar invariant calls throughout the file (e.g., lines 66–74).


64-76: Consider more specific error messages for call expression validation.

The fallback logic for call expressions is correct, but both invariants (lines 66–69 and 71–74) use the same generic message "Could not evaluate story expression."

More specific messages would help users identify whether the issue is argument count or argument type:

       invariant(
         args.length === 1,
-        () => storyPath.buildCodeFrameError('Could not evaluate story expression').message
+        () => storyPath.buildCodeFrameError('Story factory must have exactly one argument').message
       );
       const storyArgument = args[0];
       invariant(
         storyArgument.isExpression(),
-        () => storyPath.buildCodeFrameError('Could not evaluate story expression').message
+        () => storyPath.buildCodeFrameError('Story factory argument must be an expression').message
       );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c1d131 and c6bbabd.

📒 Files selected for processing (3)
  • .gitignore (1 hunks)
  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx (4 hunks)
  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts (11 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
🧰 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 (use yarn lint:js:cmd <file>)

Files:

  • code/renderers/react/src/componentManifest/generateCodeSnippet.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/renderers/react/src/componentManifest/generateCodeSnippet.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/renderers/react/src/componentManifest/generateCodeSnippet.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts
🧬 Code graph analysis (1)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (1)
code/renderers/react/src/componentManifest/utils.ts (1)
  • invariant (14-19)
⏰ 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 (11)
.gitignore (1)

68-69: Note: File scope mismatch with PR objectives.

The provided file (.gitignore) does not align with the PR objectives, which focus on improving React error handling for component manifest generation, AST tracking, and schema extensions. Only a subset of files appears to be under review.

The .gitignore entry addition itself is straightforward and low-risk. However, please clarify:

  1. What is .junie? (artifact, build output, tool-specific file?)
  2. Are other files changed in this PR pending review?
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (10)

18-23: LGTM! Signature refactoring aligns with CSF1/CSF2 flexibility.

The broadened signature correctly accepts both variable declarators and function declarations, and the optional componentName allows for stories that may not need explicit component wrapping. The mixed return type properly reflects the different output formats.


79-83: LGTM! TypeScript type assertion unwrapping is handled correctly.

The nested ternary pattern cleanly unwraps both satisfies and as expressions.


86-100: LGTM! Function detection logic is comprehensive.

The code correctly identifies arrow functions, function expressions, and function declarations, and appropriately throws a code-frame error for unsupported story shapes.


102-118: LGTM! CSF2 render function extraction is well-structured.

The code correctly prioritizes an explicit render() function when present in CSF2 object stories.


120-138: LGTM! Args merging follows CSF semantics.

The code correctly merges meta.args and story.args with proper override behavior, and appropriately separates valid JSX attribute names from invalid ones.


140-156: LGTM! Arrow function JSX transformation is correct.

The code properly handles arrow functions that directly return JSX elements or fragments, applying spread transformation and arg inlining as needed.


158-211: LGTM! Block body transformation comprehensively handles function variants.

The code correctly extracts block bodies from function declarations, arrow functions, and function expressions, transforming only return statements that return JSX. The preservation of function form (declaration vs. expression) in the output is appropriate.


214-236: LGTM! Fallback JSX generation is sound.

The invariant at line 217 ensures componentName is present before constructing the JSX element, preventing potential null/undefined errors. The fallback correctly builds a simple arrow function returning the component with merged args.


542-559: LGTM! Children injection logic is correct.

The code correctly recurses into children and only injects merged.children when appropriate (args spread present, no existing children, and children available in merged args). This prevents unintentionally overwriting existing children.


586-590: LGTM! Signature broadening improves reusability.

Changing the parameter from a specific export declaration type to a generic NodePath<t.Node> makes the function more flexible while maintaining correctness.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 (2)
code/renderers/react/src/componentManifest/generator.ts (2)

58-77: Strong per-story error handling; remove unnecessary filter.

The per-story error handling with try/catch is excellent and aligns with the PR objective. Each story failure is captured individually without breaking the entire manifest generation.

However, the .filter(Boolean) on line 77 is unnecessary since the map callback always returns an object (either with snippet or error), never a falsy value.

Apply this diff to remove the unnecessary filter:

           })
-          .filter(Boolean);
+;

89-103: Robust file reading error handling.

The try/catch around file reading is excellent. The non-null assertion on line 92 is safe due to the early return at line 79, but consider extracting entry.componentPath to a variable after the check for improved clarity.

Optional: Extract componentPath after the check for improved type safety and clarity:

         if (!entry.componentPath) {
           const message = `No component file found for the "${name}" component.`;
           return {
             ...base,
             name,
             examples,
             error: { message },
           };
         }
+        const componentPath = entry.componentPath;

         let componentFile;

         try {
-          componentFile = await readFile(path.join(process.cwd(), entry.componentPath!), 'utf-8');
+          componentFile = await readFile(path.join(process.cwd(), componentPath), 'utf-8');
         } catch (e) {
           invariant(e instanceof Error);
           return {
             ...base,
             name,
             examples,
             error: {
-              message: `Could not read the component file located at ${entry.componentPath}`,
+              message: `Could not read the component file located at ${componentPath}`,
             },
           };
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c6bbabd and a5fc2b3.

📒 Files selected for processing (1)
  • code/renderers/react/src/componentManifest/generator.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 (use yarn lint:js:cmd <file>)

Files:

  • code/renderers/react/src/componentManifest/generator.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/renderers/react/src/componentManifest/generator.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/renderers/react/src/componentManifest/generator.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/renderers/react/src/componentManifest/generator.ts
🧬 Code graph analysis (1)
code/renderers/react/src/componentManifest/generator.ts (5)
code/core/src/csf-tools/CsfFile.ts (1)
  • loadCsf (1060-1064)
code/core/src/types/modules/core-common.ts (1)
  • ComponentManifest (348-358)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (1)
  • getCodeSnippet (18-237)
code/renderers/react/src/componentManifest/utils.ts (1)
  • invariant (14-19)
code/core/src/csf-tools/enrichCsf.ts (1)
  • extractDescription (158-178)
🔇 Additional comments (4)
code/renderers/react/src/componentManifest/generator.ts (4)

47-56: Excellent error handling with code context.

This change perfectly aligns with the PR objective to surface errors with line numbers and surrounding context instead of silently skipping components. Using buildCodeFrameError provides valuable debugging information.


79-87: Good error surfacing for missing component paths.

This properly handles the case where componentPath is missing, surfacing the error in the manifest instead of silently skipping. This works in tandem with the relaxed filter condition on line 26.


111-111: Good docgen failure surfacing.

Computing and attaching the error when docgen fails to find a component ensures failures are visible in the manifest instead of being silently ignored.


119-129: Consistent manifest structure with proper error propagation.

The final manifest assembly properly spreads the base object and includes the computed error field. The populated examples array correctly overwrites the empty array from base, ensuring all story snippets and their errors are included.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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/renderers/react/src/componentManifest/generator.ts (1)

42-60: Remove unnecessary .filter(Boolean).

The .map() callback always returns an object (either with snippet or error), so the .filter(Boolean) on line 60 is redundant. All mapped values are truthy.

Apply this diff:

         })
-        .filter(Boolean);
+;

Otherwise, the per-story error handling looks excellent and aligns well with the PR objectives—errors are captured with their messages rather than being hidden.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a5fc2b3 and 350cba2.

📒 Files selected for processing (1)
  • code/renderers/react/src/componentManifest/generator.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 (use yarn lint:js:cmd <file>)

Files:

  • code/renderers/react/src/componentManifest/generator.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/renderers/react/src/componentManifest/generator.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/renderers/react/src/componentManifest/generator.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/renderers/react/src/componentManifest/generator.ts
🧬 Code graph analysis (1)
code/renderers/react/src/componentManifest/generator.ts (6)
code/core/src/csf-tools/CsfFile.ts (1)
  • loadCsf (1060-1064)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (1)
  • getCodeSnippet (18-237)
code/renderers/react/src/componentManifest/utils.ts (1)
  • invariant (14-19)
code/core/src/types/modules/core-common.ts (1)
  • ComponentManifest (348-358)
code/core/src/csf-tools/enrichCsf.ts (1)
  • extractDescription (158-178)
code/renderers/react/src/componentManifest/jsdocTags.ts (1)
  • extractJSDocInfo (5-21)
⏰ 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). (1)
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (6)
code/renderers/react/src/componentManifest/generator.ts (6)

37-40: LGTM!

The extraction of id, importPath, and name is clear and sets up the base data for manifest generation.


62-67: LGTM!

The base object pattern avoids duplication across the multiple return paths and ensures consistent manifest structure. The satisfies clause provides good type safety.


69-78: LGTM!

The missing componentName handling is excellent:

  • Clear, actionable error message
  • Uses buildCodeFrameError to provide line numbers and context
  • Falls back gracefully when _metaStatementPath is unavailable
  • Aligns perfectly with the PR objective to surface errors with context

80-104: LGTM!

The error handling for missing or unreadable component files is solid:

  • Returns partial manifests with name and examples when available
  • Provides clear error messages about what went wrong
  • The non-null assertion on line 93 is safe because we're inside the else branch
  • Preserving partial data helps users understand what succeeded before the failure

This aligns well with the PR objective to report errors clearly while providing maximum context.


106-130: LGTM!

The docgen handling and final manifest assembly are well-structured:

  • Sets an error when docgen fails but continues processing to provide as much data as possible
  • Includes all available metadata (description, summary, tags, examples)
  • The error field is conditionally included only when needed
  • Spreading the base object maintains consistency across all return paths

This approach maximizes the information available to users even when parts of the manifest generation fail, which aligns perfectly with the PR objectives.


26-26: The subtype filter is necessary and correct—it is not redundant.

The type definition shows that StoryIndexEntry entries can have subtype: 'story' | 'test'. Test entries share type: 'story' with regular stories but have subtype: 'test'. The second filter correctly excludes these test entries from the component manifest, which should only include regular stories, not tests. The filter is defensive and intentional, not unnecessary.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (1)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (1)

528-547: Good addition of pathForNode public export.

Exporting this function aligns with coding guidelines for unit testing. The implementation correctly traverses the program and stops early upon finding the target node.

One potential optimization: if pathForNode is called multiple times in a single transformation (e.g., for multiple stories or meta objects), consider caching the traversal results or using a NodePath lookup map to avoid repeated full-program traversals.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 556e266 and 0c76373.

📒 Files selected for processing (1)
  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts (6 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 (use yarn lint:js:cmd <file>)

Files:

  • code/renderers/react/src/componentManifest/generateCodeSnippet.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/renderers/react/src/componentManifest/generateCodeSnippet.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/renderers/react/src/componentManifest/generateCodeSnippet.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts
🧬 Code graph analysis (1)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (2)
code/core/src/csf-tools/CsfFile.ts (2)
  • CsfFile (281-1042)
  • meta (964-966)
code/renderers/react/src/componentManifest/utils.ts (1)
  • invariant (14-19)
⏰ 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 (15)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (15)

1-4: LGTM!

The imports are appropriate for the CSF-driven refactor.


19-36: LGTM!

The normalization logic correctly handles both function declarations and variable declarators, with appropriate error reporting using code frames.


38-72: LGTM!

The Template.bind and factory pattern handling is robust. The invariant at lines 61-64 correctly enforces that factory calls must have exactly one argument, which is a reasonable constraint for CSF story factories.


74-96: LGTM!

The TypeScript wrapper stripping and function resolution logic correctly handles the various CSF story formats.


130-149: LGTM!

The transformation logic for arrow functions with direct JSX returns correctly applies spread expansion and args inlining.


151-187: LGTM!

The block statement transformation correctly handles return statements and reconstructs the appropriate function type.


189-192: LGTM!

The fallback return correctly wraps function expressions in a variable declaration while preserving function declarations.


194-211: LGTM!

The fallback component synthesis correctly handles cases where no story function exists, appropriately handling invalid attribute names via spread syntax.


224-289: LGTM!

The helper functions are well-structured and handle the various node types appropriately.


291-317: LGTM!

The getArgsMemberKey function comprehensively handles member expressions, including optional chaining.


319-397: LGTM!

The inlineArgsInJsx function correctly handles recursive transformation of JSX trees, appropriately inlining args.* references and handling self-closing elements.


399-489: LGTM!

The transformArgsSpreadsInJsx function correctly expands {...args} spreads while avoiding duplicate attributes and preserving insertion order.


491-526: LGTM!

The resolveBindIdentifierInit function correctly resolves template identifiers, handling both top-level and exported variable declarations.


107-118: ****

The concern about FunctionDeclaration is based on an AST misunderstanding. The code correctly handles all valid render function patterns:

  • In object property positions, functions can only be FunctionExpression (e.g., render: function Name() {}) or ArrowFunctionExpression
  • FunctionDeclaration is a top-level statement syntax and cannot appear as an object property value
  • The codebase has 5 examples of render: function Name() patterns, all of which are FunctionExpression nodes and are correctly matched by v.isFunctionExpression()

The current implementation is correct.

Likely an incorrect or invalid review comment.


213-222: Correct the type annotation for merged to eliminate unnecessary defensive logic.

The type Record<string, t.Node> on line 128 is overly broad. Values in merged come from object properties and are always t.Expression instances. Using a correct type eliminates the need for the defensive t.isExpression(v) check on line 219 and prevents confusion.

Change line 128 to:

const merged: Record<string, t.Expression | undefined> = { ...metaArgs, ...storyArgs };

Then simplify line 219 to use the value directly without the fallback, or remove the unnecessary check entirely since all values will be expressions.

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

♻️ Duplicate comments (2)
code/renderers/react/src/componentManifest/generator.test.ts (1)

536-636: Add tests for missing componentPath and meta.render extraction.

  • Missing componentPath should surface a clear top-level error.
  • Validate snippet extraction when only meta.render is provided. This aligns with the PR’s error-reporting goals and a past comment.

I can draft these tests if helpful.

code/renderers/react/src/componentManifest/generator.ts (1)

78-92: Same here: don’t crash on non-Error when reading component file.

-        } catch (e) {
-          invariant(e instanceof Error);
+        } catch (e) {
           return {
             ...base,
             name,
             examples,
             error: {
-              message: `Could not read the component file located at ${entry.componentPath}`,
+              message: e instanceof Error
+                ? `Could not read the component file located at ${entry.componentPath}`
+                : String(e),
             },
           };
         }
🧹 Nitpick comments (10)
code/core/src/csf-tools/enrichCsf.test.ts (1)

17-21: DRY the fallback and add a direct “no-title” test.

Factor the fallback into a shared const and add one test that omits userTitle to assert 'Unknown' is used. Keeps intent explicit.

code/renderers/react/src/componentManifest/utils.ts (1)

14-23: LGTM; small ergonomic tweak.

Consider naming the error for easier filtering in logs.

Apply:

-  throw new Error((typeof message === 'function' ? message() : message) ?? 'Invariant failed');
+  const err = new Error(
+    (typeof message === 'function' ? message() : message) ?? 'Invariant failed'
+  );
+  err.name = 'InvariantError';
+  throw err;
code/renderers/react/src/componentManifest/generator.test.ts (3)

10-13: Use spy: true for module mocks per test guidelines.

Add spy mode to all vi.mock calls so you can introspect/override as needed.

As per coding guidelines.

-vi.mock('node:fs/promises', async () => (await import('memfs')).fs.promises);
-vi.mock('node:fs', async () => (await import('memfs')).fs);
-vi.mock('tsconfig-paths', () => ({ loadConfig: () => ({ resultType: null!, message: null! }) }));
+vi.mock('node:fs/promises', async () => (await import('memfs')).fs.promises, { spy: true });
+vi.mock('node:fs', async () => (await import('memfs')).fs, { spy: true });
+vi.mock(
+  'tsconfig-paths',
+  () => ({ loadConfig: vi.fn(() => ({ resultType: null as any, message: null as any })) }),
+  { spy: true }
+);

96-205: Add cleanup for mocks/filesystem between tests.

beforeEach returning a function isn’t executed by Vitest. Add afterEach to reset memfs and restore the cwd spy.

Apply:

 beforeEach(() => {
   vi.spyOn(process, 'cwd').mockReturnValue('/app');
   vol.fromJSON(
     {
       // ...
     },
     '/app'
   );
-  return () => vol.reset();
 });
+
+afterEach(() => {
+  vol.reset();
+  (process.cwd as unknown as { mockRestore: () => void }).mockRestore?.();
+});

As per coding guidelines.


418-483: withCSF3 helper: import fn for syntactic completeness.

Not required for parsing, but importing fn avoids accidental lint or transform issues in the future.

   return dedent`
     import type { Meta } from '@storybook/react';
+    import { fn } from 'storybook/test';
     import { Button } from './Button';
code/renderers/react/src/enrichCsf.ts (2)

20-21: Avoid per-story Prettier import.

Call getPrettier once outside the map to cut overhead.

-    const promises = Object.keys(csf._stories).map(async (key) => {
-      if (!csfSource._meta?.component) {
-        return;
-      }
-      const { format } = await getPrettier();
+    const { format } = await getPrettier();
+    const promises = Object.keys(csf._stories).map(async (key) => {
+      if (!csfSource._meta?.component) {
+        return;
+      }

Also applies to: 32-39


17-19: Surface missing component as an inline error instead of skipping.

Early return hides the problem. Prefer injecting a docs.source message like “Could not generate snippet without component name.”

Do consumers expect docs.source.code to show error text, or should we add docs.source.error?

code/renderers/react/src/componentManifest/generator.ts (2)

30-33: Deterministic component representative per group.

Picking group[0] from Object.values order can be unstable. Sort entries in each group (e.g., by exportName or id) and take the first.

-const singleEntryPerComponent = Object.values(groupByComponentId).flatMap((group) =>
-  group && group?.length > 0 ? [group[0]] : []
-);
+const singleEntryPerComponent = Object.values(groupByComponentId).flatMap((group) => {
+  if (!group?.length) return [];
+  const [first] = [...group].sort((a, b) => a.id.localeCompare(b.id));
+  return [first];
+});

58-59: Minor: filter(Boolean) on examples is redundant.

The map always returns an object; drop the filter.

-        const examples = Object.keys(csf._stories)
-          .map((storyName) => {
+        const examples = Object.keys(csf._stories).map((storyName) => {
             // ...
-          })
-          .filter(Boolean);
+          });
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (1)

528-547: Document the new public export pathForNode.

This function is now part of the public API and serves as a bridge between AST nodes and Babel NodePaths. Consider adding JSDoc comments to document:

  • The purpose (converting an AST node to its corresponding traversal path)
  • When to use it (CSF-driven processing when you have nodes but need paths)
  • Performance characteristics (traverses the entire program)

Example documentation:

+/**
+ * Find the NodePath for a given AST node within a program.
+ * @param program - The program path to search within
+ * @param target - The AST node to find
+ * @returns The NodePath for the target node, or undefined if not found
+ */
 export function pathForNode<T extends t.Node>(
   program: NodePath<t.Program>,
   target: T | undefined
 ): NodePath<T> | undefined {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c76373 and 8e4219c.

📒 Files selected for processing (7)
  • code/core/src/csf-tools/enrichCsf.test.ts (1 hunks)
  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx (6 hunks)
  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts (6 hunks)
  • code/renderers/react/src/componentManifest/generator.test.ts (6 hunks)
  • code/renderers/react/src/componentManifest/generator.ts (3 hunks)
  • code/renderers/react/src/componentManifest/utils.ts (1 hunks)
  • code/renderers/react/src/enrichCsf.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
🧰 Additional context used
📓 Path-based instructions (7)
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/componentManifest/generator.test.ts
  • code/core/src/csf-tools/enrichCsf.test.ts
**/*.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/componentManifest/generator.test.ts
  • code/core/src/csf-tools/enrichCsf.test.ts
**/*.{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 (use yarn lint:js:cmd <file>)

Files:

  • code/renderers/react/src/componentManifest/generator.test.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts
  • code/core/src/csf-tools/enrichCsf.test.ts
  • code/renderers/react/src/enrichCsf.ts
  • code/renderers/react/src/componentManifest/utils.ts
  • code/renderers/react/src/componentManifest/generator.ts
**/*.@(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 using vi.mock() (e.g., filesystem, loggers)

Files:

  • code/renderers/react/src/componentManifest/generator.test.ts
  • code/core/src/csf-tools/enrichCsf.test.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/renderers/react/src/componentManifest/generator.test.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts
  • code/core/src/csf-tools/enrichCsf.test.ts
  • code/renderers/react/src/enrichCsf.ts
  • code/renderers/react/src/componentManifest/utils.ts
  • code/renderers/react/src/componentManifest/generator.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/renderers/react/src/componentManifest/generator.test.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts
  • code/core/src/csf-tools/enrichCsf.test.ts
  • code/renderers/react/src/enrichCsf.ts
  • code/renderers/react/src/componentManifest/utils.ts
  • code/renderers/react/src/componentManifest/generator.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/renderers/react/src/componentManifest/generator.test.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts
  • code/core/src/csf-tools/enrichCsf.test.ts
  • code/renderers/react/src/enrichCsf.ts
  • code/renderers/react/src/componentManifest/utils.ts
  • code/renderers/react/src/componentManifest/generator.ts
🧠 Learnings (7)
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mocked() to access and implement mock behaviors

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.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 **/*.@(test|spec).{ts,tsx,js,jsx} : Mock external dependencies in tests using `vi.mock()` (e.g., filesystem, loggers)

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.ts
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mocked() to type and access mocked functions

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.ts
📚 Learning: 2025-09-17T08:11:47.197Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.197Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use type-safe mocking with vi.mocked()

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.ts
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.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/renderers/react/src/enrichCsf.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 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/renderers/react/src/enrichCsf.ts
🧬 Code graph analysis (5)
code/renderers/react/src/componentManifest/generator.test.ts (1)
code/renderers/react/src/componentManifest/generator.ts (1)
  • componentManifestGenerator (20-131)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (2)
code/core/src/csf-tools/CsfFile.ts (2)
  • CsfFile (281-1042)
  • meta (964-966)
code/renderers/react/src/componentManifest/utils.ts (1)
  • invariant (15-23)
code/core/src/csf-tools/enrichCsf.test.ts (1)
code/core/src/csf-tools/CsfFile.ts (1)
  • loadCsf (1060-1064)
code/renderers/react/src/enrichCsf.ts (2)
code/core/src/common/utils/formatter.ts (1)
  • getPrettier (1-6)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (1)
  • getCodeSnippet (6-211)
code/renderers/react/src/componentManifest/generator.ts (6)
code/renderers/react/src/componentManifest/utils.ts (2)
  • groupBy (2-12)
  • invariant (15-23)
code/core/src/csf-tools/CsfFile.ts (1)
  • loadCsf (1060-1064)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (1)
  • getCodeSnippet (6-211)
code/core/src/types/modules/core-common.ts (1)
  • ComponentManifest (348-358)
code/core/src/csf-tools/enrichCsf.ts (1)
  • extractDescription (158-178)
code/renderers/react/src/componentManifest/jsdocTags.ts (1)
  • extractJSDocInfo (5-21)
⏰ 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 (7)
code/renderers/react/src/componentManifest/generator.ts (1)

36-40: Confirm type of csf._meta?.component used for name.

If component can be non-string in some CSF variants, guard to fallback to title segment.

-const name = csf._meta?.component ?? entry.title.split('/').at(-1)!;
+const metaName = csf._meta?.component;
+const name = typeof metaName === 'string' ? metaName : entry.title.split('/').at(-1)!;
code/renderers/react/src/enrichCsf.ts (1)

90-103: Change aligns with documented API and existing consumer expectations — verified.

Downstream consumers confirm the expectation:

  • Manager addon reads parameter.source?.code as primary field, falling back to parameter.source?.originalSource
  • Source block directly accesses source.code
  • Vue3 renderer documents parameters.docs.source.code as a valid story parameter

The implementation correctly adds the code field via the snippet while preserving existing source properties through the spread operator (...docsParameter.source), maintaining backward compatibility with any originalSource that may be present.

code/renderers/react/src/componentManifest/generateCodeSnippet.ts (5)

6-17: LGTM! Error handling and private field access are appropriate.

The refactored signature and CSF-driven approach improve error reporting. The use of csf._storyDeclarationPath, csf._metaNode, and csf._storyPaths to access private fields is acceptable since no public accessors exist for these fields in the CsfFile API. The error framing using buildCodeFrameError provides good context for debugging.


19-96: LGTM! Story normalization logic handles multiple CSF patterns correctly.

The normalization flow properly handles:

  • Function declarations vs. variable declarators
  • Template.bind(...) pattern resolution
  • Factory function unwrapping
  • TypeScript type annotations (satisfies/as)
  • Both function-based and object-based stories

Error messages provide clear context via buildCodeFrameError.


137-192: LGTM! Efficient JSX transformation with proper change tracking.

The transformation logic correctly:

  • Handles arrow functions with direct JSX bodies
  • Processes block statements with return statements
  • Tracks changes and only creates new declarations when needed
  • Returns the appropriate declaration type (FunctionDeclaration vs. VariableDeclaration)
  • Preserves the original function when no transformations apply

319-397: LGTM! Recursive inlining handles all JSX node types correctly.

The inlineArgsInJsx function properly:

  • Recursively processes nested JSX elements and fragments
  • Inlines args.foo references into concrete values
  • Handles the special args.children case
  • Removes attributes when inlined values are null
  • Tracks changes with the ||= operator

399-489: LGTM! Spread expansion logic correctly avoids duplicate attributes.

The transformArgsSpreadsInJsx function properly:

  • Expands {...args} into concrete JSX attributes
  • Tracks existing attribute names to avoid duplicates
  • Preserves the insertion position of the first spread
  • Handles invalid JSX attribute names via spread objects
  • Injects children when appropriate

Comment on lines +41 to +57
const examples = Object.keys(csf._stories)
.map((storyName) => {
try {
return {
name: storyName,
snippet: recast.print(getCodeSnippet(csf, storyName, name)).code,
};
} catch (e) {
invariant(e instanceof Error);
return {
name: storyName,
error: {
message: e.message,
},
};
}
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Capture unknown exceptions without crashing.

Using invariant(e instanceof Error) can rethrow and abort generation. Always coerce to a string.

-          .map((storyName) => {
+          .map((storyName) => {
             try {
               return {
                 name: storyName,
                 snippet: recast.print(getCodeSnippet(csf, storyName, name)).code,
               };
             } catch (e) {
-              invariant(e instanceof Error);
+              const message = e instanceof Error ? e.message : String(e);
               return {
                 name: storyName,
                 error: {
-                  message: e.message,
+                  message,
                 },
               };
             }
           })
🤖 Prompt for AI Agents
In code/renderers/react/src/componentManifest/generator.ts around lines 41 to
57, the current catch uses invariant(e instanceof Error) which can throw and
abort generation; instead remove the invariant and safely coerce any thrown
value to a string (e.g., use const msg = e instanceof Error ? e.message :
String(e) or JSON.stringify fallback) and return the story object with error: {
message: msg } so unknown exceptions are captured without crashing.

Comment on lines +16 to 31
const promises = Object.keys(csf._stories).map(async (key) => {
if (!csfSource._meta?.component) {
return;
}
const { format } = await getPrettier();

let node;
let snippet;
try {
const code = recast.print(
getCodeSnippet(storyExport, csfSource._metaNode, csfSource._meta?.component)
).code;

// TODO read the user config
snippet = await format(code, { filepath: join(process.cwd(), 'component.tsx') });
node = getCodeSnippet(csfSource, key, csfSource._meta?.component);
} catch (e) {
// don't bother the user if we can't generate a snippet
return;
if (!(e instanceof Error)) {
return;
}
snippet = e.message;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t drop non-Error throws; coerce to string.

Returning early on non-Error hides failures; always surface a message.

Apply:

-      try {
-        node = getCodeSnippet(csfSource, key, csfSource._meta?.component);
-      } catch (e) {
-        if (!(e instanceof Error)) {
-          return;
-        }
-        snippet = e.message;
-      }
+      try {
+        node = getCodeSnippet(csfSource, key, csfSource._meta?.component);
+      } catch (e) {
+        snippet = e instanceof Error ? e.message : String(e);
+      }
📝 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.

Suggested change
const promises = Object.keys(csf._stories).map(async (key) => {
if (!csfSource._meta?.component) {
return;
}
const { format } = await getPrettier();
let node;
let snippet;
try {
const code = recast.print(
getCodeSnippet(storyExport, csfSource._metaNode, csfSource._meta?.component)
).code;
// TODO read the user config
snippet = await format(code, { filepath: join(process.cwd(), 'component.tsx') });
node = getCodeSnippet(csfSource, key, csfSource._meta?.component);
} catch (e) {
// don't bother the user if we can't generate a snippet
return;
if (!(e instanceof Error)) {
return;
}
snippet = e.message;
}
const promises = Object.keys(csf._stories).map(async (key) => {
if (!csfSource._meta?.component) {
return;
}
const { format } = await getPrettier();
let node;
let snippet;
try {
node = getCodeSnippet(csfSource, key, csfSource._meta?.component);
} catch (e) {
snippet = e instanceof Error ? e.message : String(e);
}
🤖 Prompt for AI Agents
In code/renderers/react/src/enrichCsf.ts around lines 16 to 31, the catch block
currently returns early for non-Error throws which hides failures; change it to
always capture a message instead: remove the early return and set snippet to the
thrown value coerced to a string (e.g. snippet = e instanceof Error ? e.message
: String(e)) so non-Error throws are surfaced and processing continues.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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/renderers/react/src/componentManifest/generator.test.ts (1)

12-12: Remove unnecessary non-null assertion operators.

The non-null assertion operators (!) on literal null values are contradictory and unnecessary. TypeScript allows null as a valid value without the assertion.

Apply this diff:

-vi.mock('tsconfig-paths', () => ({ loadConfig: () => ({ resultType: null!, message: null! }) }));
+vi.mock('tsconfig-paths', () => ({ loadConfig: () => ({ resultType: null, message: null }) }));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e4219c and 97c90e8.

📒 Files selected for processing (2)
  • code/core/src/types/modules/core-common.ts (1 hunks)
  • code/renderers/react/src/componentManifest/generator.test.ts (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/core/src/types/modules/core-common.ts
🧰 Additional context used
📓 Path-based instructions (7)
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/componentManifest/generator.test.ts
**/*.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/componentManifest/generator.test.ts
**/*.{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 (use yarn lint:js:cmd <file>)

Files:

  • code/renderers/react/src/componentManifest/generator.test.ts
**/*.@(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 using vi.mock() (e.g., filesystem, loggers)

Files:

  • code/renderers/react/src/componentManifest/generator.test.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/renderers/react/src/componentManifest/generator.test.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/renderers/react/src/componentManifest/generator.test.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/renderers/react/src/componentManifest/generator.test.ts
🧠 Learnings (5)
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mocked() to access and implement mock behaviors

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.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 **/*.@(test|spec).{ts,tsx,js,jsx} : Mock external dependencies in tests using `vi.mock()` (e.g., filesystem, loggers)

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.ts
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mocked() to type and access mocked functions

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.ts
📚 Learning: 2025-09-17T08:11:47.197Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.197Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use type-safe mocking with vi.mocked()

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.ts
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests

Applied to files:

  • code/renderers/react/src/componentManifest/generator.test.ts
🧬 Code graph analysis (1)
code/renderers/react/src/componentManifest/generator.test.ts (1)
code/renderers/react/src/componentManifest/generator.ts (1)
  • componentManifestGenerator (20-131)
⏰ 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 (3)
code/renderers/react/src/componentManifest/generator.test.ts (3)

418-467: LGTM! Well-structured test helper.

The getManifestForStory helper effectively encapsulates the boilerplate for testing individual story scenarios, reducing duplication and improving test maintainability.


469-482: LGTM! Clean CSF3 test wrapper.

The withCSF3 helper provides consistent CSF3 boilerplate, making the test cases more readable and maintainable.


484-636: LGTM! Comprehensive error handling tests.

These three test cases directly address the PR objectives by validating that manifest generation errors are surfaced clearly with line numbers and code context. The tests cover important edge cases:

  1. Fallback behavior when component name is missing
  2. Re-export errors with clear messaging about unsupported patterns
  3. Unknown expression errors with helpful context

The error messages include both the error description and surrounding code context, making it easy for users to identify and debug issues.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

♻️ Duplicate comments (1)
code/renderers/react/src/componentManifest/generator.ts (1)

41-57: Don’t assert inside catch; coerce and continue (prevents aborting generation).

invariant(e instanceof Error) can throw and terminate manifest generation. Coerce unknown throws to string and include file/story context in the error message.

-          .map((storyName) => {
+          .map((storyName) => {
             try {
               return {
                 name: storyName,
                 snippet: recast.print(getCodeSnippet(csf, storyName, name)).code,
               };
             } catch (e) {
-              invariant(e instanceof Error);
+              const message = e instanceof Error ? e.message : String(e);
               return {
                 name: storyName,
-                error: {
-                  message: e.message,
-                },
+                error: { message: `Snippet generation failed for ${entry.importPath}#${storyName}: ${message}` },
               };
             }
           })
🧹 Nitpick comments (5)
code/renderers/react/src/componentManifest/generator.ts (5)

34-34: Use map, not flatMap, with async callbacks.

flatMap ignores the “flattening” benefit when the callback returns a Promise; it reads as a mistake.

-      singleEntryPerComponent.flatMap(async (entry): Promise<ReactComponentManifest> => {
+      singleEntryPerComponent.map(async (entry): Promise<ReactComponentManifest> => {

68-76: Avoid redundant fields; base already includes name/examples.

Keeps objects smaller and prevents accidental divergence.

         if (!entry.componentPath) {
           const message = `No component file found for the "${name}" component.`;
           return {
             ...base,
-            name,
-            examples,
             error: { message },
           };
         }

113-122: Same redundancy in final return; name/examples are already in base.

Remove duplicates; rely on base.

         return {
           ...base,
-          name,
           description: manifestDescription?.trim(),
           summary: tags.summary?.[0],
           import: tags.import?.[0],
           reactDocgen: docgen,
           jsDocTags: tags,
-          examples,
           error,
         };

14-14: Drop unused invariant import after fixes.

Prevents dead code warnings.

-import { groupBy, invariant } from './utils';
+import { groupBy } from './utils';

60-66: Use entry.componentPath with fallback to importPath for the path field.

The ComponentManifest.path should reference the component file, not the story file. In the test setup, componentPath: './src/stories/Button.tsx' and importPath: './src/stories/Button.stories.ts' are distinct. Currently, path is always set to importPath (story file), which doesn't align with the interface name semantically.

The suggested change path: entry.componentPath ?? importPath correctly prioritizes the component file when available while maintaining consistency in the error path (which spreads ...base). The test snapshot will need updating to reflect the new path value: './src/stories/Button.tsx'.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97c90e8 and 84a35c7.

📒 Files selected for processing (1)
  • code/renderers/react/src/componentManifest/generator.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 (use yarn lint:js:cmd <file>)

Files:

  • code/renderers/react/src/componentManifest/generator.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/renderers/react/src/componentManifest/generator.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/renderers/react/src/componentManifest/generator.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/renderers/react/src/componentManifest/generator.ts
🧬 Code graph analysis (1)
code/renderers/react/src/componentManifest/generator.ts (6)
code/renderers/react/src/componentManifest/utils.ts (2)
  • groupBy (2-12)
  • invariant (15-23)
code/core/src/csf-tools/CsfFile.ts (1)
  • loadCsf (1060-1064)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (1)
  • getCodeSnippet (6-211)
code/core/src/types/modules/core-common.ts (1)
  • ComponentManifest (348-358)
code/core/src/csf-tools/enrichCsf.ts (1)
  • extractDescription (158-178)
code/renderers/react/src/componentManifest/jsdocTags.ts (1)
  • extractJSDocInfo (5-21)
⏰ 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 (2)
code/renderers/react/src/componentManifest/generator.ts (2)

24-29: Double filtering by type/subtype — confirm intent.

If type === 'story' already guarantees story entries, the subtype filter may be redundant; if not, keep both. Just flagging for clarity.


37-37: Remove the suggested type guard - no issue exists.

The code already ensures csf._meta?.component is a string. In CsfFile.ts line 395, the component property is explicitly assigned via recast.print(p.value, {}).code, which converts the AST node to a string. The type StaticMeta.component is correctly defined as string | undefined (line 263), and the fallback handles the undefined case.

Likely an incorrect or invalid review comment.

Comment on lines +80 to +91
try {
componentFile = await readFile(path.join(process.cwd(), entry.componentPath!), 'utf-8');
} catch (e) {
invariant(e instanceof Error);
return {
...base,
name,
examples,
error: {
message: `Could not read the component file located at ${entry.componentPath}`,
},
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Same issue: remove invariant in catch; preserve original cause.

This can also abort the run. Include the fs error message for debugging.

-        } catch (e) {
-          invariant(e instanceof Error);
-          return {
-            ...base,
-            name,
-            examples,
-            error: {
-              message: `Could not read the component file located at ${entry.componentPath}`,
-            },
-          };
-        }
+        } catch (e) {
+          const message = e instanceof Error ? e.message : String(e);
+          return {
+            ...base,
+            error: {
+              message: `Could not read the component file at ${entry.componentPath}: ${message}`,
+            },
+          };
+        }
🤖 Prompt for AI Agents
In code/renderers/react/src/componentManifest/generator.ts around lines 80 to
91, the catch block currently calls invariant(e instanceof Error) which can
abort the run and discards the original fs error; change the catch to not use
invariant, instead capture the thrown value safely (e.g. derive a message via e
instanceof Error ? e.message : String(e)) and include that message (and
optionally stack) in the returned error object so the original filesystem error
is preserved for debugging while still returning the manifest error.

Comment on lines +100 to +104
const error = !docgen
? {
message: `Could not parse props information for the located at ${entry.componentPath}`,
}
: undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix typo and improve clarity of docgen error.

Minor grammar; also clarify the target file.

-          ? {
-              message: `Could not parse props information for the located at ${entry.componentPath}`,
-            }
+          ? {
+              message: `Could not parse props information for the component file located at ${entry.componentPath}`,
+            }
           : undefined;
📝 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.

Suggested change
const error = !docgen
? {
message: `Could not parse props information for the located at ${entry.componentPath}`,
}
: undefined;
const error = !docgen
? {
message: `Could not parse props information for the component file located at ${entry.componentPath}`,
}
: undefined;
🤖 Prompt for AI Agents
In code/renderers/react/src/componentManifest/generator.ts around lines 100 to
104, the error message contains a typo ("the located at") and is unclear about
the target; update the message to correct grammar and explicitly reference the
component/file path (e.g. "Could not parse props information for the component
located at ${entry.componentPath}." or "for the file located at
${entry.filePath}."), leaving the rest of the logic unchanged.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (3)
code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx (1)

13-15: Use public CsfFile getter instead of private _meta.

Replace csf._meta?.component with csf.meta?.component to avoid coupling to internals.

-  .map((name) => getCodeSnippet(csf, name, csf._meta?.component ?? 'ComponentTitle'))
+  .map((name) => getCodeSnippet(csf, name, csf.meta?.component ?? 'ComponentTitle'))
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (2)

14-17: Avoid throwing a string; always throw an Error/SyntaxError.

Ensure consistent error objects when story is missing.

-  if (!storyDeclaration) {
-    const message = 'Expected story to be a function or variable declaration';
-    throw csf._storyPaths[storyName]?.buildCodeFrameError(message) ?? message;
-  }
+  if (!storyDeclaration) {
+    const message = 'Expected story to be a function or variable declaration';
+    const err = csf._storyPaths[storyName]?.buildCodeFrameError(message);
+    throw err ?? new SyntaxError(message);
+  }

208-213: Validate componentName for a safe JSX identifier.

Guard against invalid identifiers (e.g., names with dots/spaces) to avoid generating invalid JSX. Fallback to a safe default.

-  const name = t.jsxIdentifier(componentName);
+  const safeName = t.isValidIdentifier(componentName) ? componentName : 'Component';
+  const name = t.jsxIdentifier(safeName);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84a35c7 and 74728f2.

📒 Files selected for processing (2)
  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx (7 hunks)
  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts (6 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 (use yarn lint:js:cmd <file>)

Files:

  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
**/*.{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/renderers/react/src/componentManifest/generateCodeSnippet.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
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/renderers/react/src/componentManifest/generateCodeSnippet.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/renderers/react/src/componentManifest/generateCodeSnippet.ts
  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
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/componentManifest/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/componentManifest/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 using vi.mock() (e.g., filesystem, loggers)

Files:

  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
🧠 Learnings (1)
📚 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 **/*.{ts,tsx,js,jsx,mjs} : Export functions from modules when they need to be unit-tested

Applied to files:

  • code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx
🧬 Code graph analysis (2)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (2)
code/core/src/csf-tools/CsfFile.ts (2)
  • CsfFile (281-1042)
  • meta (964-966)
code/renderers/react/src/componentManifest/utils.ts (1)
  • invariant (15-23)
code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx (1)
code/renderers/react/src/componentManifest/generateCodeSnippet.ts (1)
  • getCodeSnippet (6-225)
⏰ 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 (7)
code/renderers/react/src/componentManifest/generateCodeSnippet.test.tsx (6)

79-87: LGTM: error is surfaced with codeframe.

Good assertion on the thrown SyntaxError with location context.


100-107: LGTM: CSF4 zero‑arg factory support.

Covers meta.story() and expected fallback rendering.


219-239: LGTM: meta‑level render precedence.

Validates merging and inlining from meta.render + story args.


284-288: LGTM: block‑body transform snapshot.

Confirms JSX transform inside function block preserves structure.


543-548: LGTM: multiple spreads scenario.

Ordering and de‑duplication look correct.


550-569: LGTM: top‑level export functions.

Keeps function form and correctly inlines args.

code/renderers/react/src/componentManifest/generateCodeSnippet.ts (1)

116-120: Review comment is incorrect—BabelFile already has the correct path property.

The local BabelFile interface in code/core/src/csf-tools/CsfFile.ts (lines 31–39) includes path: NodePath<t.Program>, which matches the expected type for pathForNode's first parameter. The call site at line 116 (pathForNode(csf._file.path, metaObj)) is correctly typed. No refactoring or type widening is required.

Likely an incorrect or invalid review comment.

Comment on lines +25 to +31
invariant(
init.isExpression(),
() =>
storyDeclaration.buildCodeFrameError('Expected story initializer to be an expression')
.message
);
storyPath = init;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Preserve SyntaxError + codeframe by throwing the Babel error directly (not via invariant).

Using invariant(...messageFromBuildCodeFrameError...) downgrades to Error and can drop SyntaxError typing. Throw the codeframe error object directly to align with the PR goal of better error surfacing.

-    invariant(
-      init.isExpression(),
-      () =>
-        storyDeclaration.buildCodeFrameError('Expected story initializer to be an expression')
-          .message
-    );
+    if (!init.isExpression()) {
+      throw storyDeclaration.buildCodeFrameError('Expected story initializer to be an expression');
+    }
...
-        invariant(
-          args.length === 1,
-          () => storyPath.buildCodeFrameError('Could not evaluate story expression').message
-        );
+        if (args.length !== 1) {
+          throw storyPath.buildCodeFrameError('Could not evaluate story expression');
+        }
         const storyArg = args[0];
-        invariant(
-          storyArg.isExpression(),
-          () => storyPath.buildCodeFrameError('Could not evaluate story expression').message
-        );
+        if (!storyArg.isExpression()) {
+          throw storyPath.buildCodeFrameError('Could not evaluate story expression');
+        }

Also applies to: 65-75

🤖 Prompt for AI Agents
In code/renderers/react/src/componentManifest/generateCodeSnippet.ts around
lines 25-31 (and similarly 65-75), replace the invariant(...) pattern that
passes storyDeclaration.buildCodeFrameError(...).message with directly throwing
the Babel/codeframe Error object returned by
storyDeclaration.buildCodeFrameError(...). Specifically, when init is not an
expression (and the other similar check), call const err =
storyDeclaration.buildCodeFrameError('...'); throw err; so the original
SyntaxError/codeframe type and stack are preserved instead of converting to a
plain Error via invariant.

JReinhold added a commit to storybookjs/mcp that referenced this pull request Oct 30, 2025
* add GET handler to /mcp path

* merge changelogs

* update manifest type to match upcoming manifest changes in storybookjs/storybook#32855

* add changesets
@kasperpeulen kasperpeulen merged commit b390162 into 10.1-with-canary-release Oct 30, 2025
55 of 56 checks passed
@kasperpeulen kasperpeulen deleted the kasper/error-manifest branch October 30, 2025 12:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants