Skip to content

Conversation

@yannbf
Copy link
Member

@yannbf yannbf commented Nov 12, 2025

Closes #32932

What I did

The CSF factories codemod was not properly checking usage of types before removing them, now it does.

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!

This is well tested in unit tests, but it is a good idea to give it a pass at the csf-factories codemod. An example code would be this:

import { Button } from './Button'
import { StoryFn, StoryObj, Meta } from '@storybook/react'

// Type should not be removed as it's used
type CustomStoryType = StoryFn & { bla: string }

const meta = {
  component: Button,
} as Meta<typeof Button>
export default meta

export const Primary: StoryObj<typeof Button> = {
  args: {
    label: 'Button',
  },
}

export const Secondary: CustomStoryType = (args) => {
  return <Button {...args} />
}

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

  • Tests

    • Added tests and snapshot checks to ensure user-defined generic types are preserved and Storybook-specific type aliases are removed only when unused.
  • Refactor

    • CSF transformation now uses a dedicated routine to detect and strip unused Storybook-related type declarations and clean up leftover type imports, producing cleaner transformed output.

@nx-cloud
Copy link

nx-cloud bot commented Nov 12, 2025

View your CI Pipeline Execution ↗ for commit 8ece7fd

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

☁️ Nx Cloud last updated this comment at 2025-11-15 09:36:58 UTC

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 12, 2025

📝 Walkthrough

Walkthrough

Extracts inline logic that removed unused Storybook-specific TypeScript types into a new exported helper removeUnusedTypes, integrates it into story-to-csf-factory.ts, and adds tests covering removal behavior and preservation of user-defined generics and used type aliases.

Changes

Cohort / File(s) Summary
New helper
code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.ts
Adds export function removeUnusedTypes(programNode: t.Program, ast: t.File): void that finds declared type aliases/interfaces, detects references to disallowed Storybook types, removes unused declarations that reference them, and triggers import cleanup.
New helper tests
code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts
Adds tests that apply removeUnusedTypes to CSF sources and assert transformed output via snapshots: removes unused Storybook-specific types, preserves used types, and leaves expected exports.
Codemod integration
code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
Replaces prior inline removal loop and explicit cleanupTypeImports call with an import and single call to removeUnusedTypes(programNode, csf._ast), maintaining surrounding flow and semantics.
Test expansion
code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
Adds tests verifying preservation of user-defined generic types and removal of unused Storybook-specific type aliases; includes inline snapshots asserting exact transformed code.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Factory as story-to-csf-factory
    participant Remover as removeUnusedTypes
    participant Program as ProgramNode
    participant AST as CSF_AST
    participant Cleanup as cleanupTypeImports

    Factory->>Remover: removeUnusedTypes(ProgramNode, CSF_AST)
    activate Remover
    Remover->>AST: scan for declared type aliases & interfaces
    Remover->>AST: scan for referenced type identifiers
    Remover->>Remover: mark declarations referencing disallowed Storybook types
    Remover->>Program: mutate program.body to remove unused marked declarations
    Remover->>Cleanup: cleanupTypeImports(AST, removedNames)
    deactivate Remover
    Remover-->>Factory: return (void) — AST mutated
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review AST traversal and reference-resolution in removeUnusedTypes for forward references, alias chains, and nested types.
  • Verify mutation of programNode.body is safe across AST shapes and preserves unrelated declarations.
  • Confirm cleanupTypeImports does not remove imports still referenced by preserved types.
  • Inspect new tests and snapshots for brittleness and sufficient coverage.

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 61612bd and bc61ca4.

📒 Files selected for processing (1)
  • code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: The useGlobals hook from storybook/manager-api returns a tuple where the third element (storyGlobals) is typed as Globals, not Globals | undefined. This means TypeScript guarantees it's always defined, making the `in` operator safe to use without additional null checks.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts
📚 Learning: 2025-11-05T09:37:25.920Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts
🧬 Code graph analysis (1)
code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts (2)
code/core/src/csf-tools/CsfFile.ts (2)
  • loadCsf (1060-1064)
  • printCsf (1079-1081)
code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.ts (1)
  • removeUnusedTypes (35-168)
🪛 Biome (2.1.2)
code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts

[error] 15-15: Do not shadow the global "unescape" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

⏰ 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/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts (3)

17-22: Good helper abstraction.

The getTransformed helper appropriately encapsulates the transformation workflow: load CSF, parse, apply removeUnusedTypes, and print the result.


24-89: Comprehensive test coverage for the removal case.

This test effectively validates that unused types referencing disallowed Storybook types (Meta, StoryObj, ComponentStory, ComponentMeta, StoryFn) are correctly removed, including:

  • Type aliases with direct and nested references
  • Interfaces with extends and nested references
  • Associated unused imports

The snapshot assertion clearly shows the expected diff.


91-125: Excellent validation that used types are preserved.

This test ensures the helper correctly preserves:

  • Types used in function parameters (Story, DeepType, Interface, DeepInterface in foo)
  • Types used in variable annotations (ComponentMeta in X)
  • Types referenced by other types (Alias referenced by Alias2)

The equality assertion confirms no unintended modifications occur when types are actually in use, directly addressing the bug described in issue #32932.


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

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/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts (1)

670-702: Consider adding test coverage for transitive type alias usage.

The test validates that used type aliases are preserved when referenced in variable declarations. However, it doesn't cover the case where a Storybook type alias is used by another type alias:

type Story = StoryObj<Props>;
type MyStory = Story; // Story used here

This edge case might not be handled correctly by the current implementation (see related comment on the production code).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a6dc2e and e062f38.

📒 Files selected for processing (2)
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts (1 hunks)
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts (2 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/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.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/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.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/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.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/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.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/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.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/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.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/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
🧠 Learnings (16)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to test-storybooks/** : Maintain test Storybook configurations under `test-storybooks/` for E2E and visual testing scenarios
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: The useGlobals hook from storybook/manager-api returns a tuple where the third element (storyGlobals) is typed as Globals, not Globals | undefined. This means TypeScript guarantees it's always defined, making the `in` operator safe to use without additional null checks.
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking only a subset of required dependencies
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
Repo: storybookjs/storybook PR: 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/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
Repo: storybookjs/storybook PR: 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/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to test-storybooks/** : Maintain test Storybook configurations under `test-storybooks/` for E2E and visual testing scenarios

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid inline mock implementations within test cases

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
📚 Learning: 2025-09-17T08:11:47.197Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.197Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Keep mock implementations simple and focused

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
📚 Learning: 2025-09-17T08:11:47.197Z
Learnt from: CR
Repo: storybookjs/storybook PR: 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/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to **/*.@(test|spec).{ts,tsx,js,jsx} : Unit tests should import and execute the functions under test rather than only asserting on syntax patterns

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking only a subset of required dependencies

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock implementations should match the expected return type of the original function

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
📚 Learning: 2025-09-17T08:11:47.197Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.197Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Group related mocks together

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required properties and methods that the test subject uses

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Implement mock behaviors in beforeEach blocks

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Each mock implementation should return a Promise for async functions

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
⏰ 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). (3)
  • GitHub Check: Danger JS
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (4)
code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts (1)

626-668: LGTM! Test effectively validates the fix for issue #32932.

This test confirms that user-defined generic types (like Data = Record<string, any>) are preserved when used, while unused Storybook-specific type aliases are correctly removed. The inline snapshot provides clear validation of the expected transformation.

code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts (3)

26-73: LGTM! Properly identifies Storybook-related type aliases.

The function correctly traverses TSTypeAliasDeclarations and identifies those referencing Storybook types, including handling for qualified names (e.g., Namespace.Meta). The early stopping with innerPath.stop() is a good optimization.


114-138: LGTM! Clean separation of concerns and correct removal logic.

The function properly orchestrates the detection and removal process. The filter logic correctly preserves all nodes except the identified unused Storybook type aliases.


444-449: LGTM! Clear integration with well-documented two-step cleanup process.

The comments effectively distinguish between removing unused type aliases (new functionality) and cleaning up type imports (existing functionality). The sequencing is logical.

@storybook-app-bot
Copy link

storybook-app-bot bot commented Nov 12, 2025

Package Benchmarks

Commit: 8ece7fd, ran on 15 November 2025 at 09:26:08 UTC

No significant changes detected, all good. 👏

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/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts (1)

11-19: Centralize the disallow list.

With type cleanup delegated to removeUnusedTypes, this local typesDisallowList copy is now redundant and duplicates the list maintained inside the helper. Let’s drop it (or import the shared one) so we only maintain the list in one place and avoid future drift.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e062f38 and 82deb7a.

📒 Files selected for processing (3)
  • code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts (1 hunks)
  • code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.ts (1 hunks)
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts (3 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: The useGlobals hook from storybook/manager-api returns a tuple where the third element (storyGlobals) is typed as Globals, not Globals | undefined. This means TypeScript guarantees it's always defined, making the `in` operator safe to use without additional null checks.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: In viewport tool code, when using the `useGlobals` hook from storybook/manager-api, the third returned value `storyGlobals` is guaranteed by TypeScript to be defined (not undefined/null), making the `in` operator safe to use without additional null checks.
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
📚 Learning: 2025-11-05T09:37:25.920Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
🧬 Code graph analysis (2)
code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts (2)
code/core/src/csf-tools/CsfFile.ts (2)
  • loadCsf (1060-1064)
  • printCsf (1079-1081)
code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.ts (1)
  • removeUnusedTypes (53-105)
code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts (1)
code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.ts (1)
  • removeUnusedTypes (53-105)
⏰ 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

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/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts (2)

24-85: Test logic is sound; remove unnecessary async.

The test correctly verifies that unused Storybook type aliases and interfaces are removed while preserving imports and exports. However, the async keyword is unnecessary since no asynchronous operations occur.

Apply this diff:

-  it('should remove unused Storybook types', async () => {
+  it('should remove unused Storybook types', () => {

87-121: Remove the unnecessary async keyword from the test function.

The test function is marked async but performs no asynchronous operations. The test expectations are correct—Alias2 is properly preserved because the removeUnusedTypes implementation only removes unused types that directly reference disallowed Storybook types. Since Alias2 only indirectly references StoryFn (through Alias), it is correctly kept.

-  it('should not remove used Storybook types', async () => {
+  it('should not remove used Storybook types', () => {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6935742 and ff0e4b6.

📒 Files selected for processing (1)
  • code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: The useGlobals hook from storybook/manager-api returns a tuple where the third element (storyGlobals) is typed as Globals, not Globals | undefined. This means TypeScript guarantees it's always defined, making the `in` operator safe to use without additional null checks.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: In viewport tool code, when using the `useGlobals` hook from storybook/manager-api, the third returned value `storyGlobals` is guaranteed by TypeScript to be defined (not undefined/null), making the `in` operator safe to use without additional null checks.
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts
🧬 Code graph analysis (1)
code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts (2)
code/core/src/csf-tools/CsfFile.ts (2)
  • loadCsf (1060-1064)
  • printCsf (1079-1081)
code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.ts (1)
  • removeUnusedTypes (53-105)
🪛 Biome (2.1.2)
code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts

[error] 15-15: Do not shadow the global "unescape" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

⏰ 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/lib/cli-storybook/src/codemod/helpers/remove-unused-types.test.ts (3)

1-8: LGTM!

All imports are necessary and properly used in the test suite.


10-13: Custom snapshot serializer looks good.

The serializer ensures all snapshot values are converted to strings. While test: () => true is broad, it's acceptable here since all snapshots in this file are string-based.


17-22: LGTM!

The getTransformed helper correctly implements the transformation pipeline: load → parse → transform → print. The flow matches the expected usage pattern for CSF transformations.

…com:storybookjs/storybook into yann/do-not-remove-used-types-csf-factories
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/lib/cli-storybook/src/codemod/helpers/remove-unused-types.ts (2)

54-72: Consider simplifying the type extraction.

The comment on line 55 notes that the id property is always an Identifier, yet lines 56-58 defensively check for this. Given TypeScript's type guarantees for these AST nodes, you can simplify:

-      if (path.isTSTypeAliasDeclaration() || path.isTSInterfaceDeclaration()) {
-        // These always have an `id` property that's an Identifier
-        const idNode = (node as t.TSTypeAliasDeclaration | t.TSInterfaceDeclaration).id;
-        const name = idNode && t.isIdentifier(idNode) ? idNode.name : undefined;
-        if (name) {
-          declaredTypes.add(name);
+      if (path.isTSTypeAliasDeclaration() || path.isTSInterfaceDeclaration()) {
+        const name = node.id.name;
+        declaredTypes.add(name);

-          // If we previously saw identifiers with this name before the declaration,
-          // count them now as references (handles reference-before-declaration).
-          if (pendingIdentifierNames.has(name)) {
-            referencedTypes.add(name);
-          }
+        // If we previously saw identifiers with this name before the declaration,
+        // count them now as references (handles reference-before-declaration).
+        if (pendingIdentifierNames.has(name)) {
+          referencedTypes.add(name);
         }
-
-        // No need to traverse into the id itself here; we still want to traverse the
-        // declaration body so that disallowed-type references inside are detected
-        // by the TSTypeReference/TSExpressionWithTypeArguments handlers below.
-        return;
-      }
+        return;
+      }

This matches the approach already used at line 151 and aligns with ndelangen's past review suggestion.

Based on learnings


107-144: Consider simplifying the owner extraction.

Similar to the earlier suggestion, the defensive type checking can be simplified. Since we've already confirmed the owner is a type declaration via type guards, the id is guaranteed to be an Identifier:

       if (path.isTSTypeReference()) {
         const typeRefNode = node as t.TSTypeReference;
         const typeNameNode = typeRefNode.typeName;
 
         if (t.isIdentifier(typeNameNode) && disallowedTypesSet.has(typeNameNode.name)) {
           const owner = path.findParent(
             (p) => p.isTSTypeAliasDeclaration() || p.isTSInterfaceDeclaration()
           );
-          if (owner && (owner.isTSTypeAliasDeclaration() || owner.isTSInterfaceDeclaration())) {
-            const ownerId = (owner.node as t.TSTypeAliasDeclaration | t.TSInterfaceDeclaration).id;
-            const ownerName = t.isIdentifier(ownerId) ? ownerId.name : undefined;
-            if (ownerName) {
-              typeDeclReferencesDisallowed.add(ownerName);
-            }
+          if (owner) {
+            typeDeclReferencesDisallowed.add(owner.node.id.name);
           }
         }
         return;
       }

Apply the same simplification to the TSExpressionWithTypeArguments handler (lines 128-144).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff0e4b6 and 61612bd.

📒 Files selected for processing (1)
  • code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: The useGlobals hook from storybook/manager-api returns a tuple where the third element (storyGlobals) is typed as Globals, not Globals | undefined. This means TypeScript guarantees it's always defined, making the `in` operator safe to use without additional null checks.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.
⏰ 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 (5)
code/lib/cli-storybook/src/codemod/helpers/remove-unused-types.ts (5)

1-16: LGTM! Clean imports and efficient type tracking setup.

The disallowed types list covers the key Storybook-specific types that should be removed when unused, and using a Set for fast lookups is appropriate.


35-48: Excellent approach with forward reference handling.

The single-pass traversal with four tracking sets is efficient and elegant. The pendingIdentifierNames set correctly handles the case where a type is referenced before its declaration, which can occur in TypeScript files.


75-103: LGTM! Identifier tracking correctly handles references and forward declarations.

The logic properly skips declaration identifiers (lines 82-92) and correctly promotes pending identifiers to references when their declaration is encountered. This ensures that types used before they're declared aren't incorrectly marked as unused.


149-164: Filter logic correctly implements the removal conditions.

The three-part check ensures only unused Storybook-related type declarations are removed, which directly addresses the bug described in issue #32932. Note that line 151 uses node.id.name directly (correctly), which contrasts with the more defensive approach elsewhere—this simpler pattern could be applied throughout the file.


166-168: LGTM! Proper cleanup of orphaned imports.

Delegating to cleanupTypeImports after removing the type declarations ensures that any now-unused Storybook type imports are also removed, keeping the file clean.

@yannbf yannbf merged commit 6222a0d into next Nov 18, 2025
63 checks passed
@yannbf yannbf deleted the yann/do-not-remove-used-types-csf-factories branch November 18, 2025 08:48
@github-actions github-actions bot mentioned this pull request Nov 18, 2025
17 tasks
@yannbf yannbf added the needs qa Indicates that this needs manual QA during the upcoming minor/major release label Nov 21, 2025
@github-actions
Copy link
Contributor

Fails
🚫

Danger failed to run scripts/dangerfile.ts.

Error Error

require() cannot be used on an ESM graph with top-level await. Use import() instead. To see where the top-level await comes from, use --experimental-print-required-tla.
  From /usr/src/danger/dist/runner/runners/inline.js 
  Requiring /github/workspace/scripts/dangerfile.ts 
Error [ERR_REQUIRE_ASYNC_MODULE]: require() cannot be used on an ESM graph with top-level await. Use import() instead. To see where the top-level await comes from, use --experimental-print-required-tla.
  From /usr/src/danger/dist/runner/runners/inline.js 
  Requiring /github/workspace/scripts/dangerfile.ts 
    at ModuleJobSync.runSync (node:internal/modules/esm/module_job:433:13)
    at ModuleLoader.importSyncForRequire (node:internal/modules/esm/loader:389:47)
    at loadESMFromCJS (node:internal/modules/cjs/loader:1363:24)
    at Module._compile (node:internal/modules/cjs/loader:1503:5)
    at requireFromString (/usr/src/danger/node_modules/require-from-string/index.js:28:4)
    at /usr/src/danger/dist/runner/runners/inline.js:183:68
    at step (/usr/src/danger/dist/runner/runners/inline.js:56:23)
    at Object.next (/usr/src/danger/dist/runner/runners/inline.js:37:53)
    at /usr/src/danger/dist/runner/runners/inline.js:31:71
    at new Promise (<anonymous>)

Dangerfile

----------------^

Generated by 🚫 dangerJS against 8ece7fd

@yannbf yannbf removed the needs qa Indicates that this needs manual QA during the upcoming minor/major release label Nov 25, 2025
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.

[Bug]: CSF-Factories migration: sometimes removes typescript types

3 participants