Skip to content

Core: Add ref-based components manifest index backed by docgen open service#35063

Merged
JReinhold merged 5 commits into
nextfrom
jeppe-cursor/39213f22
Jun 5, 2026
Merged

Core: Add ref-based components manifest index backed by docgen open service#35063
JReinhold merged 5 commits into
nextfrom
jeppe-cursor/39213f22

Conversation

@JReinhold

@JReinhold JReinhold commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Closes #

What I did

Wire the experimental docgen open service into static and dev manifest generation when features.componentsManifest is enabled. With features.experimentalDocgenServer, React skips legacy components extraction and supplies meta.docgen: 'react-component-meta' via experimental_manifests; the docgen service holds full component payloads while manifests/components.json becomes a v1 ref-based index with inlined summary fields and nested docgen.$ref pointers into the service static store. Static build writes open-service artifacts before manifests, serves components.html from live docgen data, and keeps the docs manifest unchanged. Adds JSON reference utilities (RFC 6901 resolver, dereference helper), aligns docgen payloads with ComponentManifest, and extends open-service runtime for void queries and subscription ergonomics.

Agent prompt: migrate manifest consumers

Migrate a consumer from legacy inline components.json to the ref-based multi-file format (support both)
You are migrating a tool that reads Storybook's static `manifests/components.json`.

## Goal

Update the consumer to support **both** manifest formats and produce the same in-memory
`Record<componentId, ComponentManifest>` (full docgen payloads) regardless of which format
was emitted.

## Background — two formats

### Legacy inline format (`v: 0`)

- Single file: `<storybook-static>/manifests/components.json`
- `components[componentId]` contains the **full** component docgen inline:
  `id`, `name`, `path`, `stories`, `jsDocTags`, optional `subcomponents`, engine fields
  (e.g. `reactComponentMeta`), etc.
- Emitted when `features.experimentalDocgenServer` is **off** (or legacy manifest path).

### Ref-based multi-file format (`v: 1`)

- Index file: `<storybook-static>/manifests/components.json`
- `components[componentId]` is a **summary row** only:
  `id`, `name`, optional `description`, optional `summary`, and optional nested
  `docgen: { "$ref": "<relative-path>#<json-pointer>" }`.
- Full docgen lives in separate snapshot files under `<storybook-static>/services/`:
  - Per-component snapshot: `services/core/docgen/<componentId>.json`
  - Payload at JSON Pointer `/components/<componentId>` inside that file
  - Example `$ref` from the index (relative to `manifests/`):
    `../services/core/docgen/button.json#/components/button`
- Emitted on **static build** when `features.experimentalDocgenServer` is **on**.
- In **dev**, `manifests/components.json` is not served (404); only the static build produces
  the v1 index. `manifests/components.html` is still available in dev.

## Detection

1. Read `manifests/components.json`.
2. If `manifest.v === 1` → ref-based index (resolve `docgen.$ref` per component).
3. If `manifest.v === 0` (or `v` absent and rows look like full manifests with `path` +
   `stories`) → legacy inline; use rows directly.

## Resolution algorithm (v: 1)

For each `componentId` in `manifest.components`:

1. Start from the index row (`id`, `name`, `description?`, `summary?`).
2. If `row.docgen?.$ref` is present:
   - Parse `filePath` and `pointer` by splitting on the first `#`.
   - Resolve `filePath` relative to the `manifests/` directory (where `components.json` lives).
   - Read and parse that JSON file.
   - Follow the JSON Pointer (`/components/<componentId>`) to get the full docgen payload.
   - Merge: full payload is authoritative; index summary fields are already duplicated on
     the payload when present.
3. If `row.docgen` is absent (snapshot missing) → fall back to the index row only (partial
   data; no `path`/`stories`/engine fields).

Collect results into `Record<string, ComponentManifest>` keyed by component id.

## Requirements

- Support **both** v0 and v1 without breaking existing v0 consumers.
- Do not assume `components.json` is self-contained when `v === 1`.
- Resolve `$ref` paths relative to `manifests/`, not the repo root.
- Handle missing snapshot files gracefully (keep partial index row, do not throw).
- Keep the public output shape stable: callers should still receive full component objects
  with `path`, `stories`, `jsDocTags`, and engine-specific fields when available.
- Add tests for both formats using fixture files (legacy inline JSON + v1 index + snapshot).

## Files to inspect in this repo (reference)

- `code/core/src/core-server/utils/manifests/components-ref-manifest.ts` — index shape
- `code/core/src/shared/open-service/services/docgen/paths.ts` — `$ref` path conventions
- `code/core/src/types/modules/core-common.ts` — `ComponentManifest` / `ComponentsManifest`

## Deliverables

1. A small loader (or update to the existing one) with `loadComponentsManifest(staticDir)`.
2. Tests with fixtures for v0 and v1.
3. Brief comment documenting which Storybook feature flags produce which format.

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

Caution

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

Use the internal Storybook in code/ (code/.storybook). Compare behavior with features.experimentalDocgenServer toggled on vs off.

With experimentalDocgenServer: true (current default in internal Storybook):

  1. cd code && yarn storybook:ui
  2. Open http://localhost:6006/manifests/components.html — expect the components debugger rendered from the docgen open service.
  3. Open http://localhost:6006/manifests/components.json — expect 404 in dev (ref index is build-only).
  4. cd code && yarn storybook:ui:build
  5. Inspect code/storybook-static/manifests/components.json — expect v: 1 ref-based index rows with nested docgen.$ref pointers.
  6. Open code/storybook-static/manifests/components.html in a browser — expect the same docgen-backed debugger as dev.

With experimentalDocgenServer: false:

  1. Repeat the dev and build steps above.
  2. In dev, http://localhost:6006/manifests/components.json should return the legacy inline components manifest from experimental_manifests.
  3. After build, storybook-static/manifests/components.json should contain the legacy inline manifest (v: 0, full component payloads inlined).
  4. components.html should still render from the inline manifest in both dev and build.

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

  • Declare whether manual QA will be needed for this PR during the release, through qa:needed or qa:skip

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the @storybookjs/core team here.

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

Summary by CodeRabbit

  • New Features

    • Experimental docgen server support for enriched component documentation in dev and static builds
    • Ref-based components.json manifests with structured docgen data including stories, subcomponents, and JSDoc tags
    • Improved ergonomics for zero-argument query calls when input schema is void
  • Documentation

    • Added filesystem testing guidance for mocking node:fs interactions

…vice.

Writes v1 manifest rows with summary fields and nested docgen.$ref targets,
runs open-service static builds before manifests, gates manifest I/O on
componentsManifest, and aligns docgen payloads with the React manifest shape.

Co-authored-by: Cursor <cursoragent@cursor.com>
@JReinhold JReinhold added maintenance User-facing maintenance tasks core ci:normal Run our default set of CI jobs (choose this for most PRs). qa:skip Pull Requests that do not need any QA. labels Jun 5, 2026
@JReinhold JReinhold self-assigned this Jun 5, 2026
@JReinhold

Copy link
Copy Markdown
Contributor Author

Manual QA report

Executed the manual testing steps from the PR description against the internal Storybook (code/.storybook) with features.componentsManifest: true toggled for QA.

Environment

  • compile: OK (yarn nx compile core; code/core/dist/bin/dispatcher.js present)
  • main.ts: Temporarily added componentsManifest: true and toggled experimentalDocgenServer; restored to original after QA
  • dev servers: Stopped (nothing listening on :6006)

Pass A — experimentalDocgenServer: true

Check Result Evidence
Dev server starts PASS http://localhost:6006/ → 200
GET /manifests/components.html PASS HTTP 200, ~2.6MB; Components Manifest debugger UI
GET /manifests/components.json (dev) PASS HTTP 404 (expected with docgen server)
Static build PASS yarn storybook:ui:build succeeded
Built components.json PASS v: 1; ref index with nested docgen.$ref pointers (~275 refs); not full inlined payloads
Built components.html PASS Exists, substantive HTML

Pass B — experimentalDocgenServer: false

Check Result Evidence
Dev server starts PASS http://localhost:6006/ → 200
GET /manifests/components.json (dev) PASS HTTP 200; v: 0; inline objects (path, stories, …); no ref index
GET /manifests/components.html (dev) PASS HTTP 200; inline manifest rendering
Static build PASS yarn storybook:ui:build succeeded
Built components.json PASS v: 0; full inline component objects
Built components.html PASS Substantive HTML; legacy inline rendering

Overall: PASS

Notes:

  • Pass A dev HTML validation relied on debugger UI markup plus dev JSON 404 and static v: 1 ref index (page does not expose raw v: 1).
  • Pass B build overwrote Pass A static artifacts (sequential QA runs).
  • Legacy manifest path works with docgen server disabled.

input.id in ctx.self.state.components ? ctx.self.state.components[input.id] : undefined,
staticPath: (input) => docgenQueryStaticPath(input.id),
},
getDocgenForAllComponents: {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is used only by the manifest debugger, as it needs docgen for all components to be able to render the debugger. is not used in any other hot paths.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

the changes here are to make the docgen service format conform 100 % with the existing manifest format.

JReinhold and others added 2 commits June 5, 2026 14:53
Remove the unused JSON-Ref resolver/dereferencer (no in-repo consumer), simplify the
snapshot loader to a direct typed read, and rename json-references.ts to
components-ref-manifest.ts. Dedup the per-operation story-index walk in manifests.ts by
threading manifest entries through, and inline the trivial fs wrappers.

Co-authored-by: Cursor <cursoragent@cursor.com>
Read docgen payloads once from the static service snapshots and reuse them for
both the ref-based components.json index and the HTML debugger. Dev still
renders from the live docgen service.

Co-authored-by: Cursor <cursoragent@cursor.com>
@storybook-app-bot

storybook-app-bot Bot commented Jun 5, 2026

Copy link
Copy Markdown

Package Benchmarks

Commit: 903f5d8, ran on 5 June 2026 at 18:52:43 UTC

No significant changes detected, all good. 👏

Apply oxfmt fixes and align ReactComponentManifest with DocgenPayload via
index signatures. Narrow reactComponentMeta in buildDocgen tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
@JReinhold

Copy link
Copy Markdown
Contributor Author

Manual QA report (re-run after build HTML / CI fixes)

Re-executed the PR manual testing steps after 2e90f3f035b (build HTML from snapshots + format/type fixes).

Environment

  • compile: OK (yarn nx compile core)
  • main.ts: Temporarily added features.componentsManifest: true; restored after QA
  • dev servers: Stopped (port 6006 free)

Pass A — experimentalDocgenServer: true

Check Result Evidence
Dev server PASS localhost:6006 ready
GET /manifests/components.html PASS HTTP 200, ~2.6MB; Components Manifest debugger UI
GET /manifests/components.json (dev) PASS HTTP 404 (expected)
Static build PASS yarn storybook:ui:build succeeded
Built components.json PASS v: 1; 275 components; nested docgen.$ref index
Built components.html PASS ~2.6MB; substantive debugger HTML

Pass B — experimentalDocgenServer: false

Check Result Evidence
Dev components.json PASS HTTP 200; v: 0; inline legacy manifest; no docgen.$ref
Dev components.html PASS HTTP 200; ~2.8MB
Built components.json PASS v: 0; full inline payloads
Built components.html PASS Legacy inline rendering

Build HTML regression check

Check Result Notes
Pass A dev vs build HTML size PASS 2,615,921 → 2,618,387 bytes (+0.09%)
Pass B dev vs build HTML size PASS Stable (~2.8MB)
Build log docgen re-extraction PASS No docgen extract/re-extract noise in build output

Overall: PASS

Legacy manifest path still works with docgen server off. Pass B build overwrote Pass A static artifacts (sequential QA runs).

@JReinhold JReinhold marked this pull request as ready for review June 5, 2026 18:09
@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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: f7c34626-70f4-4fae-8567-ab473153c365

📥 Commits

Reviewing files that changed from the base of the PR and between 2e90f3f and 903f5d8.

📒 Files selected for processing (2)
  • code/core/src/shared/open-service/services/docgen/server.test.ts
  • code/core/src/types/modules/core-common.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • code/core/src/types/modules/core-common.ts
  • code/core/src/shared/open-service/services/docgen/server.test.ts

📝 Walkthrough

Walkthrough

This PR refactors the docgen service and manifest generation pipeline to support id-based component selection, ref-based manifest outputs, and experimental docgen-server flows. Changes include void-input query ergonomics, updated docgen contracts, React extraction modules, ref-based manifest artifacts, and updated manifest routing with comprehensive test coverage.

Changes

Docgen, React extraction, and manifests

Layer / File(s) Summary
Void-input query call semantics
code/core/src/shared/open-service/types.ts, code/core/src/shared/open-service/service-runtime.ts, code/core/src/shared/open-service/index.test-d.ts, code/core/src/shared/open-service/service-runtime.test.ts
Query types and runtime behavior are updated to allow zero-argument () and loaded() calls for queries with v.void() inputs, with conditional type overloads and test validation.
Docgen path helpers and type contracts
code/core/src/shared/open-service/services/docgen/paths.ts, code/core/src/shared/open-service/services/docgen/paths.test.ts, code/core/src/shared/open-service/services/docgen/definition.ts, code/core/src/shared/open-service/services/docgen/types.ts, code/core/src/server-errors.ts, code/core/src/types/modules/core-common.ts
New docgen path helpers (docgenQueryStaticPath, docgenStaticStorePath, docgenPayloadJsonPointer, docgenManifestRef) are added; docgen service definition and payload types are expanded with id-based contracts, enriched story/error/JSDoc/subcomponent fields, and matching index-entry input shape; error class updated to use id instead of componentId.
Docgen service runtime behavior and tests
code/core/src/shared/open-service/services/docgen/server.ts, code/core/src/shared/open-service/services/docgen/server.test.ts
Service runtime uses selectComponentEntriesByComponentId for id-based entry resolution, adds getDocgenForAllComponents query, and updates state storage/error handling; comprehensive test suite validates command/query behavior, payload shapes, provider composition, and description merging across stacked providers.
React component docgen extraction and manifest building
code/renderers/react/src/componentManifest/buildReactComponentDocgen.ts, code/renderers/react/src/docgen/buildDocgen.ts, code/renderers/react/src/docgen/buildDocgen.test.ts
New modules provide React-specific docgen extraction (story snippet generation, description/summary parsing, subcomponent walking) and complete manifest assembly with engine-specific payload fields; integration with shared ComponentMetaManager and error handling when component imports are unavailable.
React component manifest generator orchestration
code/renderers/react/src/componentManifest/generator.ts
Generator refactored to delegate manifest building to shared modules, adds early return for experimentalDocgenServer feature flag, and wires concurrent story/component resolution and batch RCM extraction.
Ref-based components manifest model and helpers
code/core/src/core-server/utils/manifests/components-ref-manifest.ts, code/core/src/core-server/utils/manifests/components-ref-manifest.test.ts
New module defines versioned ref-based manifest types, implements disk-based payload loading, and generates compact index entries with nested $ref links to docgen snapshots; tests validate payload mapping, metadata propagation, and fallback to minimal entries when payloads are missing.
Manifest generation pipeline and dev routes
code/core/src/core-server/utils/manifests/manifests.ts
Manifest entry filtering and loading refactored to support precomputed filtered entries; experimental docgen-server write path conditionally emits ref-based components.json and optional HTML from on-disk/live docgen payloads; dev routes updated to load manifests via filtered entries and serve experimental docgen HTML when enabled.
Manifest tests, static build coordination, and test guidance
code/core/src/core-server/utils/manifests/manifests.test.ts, code/core/src/core-server/build-static.ts, AGENTS.md
Manifests tests updated to use memfs spy-wiring with explicit beforeEach setup; new test coverage for experimentalDocgenServer behavior and ref-based output; static build effect ordering ensures manifest write depends on open-service output; documentation added for memfs testing patterns.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • storybookjs/storybook#33974: Both PRs modify manifest-writing gating to use the componentsManifest feature flag around when to emit manifests.
  • storybookjs/storybook#32939: Both PRs touch the React component-manifest/docgen pipeline and manifest generation input sourcing.
  • storybookjs/storybook#34954: This PR builds on the docgen open-service scaffolding introduced in the retrieved PR, updating docgen contracts and manifest generation around the new docgen service.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@code/renderers/react/src/componentManifest/componentMetaManagerSingleton.ts`:
- Around line 19-27: The catch in getSharedComponentMetaManager() currently
masks all init failures as “TypeScript not available”; change it to detect a
genuine missing-module error (e.g., check error instanceof Error and inspect
error.code === 'MODULE_NOT_FOUND' or error.message includes "typescript") and
log the original Error details for non-missing-module failures (use logger.error
with error.stack/message) while still returning undefined in both cases so the
unawaited init and docgen fallback behavior remain unchanged; keep
ComponentMetaManager creation path and only treat the import failure as the
"missing TypeScript" debug log.

In `@code/renderers/react/src/docgen/buildDocgen.test.ts`:
- Around line 34-40: Tests currently create real temp dirs/files via
createTempDir/writeFiles/cleanup and helpers use node:fs functions; switch to an
in-memory fs by importing memfs (vol) and use vol.fromJSON(...) in a beforeEach
to populate fixtures and vol.reset() to clear state, update the afterEach to
call vol.reset() instead of cleanup and remove or adapt
createTempDir/writeFiles/cleanup usages so they write into memfs (or replace
them with a helper that constructs the same JSON map passed to vol.fromJSON),
and ensure tests import { vol } from 'memfs' and no longer rely on node:fs
helper side effects (references: afterEach, beforeEach, createTempDir,
writeFiles, cleanup, vol.fromJSON, vol.reset; also address helper functions that
call mkdirSync/writeFileSync/rmSync).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 581188ea-8831-4afb-b768-18cd20f844df

📥 Commits

Reviewing files that changed from the base of the PR and between 13ce265 and 2e90f3f.

📒 Files selected for processing (33)
  • AGENTS.md
  • code/.storybook/main.ts
  • code/addons/docs/src/docgen.ts
  • code/core/src/common/index.ts
  • code/core/src/common/utils/select-component-entry.test.ts
  • code/core/src/common/utils/select-component-entry.ts
  • code/core/src/core-server/build-static.ts
  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/core-server/utils/manifests/components-ref-manifest.test.ts
  • code/core/src/core-server/utils/manifests/components-ref-manifest.ts
  • code/core/src/core-server/utils/manifests/manifests.test.ts
  • code/core/src/core-server/utils/manifests/manifests.ts
  • code/core/src/server-errors.ts
  • code/core/src/shared/open-service/index.test-d.ts
  • code/core/src/shared/open-service/service-runtime.test.ts
  • code/core/src/shared/open-service/service-runtime.ts
  • code/core/src/shared/open-service/services/docgen/definition.ts
  • code/core/src/shared/open-service/services/docgen/paths.test.ts
  • code/core/src/shared/open-service/services/docgen/paths.ts
  • code/core/src/shared/open-service/services/docgen/server.test.ts
  • code/core/src/shared/open-service/services/docgen/server.ts
  • code/core/src/shared/open-service/services/docgen/types.ts
  • code/core/src/shared/open-service/types.ts
  • code/core/src/types/modules/core-common.ts
  • code/renderers/react/src/componentManifest/buildReactComponentDocgen.ts
  • code/renderers/react/src/componentManifest/componentMeta/componentMetaExtractor.test-helpers.ts
  • code/renderers/react/src/componentManifest/componentMetaManagerSingleton.ts
  • code/renderers/react/src/componentManifest/extractComponentDescription.ts
  • code/renderers/react/src/componentManifest/generator.ts
  • code/renderers/react/src/componentManifest/resolveComponents.ts
  • code/renderers/react/src/docgen/buildDocgen.test.ts
  • code/renderers/react/src/docgen/buildDocgen.ts
  • code/renderers/react/src/docgen/preset.ts
💤 Files with no reviewable changes (1)
  • code/addons/docs/src/docgen.ts

Comment on lines +19 to +27
try {
const ts = await import('typescript');
return new ComponentMetaManager(ts);
} catch {
logger.debug(
'[reactComponentMeta] TypeScript not available, skipping component meta extraction.'
);
return undefined;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="code/renderers/react/src/componentManifest/componentMetaManagerSingleton.ts"

echo "== File =="
test -f "$FILE" && echo "Found $FILE" || { echo "Missing $FILE"; exit 1; }

echo
echo "== Relevant section (lines 1-120) =="
nl -ba "$FILE" | sed -n '1,140p'
echo
echo "== Search for ComponentMetaManager instantiation and catch usage =="
rg -n "ComponentMetaManager|import\\('typescript'\\)|TypeScript not available|catch \\{" "$FILE"

Repository: storybookjs/storybook

Length of output: 258


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="code/renderers/react/src/componentManifest/componentMetaManagerSingleton.ts"

echo "== Relevant section (approx lines 1-140) =="
awk '{print NR ":" $0}' "$FILE" | sed -n '1,160p'

echo
echo "== Search within file =="
rg -n "ComponentMetaManager|import\\('typescript'\\)|TypeScript not available|catch \\{" "$FILE" || true

echo
echo "== Show surrounding try/catch =="
rg -n "try \\{|catch \\{" "$FILE" || true

Repository: storybookjs/storybook

Length of output: 2436


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the ComponentMetaManager implementation
FILE_MANAGER="code/renderers/react/src/componentManifest/componentMeta/ComponentMetaManager.ts"
test -f "$FILE_MANAGER" && echo "Found $FILE_MANAGER" || { echo "Missing $FILE_MANAGER"; exit 1; }

echo "== Constructor / init section (lines 1-220) =="
awk '{print NR ":" $0}' "$FILE_MANAGER" | sed -n '1,220p'

echo
echo "== Search for constructor throwing / TypeScript usage =="
rg -n "constructor\\(|new ComponentMetaManager|dispose\\(|import\\('typescript'\\)|typescript" "$FILE_MANAGER" || true

Repository: storybookjs/storybook

Length of output: 14442


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="code/renderers/react/src/componentManifest/componentMetaManagerSingleton.ts"
rg -n "getSharedComponentMetaManager\\(" -S code/renderers/react/src | head -n 50

echo
echo "== Show a couple call sites (with nearby code) =="
rg -n "getSharedComponentMetaManager\\(" -S code/renderers/react/src -n --context 4 | head -n 120

Repository: storybookjs/storybook

Length of output: 4230


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Search for similar optional typescript imports/catches =="
rg -n "import\\('typescript'\\)|TypeScript not available|ERR_MODULE_NOT_FOUND|Cannot find package 'typescript'" code | head -n 200

Repository: storybookjs/storybook

Length of output: 1066


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Files to inspect
FILES=(
  "code/renderers/react/src/componentManifest/reactDocgen/utils.ts"
  "code/renderers/react/src/componentManifest/reactDocgenTypescript.ts"
)

for f in "${FILES[@]}"; do
  echo
  echo "==== $f (first ~220 lines) ===="
  awk '{print NR ":" $0}' "$f" | sed -n '1,220p'
done

echo
echo "==== show try/catch blocks in utils.ts around import('typescript') ===="
rg -n "import\\('typescript'\\)" "code/renderers/react/src/componentManifest/reactDocgen/utils.ts"
rg -n "try \\{|catch \\{" "code/renderers/react/src/componentManifest/reactDocgen/utils.ts"

echo
echo "==== show relevant part of reactDocgenTypescript.ts ===="
awk '{print NR ":" $0}' "code/renderers/react/src/componentManifest/reactDocgenTypescript.ts" | sed -n '1,160p'

Repository: storybookjs/storybook

Length of output: 19775


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="code/renderers/react/src/docgen/preset.ts"
awk '{print NR ":" $0}' "$FILE" | sed -n '1,120p'

Repository: storybookjs/storybook

Length of output: 2910


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for docgen provider invocation and error handling
rg -n "experimental_docgenProvider|docgenProvider|nextDocgen\\(input\\)|getSharedComponentMetaManager\\(" code/renderers/react/src -S

rg -n "nextDocgen\\(|DocgenProvider|experimental_docgenProvider" code -S | head -n 200

Repository: storybookjs/storybook

Length of output: 7402


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="code/core/src/shared/open-service/services/docgen/server.ts"
test -f "$FILE" && echo "Found $FILE" || { echo "Missing $FILE"; exit 1; }

awk '{print NR ":" $0}' "$FILE" | sed -n '1,260p'

echo
echo "== Search for try/catch around provider execution =="
rg -n "try \\{|catch \\{|swallow|silent|nextDocgen" "$FILE" || true

Repository: storybookjs/storybook

Length of output: 3932


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILES=(
  "code/renderers/react/src/componentManifest/generator.ts"
  "code/renderers/react/src/docgen/buildDocgen.ts"
  "code/core/src/core-server/presets/common-preset.ts"
  "code/core/src/types/modules/core-common.ts"
)

for f in "${FILES[@]}"; do
  echo
  echo "==== $f (first/selected sections) ===="
  test -f "$f" || { echo "Missing $f"; continue; }
  awk '{print NR ":" $0}' "$f" | sed -n '1,220p'
done

echo
echo "== Search for how provider errors are handled in common-preset/docgen chain =="
rg -n "experimental_docgenProvider" code/core/src/core-server/presets/common-preset.ts
rg -n "catch \\{|throw|suppress|swallow|nextDocgen" code/core/src/core-server/presets/common-preset.ts

Repository: storybookjs/storybook

Length of output: 25865


Avoid mislabeling non-TypeScript init errors as “TypeScript not available”
getSharedComponentMetaManager() is intentionally kicked off unawaited (void getSharedComponentMetaManager()) and docgen expects undefined fallback, so rethrowing from this catch would break that assumption. Keep returning undefined, but distinguish “typescript missing” from other initialization failures and log the actual error (so real ComponentMetaManager bugs aren’t silently misreported as “TypeScript not available”).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/renderers/react/src/componentManifest/componentMetaManagerSingleton.ts`
around lines 19 - 27, The catch in getSharedComponentMetaManager() currently
masks all init failures as “TypeScript not available”; change it to detect a
genuine missing-module error (e.g., check error instanceof Error and inspect
error.code === 'MODULE_NOT_FOUND' or error.message includes "typescript") and
log the original Error details for non-missing-module failures (use logger.error
with error.stack/message) while still returning undefined in both cases so the
unawaited init and docgen fallback behavior remain unchanged; keep
ComponentMetaManager creation path and only treat the import failure as the
"missing TypeScript" debug log.

Comment on lines +34 to +40
afterEach(() => {
componentMetaManager?.dispose();
componentMetaManager = undefined;
if (tempDir) {
cleanup(tempDir);
tempDir = undefined;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Confirm this suite relies on temp-dir helpers and that helpers use real fs.
rg -n "createTempDir|writeFiles|cleanup" code/renderers/react/src/docgen/buildDocgen.test.ts -C2
rg -n "node:fs|mkdtemp|writeFile|rmSync|mkdirSync" code/renderers/react/src/componentManifest/componentMeta/test-helpers.ts -C2

Repository: storybookjs/storybook

Length of output: 2670


Use memfs for fs-backed fixtures in code/renderers/react/src/docgen/buildDocgen.test.ts

This suite writes real temp directories/files via createTempDir/writeFiles/cleanup, and the shared helpers use node:fs (mkdirSync, writeFileSync, rmSync). Replace the disk-backed setup with memfs (vol.fromJSON(...)) and add vol.reset() in a beforeEach.

Minimal direction
-import { afterEach, describe, expect, it } from 'vitest';
+import { afterEach, beforeEach, describe, expect, it } from 'vitest';
+import { vol } from 'memfs';
+
+beforeEach(() => {
+  vol.reset();
+});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/renderers/react/src/docgen/buildDocgen.test.ts` around lines 34 - 40,
Tests currently create real temp dirs/files via createTempDir/writeFiles/cleanup
and helpers use node:fs functions; switch to an in-memory fs by importing memfs
(vol) and use vol.fromJSON(...) in a beforeEach to populate fixtures and
vol.reset() to clear state, update the afterEach to call vol.reset() instead of
cleanup and remove or adapt createTempDir/writeFiles/cleanup usages so they
write into memfs (or replace them with a helper that constructs the same JSON
map passed to vol.fromJSON), and ensure tests import { vol } from 'memfs' and no
longer rely on node:fs helper side effects (references: afterEach, beforeEach,
createTempDir, writeFiles, cleanup, vol.fromJSON, vol.reset; also address helper
functions that call mkdirSync/writeFileSync/rmSync).

Resolve conflicts in docgen server tests and buildReactComponentDocgen
by keeping the ref-manifest branch behavior (no toReactComponentManifest).

Co-authored-by: Cursor <cursoragent@cursor.com>
@JReinhold

Copy link
Copy Markdown
Contributor Author

Babysit triage

Merge conflicts: Resolved by merging next (903f5d8832b). Kept branch behavior (no toReactComponentManifest; single-entry docgen server test fixture).

CodeRabbit comments:

  • componentMetaManagerSingleton.ts — intentional: optional dynamic typescript import with debug log when unavailable (same pattern as other React docgen paths). Not a runtime failure path for normal Storybook installs.
  • buildDocgen.test.ts memfs — not taking in this PR: these tests exercise real ComponentMetaManager / TypeScript language-service extraction over on-disk fixtures; memfs would not give meaningful RCM coverage without a much larger harness rewrite.

CI: Re-triggered CircleCI after transient 403 on trigger-circle-ci-workflow (CloudFront block).

@JReinhold JReinhold requested a review from ndelangen June 5, 2026 19:48
@JReinhold JReinhold merged commit 2ddbc3f into next Jun 5, 2026
145 of 146 checks passed
@JReinhold JReinhold deleted the jeppe-cursor/39213f22 branch June 5, 2026 19:49
@JReinhold JReinhold mentioned this pull request Jun 5, 2026
54 tasks
@github-actions github-actions Bot mentioned this pull request Jun 5, 2026
24 tasks
@github-actions github-actions Bot mentioned this pull request Jun 10, 2026
24 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:normal Run our default set of CI jobs (choose this for most PRs). core maintenance User-facing maintenance tasks qa:skip Pull Requests that do not need any QA.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant