Skip to content

Addon-Docs: Resolve CSF4 module exports without a default export#34834

Merged
kasperpeulen merged 2 commits into
storybookjs:nextfrom
TheSeydiCharyyev:fix/issue-34159-csf4-meta-split-chunk
May 22, 2026
Merged

Addon-Docs: Resolve CSF4 module exports without a default export#34834
kasperpeulen merged 2 commits into
storybookjs:nextfrom
TheSeydiCharyyev:fix/issue-34159-csf4-meta-split-chunk

Conversation

@TheSeydiCharyyev
Copy link
Copy Markdown
Contributor

@TheSeydiCharyyev TheSeydiCharyyev commented May 19, 2026

Closes #34159
Closes #34373

What I did

DocsContext.resolveModuleExport looks up CSF files by object identity, with a fallback through the default export to handle bundlers like Rolldown that don't preserve namespace identity. CSF4 stories without export default meta defeat that fallback: there is no default key to match on, so a chunk-split namespace from MDX never resolves.

This adds a second fallback that iterates the namespace's exported values, looking them up via the existing exportToStory map. If every recognized export points to the same CSF file, that file wins. If they span multiple CSF files, the lookup is rejected so we don't silently pick a wrong file. An isStory guard skips this loop for individual CSF4 Story objects so <Canvas of={Stories.Primary} /> keeps resolving to a story.

referenceCSFFile also guards its default registration — for CSF4 modules without default, the previous code was registering undefined in the map.

Manual testing

The fix can be verified against the reporter's repro (https://github.com/yuheiy/storybook-mdx-meta-without-default-export-bug):

  1. npm install
  2. npm run build-storybook
  3. npx http-server storybook-static
  4. Open http://localhost:8080/iframe.html?viewMode=docs&id=button--docs

Before/after screenshots from this reproduction are attached below.

before-fix after-fix

What I changed

  • code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.ts
    • referenceCSFFile: guard the default export registration so CSF4 modules don't pollute the map with undefined
    • resolveModuleExport: add a story-export fallback (with isStory guard, and reject ambiguous multi-CSF matches)
  • code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.test.ts
    • referenceMeta: new test for CSF4 modules without default + different namespace identity
    • referenceMeta: new test that rejects namespaces whose story exports span multiple CSF files
    • resolveOf: new test for CSF4 namespace resolution
    • resolveOf: new regression test that a CSF4 Story object still resolves to its story
  • code/core/src/preview-api/modules/preview-web/docs-context/test-utils.ts
    • csfFileParts: optional includeDefaultExport to simulate CSF4 modules

Checklist

  • Tests added — 4 new cases in DocsContext.test.ts (37 total pass)
  • Type checks pass (yarn nx check core)
  • No new lint warnings
  • Verified end-to-end against the reporter's reproduction repository

Related

Picks up the approach from the previous attempt #34375, which was closed because the author did not provide before/after verification. The technique is the same (story-export fallback); this diff is focused (no unrelated reformatting) and ships with the requested verification.

Summary by CodeRabbit

  • Tests

    • Expanded test coverage for CSF4 module handling without default exports, including namespace resolution scenarios
    • Added test assertions for story object identity and CSF file resolution
  • Chores

    • Improved module export resolution logic for edge cases
    • Enhanced test utilities with configurable module export composition support

Review Change Stack

When a bundler splits a CSF4 story module (no `export default meta`)
into a separate chunk, the namespace object passed to `<Meta of={...} />`
differs by object identity from the one Storybook registered. The
existing default-export fallback in `DocsContext.resolveModuleExport`
cannot handle this case because CSF4 modules have no `default` key.

Fall back to identifying the CSF file via any of its story exports.
Reject the lookup when story exports span multiple CSF files. Guard
against individual CSF4 Story objects so `<Canvas of={Stories.Primary} />`
keeps resolving to a story.

Fixes storybookjs#34159
Fixes storybookjs#34373
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a88ca2c1-7db7-4397-bf98-36b4bdade5f2

📥 Commits

Reviewing files that changed from the base of the PR and between 3ae861f and a328ec0.

📒 Files selected for processing (3)
  • code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.test.ts
  • code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.ts
  • code/core/src/preview-api/modules/preview-web/docs-context/test-utils.ts

📝 Walkthrough

Walkthrough

DocsContext resolution now handles CSF4 modules without default exports. The test utility is enhanced to support optional default exports, the core logic conditionally guards default registration and adds fallback resolution scanning module values to their CSF files, and test coverage validates both non-default module resolution and Story object identity handling.

Changes

CSF4 modules without default exports in DocsContext

Layer / File(s) Summary
Test utility for optional default exports
code/core/src/preview-api/modules/preview-web/docs-context/test-utils.ts
csfFileParts accepts an includeDefaultExport option (default true) to control whether the returned moduleExports object includes the default export, enabling creation of test fixtures matching CSF4 modules with only story exports.
DocsContext conditional default and fallback resolution
code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.ts
referenceCSFFile now only registers the default export when moduleExports.default exists. resolveModuleExport gains a fallback path that scans object values, maps story-like exports to their CSF files via internal mappings, and returns a CSF file match only if all values converge on the same file; otherwise clears the match to allow subsequent story/component inference.
Test coverage for non-default and Story object resolution
code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.test.ts
New referenceMeta test cases validate successful resolution of modules without default exports and error handling for exports spanning multiple CSF files. New resolveOf assertions verify fallback resolution of CSF4 namespace objects without defaults and correct handling of CSF4 Story objects (_tag === 'Story') that resolve to the story itself rather than the containing CSF file.

Possibly related PRs

  • storybookjs/storybook#33280: Both PRs modify DocsContext.resolveModuleExport to add fallback logic for bundler-style module namespace objects and extend test coverage for resolveOf/referenceMeta behavior.

🎯 3 (Moderate) | ⏱️ ~20 minutes


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Member

@kasperpeulen kasperpeulen left a comment

Choose a reason for hiding this comment

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

Reviewed the DocsContext fallback for CSF4 modules without a default export. The change is narrowly scoped, rejects ambiguous multi-CSF namespace matches, preserves individual Story-object resolution, and matches the linked repro/root cause. Local focused tests/typecheck passed and CI is green.

@kasperpeulen kasperpeulen merged commit f453d49 into storybookjs:next May 22, 2026
136 of 137 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

3 participants