Skip to content

Maintenance: Extract createPreviewAnnotations factory to internal/common#34394

Closed
mixelburg wants to merge 3 commits into
storybookjs:nextfrom
mixelburg:refactor/extract-create-preview-annotations
Closed

Maintenance: Extract createPreviewAnnotations factory to internal/common#34394
mixelburg wants to merge 3 commits into
storybookjs:nextfrom
mixelburg:refactor/extract-create-preview-annotations

Conversation

@mixelburg
Copy link
Copy Markdown

@mixelburg mixelburg commented Mar 29, 2026

What

Extracts a createPreviewAnnotations(packageName, extraEntries?) factory function to storybook/internal/common and uses it in all 5 renderer preset files.

Closes #34389

Why

The previewAnnotations export was copy-pasted identically across 5 files (html, preact, svelte, vue3, web-components), differing only in the package name string and — for web-components — an extra entry-preview-argtypes annotation. This means any change to the docsEnabled check or result-building logic requires updating 5 files, with real risk of inconsistencies.

Changes

  • New: code/core/src/common/utils/preview-annotations.tscreatePreviewAnnotations(packageName, extraEntries?) factory
  • Updated: code/core/src/common/index.ts — re-exports the new factory
  • Updated: code/renderers/{html,preact,svelte,vue3,web-components}/src/preset.ts — replaced inlined function with createPreviewAnnotations() call

The web-components renderer passes ['entry-preview-argtypes'] as the second argument; all others use the default (no extra entries). Behaviour is identical to before.

Summary by CodeRabbit

  • Refactor
    • Centralized preview annotation handling into a shared utility and re-exported it from the common package.
    • Updated HTML, Preact, Svelte, Vue 3, and Web Components renderers to use the shared preview annotation logic, reducing duplication and ensuring consistent inclusion of preview-related entries (including optional docs and argtypes entries) across renderers.

…rnal/common

The previewAnnotations preset function was copy-pasted identically across
5 renderer preset files (html, preact, svelte, vue3, web-components). The
only difference was the package name string and, for web-components, an
extra entry-preview-argtypes entry.

Extract a createPreviewAnnotations(packageName, extraEntries?) factory to
storybook/internal/common and use it in all 5 renderers. This eliminates
the duplication and ensures future changes to the docsEnabled check or the
result-building logic only need to be made in one place.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 29, 2026

📝 Walkthrough

Walkthrough

A shared factory createPreviewAnnotations was added to the common package and re-exported; multiple renderer presets were updated to delegate their previewAnnotations implementations to this factory, consolidating common annotation-entry construction and docs-detection logic.

Changes

Cohort / File(s) Summary
Common Exports
code/core/src/common/index.ts
Re-exported the new preview-annotations utility.
Preview Annotations Utility
code/core/src/common/utils/preview-annotations.ts
Added createPreviewAnnotations(packageName, extraEntries?) which returns a previewAnnotations preset that resolves entry-preview, optional extra entries, and conditionally entry-preview-docs when the docs preset is enabled.
Renderers — presets switched to factory
code/renderers/html/src/preset.ts, code/renderers/preact/src/preset.ts, code/renderers/svelte/src/preset.ts, code/renderers/vue3/src/preset.ts, code/renderers/web-components/src/preset.ts
Replaced inline async previewAnnotations implementations with createPreviewAnnotations(...) calls (web-components passes ['entry-preview-argtypes']); local path-resolution and docs-check logic removed in favor of the shared factory.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs


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
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.

🧹 Nitpick comments (2)
code/core/src/common/utils/preview-annotations.ts (2)

11-25: Add a focused regression test around this factory.

This helper now fans out to five renderer presets, so one small test for docs on/off plus extraEntries ordering would catch behavior drift without duplicating coverage in every preset.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/common/utils/preview-annotations.ts` around lines 11 - 25, Add
a focused regression test for createPreviewAnnotations that verifies behavior
when docs are enabled and disabled and confirms extraEntries ordering: call
createPreviewAnnotations(packageName, extraEntries) to get the returned async
preset function, provide a mocked options object whose presets.apply('docs', {},
options) returns either an empty object (docs disabled) or non-empty (docs
enabled), await the preset function (passing an initial input array) and assert
the resulting string array includes
fileURLToPath(import.meta.resolve(`${packageName}/entry-preview`)) first, then
the fileURLToPath(...) results for each extraEntries in order, and finally
includes fileURLToPath(import.meta.resolve(`${packageName}/entry-preview-docs`))
only when docs are enabled; also include a test for empty extraEntries and for
non-empty to catch ordering regressions.

18-23: Drop the string[] cast on input.

previewAnnotations accepts object-form entries as well as strings, so this cast narrows away valid inputs and makes future string-only assumptions easier to miss. Please double-check the inferred type here against the Entry[] preset contract.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/common/utils/preview-annotations.ts` around lines 18 - 23,
Remove the "(input as string[])" cast in previewAnnotations so we don't narrow
Entry[] to strings; instead concatenate the original input array directly (or
start with ([] as Entry[]).concat(input)) and ensure the function signature
accepts Entry[] per the preset contract. Keep the fileURLToPath +
import.meta.resolve calls for package-based strings (extraEntries,
entry-preview, entry-preview-docs) but do not assume the existing input entries
are strings—preserve object-form entries when concatenating and only pass actual
strings into fileURLToPath/resolve paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@code/core/src/common/utils/preview-annotations.ts`:
- Around line 11-25: Add a focused regression test for createPreviewAnnotations
that verifies behavior when docs are enabled and disabled and confirms
extraEntries ordering: call createPreviewAnnotations(packageName, extraEntries)
to get the returned async preset function, provide a mocked options object whose
presets.apply('docs', {}, options) returns either an empty object (docs
disabled) or non-empty (docs enabled), await the preset function (passing an
initial input array) and assert the resulting string array includes
fileURLToPath(import.meta.resolve(`${packageName}/entry-preview`)) first, then
the fileURLToPath(...) results for each extraEntries in order, and finally
includes fileURLToPath(import.meta.resolve(`${packageName}/entry-preview-docs`))
only when docs are enabled; also include a test for empty extraEntries and for
non-empty to catch ordering regressions.
- Around line 18-23: Remove the "(input as string[])" cast in previewAnnotations
so we don't narrow Entry[] to strings; instead concatenate the original input
array directly (or start with ([] as Entry[]).concat(input)) and ensure the
function signature accepts Entry[] per the preset contract. Keep the
fileURLToPath + import.meta.resolve calls for package-based strings
(extraEntries, entry-preview, entry-preview-docs) but do not assume the existing
input entries are strings—preserve object-form entries when concatenating and
only pass actual strings into fileURLToPath/resolve paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 82093879-03e5-46ec-a464-a927d3dfc318

📥 Commits

Reviewing files that changed from the base of the PR and between 0efef13 and cfc2970.

📒 Files selected for processing (7)
  • code/core/src/common/index.ts
  • code/core/src/common/utils/preview-annotations.ts
  • code/renderers/html/src/preset.ts
  • code/renderers/preact/src/preset.ts
  • code/renderers/svelte/src/preset.ts
  • code/renderers/vue3/src/preset.ts
  • code/renderers/web-components/src/preset.ts

valentinpalkovic

This comment was marked as outdated.

@valentinpalkovic valentinpalkovic self-assigned this Mar 30, 2026
@valentinpalkovic valentinpalkovic added maintenance User-facing maintenance tasks ci:normal labels Mar 30, 2026
@valentinpalkovic valentinpalkovic moved this to Empathy Queue (prioritized) in Core Team Projects Mar 30, 2026
@valentinpalkovic valentinpalkovic changed the title refactor(renderers): extract createPreviewAnnotations factory to internal/common Maintenance: extract createPreviewAnnotations factory to internal/common Mar 30, 2026
@valentinpalkovic valentinpalkovic changed the title Maintenance: extract createPreviewAnnotations factory to internal/common Maintenance: Extract createPreviewAnnotations factory to internal/common Mar 30, 2026
@valentinpalkovic
Copy link
Copy Markdown
Contributor

valentinpalkovic commented Mar 30, 2026

Hi @mixelburg

Thank you for your contribution!

Please fix the formatting issues so that we can proceed with merging the PR

@valentinpalkovic valentinpalkovic moved this from Empathy Queue (prioritized) to On Hold in Core Team Projects Mar 30, 2026
Copy link
Copy Markdown
Contributor

@valentinpalkovic valentinpalkovic left a comment

Choose a reason for hiding this comment

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

Unfortunately, this refactoring will cause issues in package managers with strict dependency resolution. Some package managers like pnpm will complain because they will try to e.g. resolve @storybook/react from the storybook package, which doesn't have @storybook/react defined as an entry point. Therefore, the import.meta.resolve calls has to remain in their renderer packages.

Copy link
Copy Markdown
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code/core/src/common/utils/preview-annotations.ts`:
- Line 16: The expression computing docsEnabled calls Object.keys on the result
of presets.apply('docs', {}, options) which can be null/undefined; change the
expression that assigns docsEnabled so it treats the apply result as
(presets.apply('docs', {}, options) ?? {}) before calling Object.keys (i.e., use
nullish coalescing) to avoid TypeError when a preset returns a nullish value;
update the assignment where docsEnabled is defined to apply this coalescing.
🪄 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: f6fe8206-5de0-48f9-93c3-1d1f32d374c1

📥 Commits

Reviewing files that changed from the base of the PR and between cfc2970 and adb2ddd.

📒 Files selected for processing (1)
  • code/core/src/common/utils/preview-annotations.ts

extraEntries: string[] = []
): PresetProperty<'previewAnnotations'> {
return async (input = [], options) => {
const docsEnabled = Object.keys(await options.presets.apply('docs', {}, options)).length > 0;
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

Add defensive nullish coalescing for presets.apply() result.

Per the codebase pattern shown in common-preset.ts and server-statics.ts, presets.apply() can return null/undefined if a preset's extension function returns a nullish value. Calling Object.keys() on null/undefined throws a TypeError.

Proposed fix
-    const docsEnabled = Object.keys(await options.presets.apply('docs', {}, options)).length > 0;
+    const docsEnabled =
+      Object.keys((await options.presets.apply('docs', {}, options)) ?? {}).length > 0;
📝 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 docsEnabled = Object.keys(await options.presets.apply('docs', {}, options)).length > 0;
const docsEnabled =
Object.keys((await options.presets.apply('docs', {}, options)) ?? {}).length > 0;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/common/utils/preview-annotations.ts` at line 16, The expression
computing docsEnabled calls Object.keys on the result of presets.apply('docs',
{}, options) which can be null/undefined; change the expression that assigns
docsEnabled so it treats the apply result as (presets.apply('docs', {}, options)
?? {}) before calling Object.keys (i.e., use nullish coalescing) to avoid
TypeError when a preset returns a nullish value; update the assignment where
docsEnabled is defined to apply this coalescing.

@github-project-automation github-project-automation Bot moved this from On Hold to Done in Core Team Projects Apr 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:normal maintenance User-facing maintenance tasks

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Duplicate Code: previewAnnotations Pattern Repeated Across 5 Renderer Presets

2 participants