Skip to content

Docgen: Wire up react-component-meta with DocgenService#34963

Draft
JReinhold wants to merge 10 commits into
claude/wizardly-bartik-cc2705from
claude/docgen-phase-3-rcm-provider
Draft

Docgen: Wire up react-component-meta with DocgenService#34963
JReinhold wants to merge 10 commits into
claude/wizardly-bartik-cc2705from
claude/docgen-phase-3-rcm-provider

Conversation

@JReinhold
Copy link
Copy Markdown
Contributor

@JReinhold JReinhold commented May 28, 2026

What I did

Combined phases 2 and 3 of the docgen-service rollout. Phase 2 was small enough on its own (one method + three tests) that splitting it from phase 3 didn't add value. Telescopes on top of #34954; merge after that lands. Phase-2's standalone PR (#34956) was closed in favor of this one.

Phase 2 — per-component RCM extraction API

Adds ComponentMetaManager.extractDocForComponent(storyRefs) — a thin wrapper around the existing batchExtract that returns the first resolved ComponentDoc. Keeping the per-component path on the manager means successive calls share the file-snapshot cache, TS program, and watcher.

Three tests prove the docgen-service-facing surface: single-component subset extraction, fsFileSnapshots reuse across separate per-component calls, and fresh docs after an onFilesChanged-driven invalidation between calls.

Phase 3 — real RCM-backed React provider + tightened schema

Replaces the phase-1 mock React provider with a real RCM-backed implementation. The provider:

  • Uses the new DocgenProviderPreset type (nextDocgen non-nullable, no ?. noise)
  • Receives { importPath } from the docgen service
  • Bails to nextDocgen for non-CSF paths (.mdx attached-docs) and when TypeScript isn't available
  • Reads + parses CSF, resolves the component reference + declared subcomponents via getComponents, batch-extracts ComponentDocs through a module-scoped ComponentMetaManager singleton so the file-snapshot cache stays hot across calls
  • Maps the result into a DocgenPayload with name, description, props, jsDocTags, subcomponents, and per-story snippets
  • Merges with downstream via the documented { ...downstream, ...ours } spread idiom so any unknown future fields downstream sets survive intact
  • Falls through to nextDocgen (rather than returning empty data) when extraction fails

Schemas tighten alongside. DocgenPayload now expresses:

  • Real DocgenProp (mirrors RCM's PropItemname, required, type, defaultValue, description, optional jsDocTags)
  • subcomponents: Record<string, DocgenSubcomponent> walked from CSF meta.subcomponents
  • stories: DocgenStory[] with per-story snippets and inline error fields
  • Component-level jsDocTags, summary, and optional error

New types (DocgenProp, DocgenStory, DocgenSubcomponent, DocgenError, DocgenJsDocTags) are re-exported through storybook/internal/types for downstream provider authors.

addon-docs provider is updated to the tightened schema: the synthetic { source, kind } marker-prop from phase 1 is dropped (doesn't match the real DocgenProp shape), so it's now a pure description enricher that spreads downstream and appends (docs enabled).

Telescoping plan

  1. #34954 — phase 1: DocgenService scaffolding + experimental_docgenProvider preset + experimentalDocgenServer feature flag + mock React provider
  2. This PR — phases 2 & 3 combined: per-component RCM extraction + real RCM-backed React provider + tightened payload schema

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

Three new integration-style tests in code/renderers/react/src/docgen/buildDocgen.test.ts drive real story files through buildDocgenPayload: component-level extraction (name/description/props/snippet), undefined return when the file is missing, and subcomponent extraction.

Three new tests in ComponentMetaManager.test.ts cover the per-component extraction API: single-subset extraction, cache reuse across calls, and source-edit invalidation.

Manual testing

  1. With experimentalDocgenServer: true already enabled in code/.storybook/main.ts (from Docgen: Add mock open service #34954), run cd code && yarn storybook:ui:build
  2. Confirm code/storybook-static/services/core/docgen/<componentId>.json files contain real name, description, props array, and stories array with snippets — not the mock placeholder strings from phase 1
  3. Confirm description fields end with (docs enabled) (proving the addon-docs enricher ran on top of the React provider)

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
  • Make sure this PR contains one of the labels below: `bug`, `maintenance`, `build`, `cleanup`, `feature request`, etc.

🦋 Canary release

This PR does not have a canary release associated.

🤖 Generated with Claude Code

JReinhold and others added 4 commits May 28, 2026 16:12
Adds ComponentMetaManager.extractDocForComponent(storyRefs) — a thin
wrapper around batchExtract that returns the first resolved ComponentDoc.
The docgen open service will call this once per componentId in phase 3;
keeping the helper on ComponentMetaManager means successive calls share
the file-snapshot cache, TS program, and watcher already maintained
there.

Three new tests cover the surface that matters for the docgen use case:
returning a doc for a single-component subset, sharing fsFileSnapshots
across separate per-component calls, and producing fresh docs after an
onFilesChanged-driven invalidation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the mock React provider with a real RCM-backed implementation.
The provider receives an IndexEntry[] for one componentId, picks the
authoritative entry (story over docs), parses the CSF file, resolves
the component reference plus any declared subcomponents via the existing
getComponents flow, batch-extracts ComponentDoc data through the
process-wide ComponentMetaManager singleton, and maps the result into a
DocgenPayload — including props, JSDoc tags, subcomponent docs, and
per-story snippets with errors surfaced inline.

Schemas tighten alongside: DocgenPayload now expresses real props,
subcomponents, stories[], jsDocTags, and an optional error field. The
new types are re-exported through storybook/internal/types so renderers
and addon authors can build conforming providers.

The addon-docs provider follows the new shape — it still appends a
'(docs enabled)' marker to description but now forwards subcomponents,
stories, and jsDocTags from downstream unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 28, 2026

Fails
🚫

PR is not labeled with one of: ["cleanup","BREAKING CHANGE","feature request","bug","documentation","maintenance","build","dependencies"]

🚫

PR is not labeled with one of: ["ci:normal","ci:merged","ci:daily","ci:docs"]

Warnings
⚠️

This PR targets claude/wizardly-bartik-cc2705. The default branch for contributions is next. Please make sure you are targeting the correct branch.

Generated by 🚫 dangerJS against 88897cf

…-3-rcm-provider

# Conflicts:
#	code/addons/docs/src/docgen.ts
#	code/core/src/shared/open-service/services/docgen/definition.ts
#	code/core/src/shared/open-service/services/docgen/server.test.ts
#	code/renderers/react/src/docgen/preset.ts
@JReinhold JReinhold changed the title react: real RCM-backed docgen provider (docgen phase 3) react: per-component RCM extraction + RCM-backed docgen provider (docgen phases 2 & 3) May 29, 2026
@JReinhold JReinhold changed the base branch from claude/docgen-phase-2-rcm-per-component to claude/wizardly-bartik-cc2705 May 29, 2026 09:03
JReinhold added 2 commits May 29, 2026 11:29
…-3-rcm-provider

# Conflicts:
#	code/addons/docs/src/docgen.ts
#	code/renderers/react/src/docgen/preset.ts
…-3-rcm-provider

# Conflicts:
#	code/core/src/types/modules/core-common.ts
#	code/renderers/react/src/docgen/preset.ts
@JReinhold JReinhold changed the title react: per-component RCM extraction + RCM-backed docgen provider (docgen phases 2 & 3) Docgen: Wire up react-component-meta with DocgenService May 29, 2026
@JReinhold JReinhold mentioned this pull request May 29, 2026
8 tasks
JReinhold and others added 3 commits June 1, 2026 15:00
Two phase-2/3 follow-ups from review:

1. Pre-boot the TypeScript ComponentMetaManager. The first docgen request
   was paying 4-5s to `import('typescript')` (a multi-MB module) on the
   critical path. The React provider now kicks getManager() off
   fire-and-forget at chain-build time, so the import warms up in parallel
   with the rest of server startup and `await getManager()` in the request
   path usually resolves instantly.

2. Loosen the docgen payload schema. The previous `DocgenProp` shape was a
   verbatim copy of react-docgen-typescript's PropItem, baking one engine's
   structure into the core service contract — but the docgen service is
   meant to interface with other integrations (vue-docgen, future
   extractors) whose props look nothing like RCM's. Following the pattern
   in Storybook MCP's manifest types (which keep `reactComponentMeta` etc.
   as `any`), `props` is now `unknown[]` in both the payload and
   subcomponents, and the `DocgenProp` type is removed from the core
   contract. The React renderer keeps a local `ReactDocgenProp` type for
   its own emitted shape; consumers branch on the producing integration
   when they need concrete prop types. Identity and human-readable fields
   (componentId, name, description, summary, jsDocTags, stories) stay
   strictly typed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
extractDocForComponent was added in phase 2 as a speculative per-component
entry point, but the actual consumer (buildDocgenPayload) never used it.
buildDocgen needs the ComponentDoc for the primary component AND each
subcomponent — it calls batchExtract (which mutates every StoryRef in
place) and reads .reactComponentMeta off each ref. extractDocForComponent
returns only the first doc, so it never fit that need.

batchExtract (which predates phase 2) is the real API. Its cache-sharing
and invalidation behavior is already covered by the existing project-level
ComponentMetaManager tests and end-to-end by buildDocgen.test.ts, so the
three extractDocForComponent tests are removed alongside the method. The
now-orphaned ComponentDoc import is dropped too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The manifest generator and the docgen provider both ran the same pipeline
to turn one CSF story file into resolved components (read CSF → getComponents
→ match primary + subcomponents) and to extract per-story snippets. The
docgen provider was a parallel re-implementation that even reached into
generator.ts for findMatchingComponent.

Extract that shared work into componentManifest/resolveComponents.ts:
- resolveStoryFileComponents(): one CSF file → ResolvedStoryFile (the unit
  the manifest maps over per component, and the docgen provider runs once).
- findMatchingComponent(): moved here from generator.ts.
- extractStorySnippets(): unifies the near-identical extractStories /
  buildStories; an optional filter set models the manifest's tag filtering.

Both callers now resolve via the shared module and keep only their
output-specific shaping. batchExtract intentionally stays with each caller:
the manifest batches across all files in one call (its main perf win) while
the docgen provider batches its single file — folding extraction into the
shared function would have regressed the manifest's program-build batching.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant