Skip to content

Conversation

@yannbf
Copy link
Member

@yannbf yannbf commented Jan 7, 2026

Closes #

What I did

This PR introduces a Vite transformer which transforms a component code directly to a test code, using portable stories. This is useful to quickly detect whether a component turned into a story would be successful. It also uses the experimental_getArgTypesData preset to generate correct args for required props.

This will only be used internally as there's no use case for user usage at the moment.

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

  1. Go to the code directory
  2. Open a component like code/core/src/components/components/Badge/Badge.tsx
  3. Add a console log to the props it receives, make some of the props required.
  4. run STORYBOOK_COMPONENT_PATHS=core/src/components/components/Badge/Badge.tsx y test:watch Badge.tsx
  5. Notice that the test executes correctly, and the generated props are shown in the logs.
  6. If you throw an error, the test should fail.

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

  • New Features

    • Automatic component test generation for Vitest with optional path-based selection via env config.
    • Improved component test metadata (ArgTypes-driven props) and safer test execution guarding to avoid duplicate runs.
  • Tests

    • Added comprehensive tests covering named, default, and re-exported components and generated test scaffolding.

✏️ Tip: You can customize this high-level summary in your review settings.

@nx-cloud
Copy link

nx-cloud bot commented Jan 7, 2026

View your CI Pipeline Execution ↗ for commit cf4298c

Command Status Duration Result
nx run-many -t compile,check,knip,test,pretty-d... ✅ Succeeded 11m 17s View ↗

☁️ Nx Cloud last updated this comment at 2026-01-09 12:01:33 UTC

@yannbf yannbf force-pushed the yann/vitest-component-transformer branch from 80898bc to da79150 Compare January 7, 2026 20:37
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 7, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds a component-to-test transformer and integrates it into the Vitest Storybook addon via a conditional pre-transform plugin driven by STORYBOOK_COMPONENT_PATHS; extracts test-guard creation into a reusable helper; and re-exports the transformer from csf-tools.

Changes

Cohort / File(s) Summary
Vitest Addon Plugin Integration
code/addons/vitest/src/vitest-plugin/index.ts
Added getComponentTestPaths() to read STORYBOOK_COMPONENT_PATHS; added createComponentTestTransformPlugin() and conditionally injects the pre-transform when paths are set; extended Vitest include list; switched two try-catch blocks to bare catch { }.
Core CSF Tools Exports
code/core/src/csf-tools/index.ts
Re-exported componentTransform (from ./vitest-plugin/component-transformer).
Component Transformer Implementation
code/core/src/csf-tools/vitest-plugin/component-transformer.ts
New componentTransform that parses files, detects JSX-bearing exports, generates per-component Vitest test wrappers/metadata, invokes optional getComponentArgTypes, and returns transformed code + source map (or original code with null map if nothing to transform).
Transformer Tests
code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
New tests exercising named exports, default inline component hoisting, re-exports, multiple exports, and ensuring non-component exports are unchanged.
Test Guard Helper / Transformer Adjustments
code/core/src/csf-tools/vitest-plugin/transformer.ts
Added exported createTestGuardDeclaration() and refactored vitest transform flow to use this helper for guard generation.

Sequence Diagram(s)

sequenceDiagram
    participant VitestPlugin as Vitest Addon Plugin
    participant ComponentTransform as componentTransform
    participant Parser as Code Parser/AST
    participant Generator as Test Generator

    VitestPlugin->>VitestPlugin: Read STORYBOOK_COMPONENT_PATHS
    alt paths set
        VitestPlugin->>ComponentTransform: transform(code, fileName, getComponentArgTypes)
        ComponentTransform->>Parser: parse code, find JSX-bearing exports
        Parser-->>ComponentTransform: export metadata (names, AST nodes)
        ComponentTransform->>Generator: request argTypes (via getComponentArgTypes) and generate dummy props
        Generator->>ComponentTransform: test wrappers, metadata, guard insertion
        ComponentTransform-->>VitestPlugin: return transformed code + map
        VitestPlugin->>VitestPlugin: inject pre-transform plugin into Vite/Vitest plugins
    else no paths
        VitestPlugin->>VitestPlugin: skip componentTransform injection
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da79150 and cf4298c.

📒 Files selected for processing (3)
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts

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

🤖 Fix all issues with AI agents
In @code/core/src/csf-tools/vitest-plugin/component-transformer.ts:
- Around line 24-27: The sanitizeIdentifier function can return strings that
start with a digit (e.g., "123Component") which are invalid JS identifiers;
modify sanitizeIdentifier to, after stripping invalid chars and ensuring a
non-empty fallback, check whether the first character is a digit and if so
prefix the result with a safe character (e.g., '_' or 'C') so the returned value
is always a valid identifier; ensure this uses the existing sanitizeIdentifier
symbol and preserves the current fallback ('Component') behavior.
🧹 Nitpick comments (5)
code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts (1)

5-13: Consider adding test coverage for the getComponentArgTypes callback.

The transform helper always calls componentTransform without the optional getComponentArgTypes parameter. This means the args generation path (lines 314-317 in component-transformer.ts) is not exercised.

💡 Example test case
it('generates args from getComponentArgTypes when provided', async () => {
  const code = `
    export const Button = ({ label }: { label: string }) => <button>{label}</button>;
  `;

  const result = await componentTransform({
    code,
    fileName: 'src/components/Button.tsx',
    getComponentArgTypes: async () => ({
      label: { type: { name: 'string', required: true } },
    }),
  });

  expect(result.code).toContain('args: {');
  expect(result.code).toContain('label:');
});
code/addons/vitest/src/vitest-plugin/index.ts (2)

104-118: Consider clarifying or removing the TODO comment.

The path normalization appears necessary to ensure consistent matching against resolved file IDs. If this is confirmed to work correctly during testing, consider removing the TODO or documenting why it's needed.


131-161: Return undefined instead of code for early exits to signal no transformation.

When a Vite transform hook returns the original code as a string, it may cause unnecessary source map invalidation. Returning undefined or null explicitly signals that no transformation occurred.

Also, consider returning the source map from componentTransform for better debugging support.

♻️ Proposed fix
     async transform(code, id) {
       if (!optionalEnvToBoolean(process.env.VITEST) || storybookComponentTestPaths.length === 0) {
-        return code;
+        return;
       }

       const resolvedId = path.resolve(id);
       const matches = storybookComponentTestPaths.some(
         (testPath) =>
           resolvedId === testPath ||
           resolvedId.startsWith(testPath + path.sep) ||
           resolvedId.endsWith(testPath)
       );

       // We only transform paths included in STORYBOOK_COMPONENT_PATHS
       if (!matches) {
-        return code;
+        return;
       }

       const result = await componentTransform({
         code,
         fileName: id,
         getComponentArgTypes: async ({ componentName, fileName }) =>
           presets.apply('experimental_getArgTypesData', null, {
             componentFilePath: fileName,
             componentExportName: componentName,
             configDir,
           }),
       });

-      return result.code;
+      return {
+        code: result.code,
+        map: result.map,
+      };
     },
code/core/src/csf-tools/vitest-plugin/component-transformer.ts (2)

180-209: Complex but correct hoisting logic for inline default exports.

The approach of inserting a variable declaration before the export and then using getPrevSibling() to get the init path is clever. Consider adding a brief comment explaining why this two-step approach is needed (to get a proper NodePath for JSX detection).


312-360: Consider parallelizing getComponentArgTypes calls for performance.

The current for...of loop with await processes components sequentially. If getComponentArgTypes involves I/O (like docgen), parallel processing could improve performance for files with many exports.

💡 Parallel approach
// Build component data in parallel
const componentData = await Promise.all(
  components.map(async (component) => {
    const argTypes = getComponentArgTypes
      ? await getComponentArgTypes({ componentName: component.exportedName, fileName })
      : undefined;
    const generatedArgs = argTypes ? generateDummyPropsFromArgTypes(argTypes).required : undefined;
    return { component, generatedArgs };
  })
);

// Generate test statements (synchronous)
for (const { component, generatedArgs } of componentData) {
  // ... build testStoryArgs and testCall
}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70531e4 and da79150.

📒 Files selected for processing (5)
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/core/src/csf-tools/index.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/transformer.ts
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{js,jsx,ts,tsx,json,md,html,css,scss}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Format code using Prettier with yarn prettier --write <file>

Files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/core/src/csf-tools/index.ts
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Run ESLint checks using yarn lint:js:cmd <file> or the full command cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives to fix linting errors before committing

Files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/core/src/csf-tools/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/core/src/csf-tools/index.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx,js,jsx}: Export functions from modules if they need to be tested
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/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/core/src/csf-tools/index.ts
code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use logger from storybook/internal/node-logger for server-side logging in Node.js code

Files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/core/src/csf-tools/index.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{test,spec}.{ts,tsx}: Test files should follow the naming pattern *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx
Follow the spy mocking rules defined in .cursor/rules/spy-mocking.mdc for consistent mocking patterns with Vitest

Files:

  • code/core/src/csf-tools/vitest-plugin/component-transformer.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 the mocked functions in Vitest tests
Implement mock behaviors in beforeEach blocks in Vitest tests
Mock all required dependencies that the test subject uses
Each mock implementation should return a Promise for async functions in Vitest
Mock implementations should match the expected return type of the original function
Mock all required properties and methods that the test subject uses in Vitest tests
Avoid direct function mocking without vi.mocked() in Vitest tests
Avoid mock implementations outside of beforeEach blocks in Vitest tests
Avoid mocking without the spy: true option in Vitest tests
Avoid inline mock implementations within test cases in Vitest tests
Avoid mocking only a subset of required dependencies in Vitest tests
Mock at the highest level of abstraction needed in Vitest tests
Keep mock implementations simple and focused in Vitest tests
Use type-safe mocking with vi.mocked() in Vitest tests
Document complex mock behaviors in Vitest tests
Group related mocks together in Vitest tests

Files:

  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{test,spec}.{ts,tsx,js,jsx}: Write meaningful unit tests that actually import and call the functions being tested, not just verify syntax patterns
Achieve high test coverage of business logic, aiming for 75%+ coverage of statements/lines
Cover all branches, conditions, edge cases, error paths, and different input variations in unit tests
Use vi.mock() to mock file system, loggers, and other external dependencies in tests

Files:

  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
🧠 Learnings (25)
📓 Common learnings
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Document complex mock behaviors in Vitest tests
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid inline mock implementations within test cases in Vitest tests
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking only a subset of required dependencies in Vitest tests
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Implement mock behaviors in `beforeEach` blocks in Vitest tests
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required properties and methods that the test subject uses in Vitest tests
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Group related mocks together in Vitest tests
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mock implementations outside of `beforeEach` blocks in Vitest tests
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Keep mock implementations simple and focused in Vitest tests
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid inline mock implementations within test cases in Vitest tests

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/core/src/csf-tools/index.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mock implementations outside of `beforeEach` blocks in Vitest tests

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Document complex mock behaviors in Vitest tests

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/core/src/csf-tools/index.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking only a subset of required dependencies in Vitest tests

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/core/src/csf-tools/index.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Implement mock behaviors in `beforeEach` blocks in Vitest tests

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/core/src/csf-tools/index.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid direct function mocking without `vi.mocked()` in Vitest tests

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
📚 Learning: 2025-11-24T17:49:31.838Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T17:49:31.838Z
Learning: Applies to code/vitest.workspace.ts : Vitest configuration is centralized in `code/vitest.workspace.ts` for workspace setup

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Keep mock implementations simple and focused in Vitest tests

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking without the `spy: true` option in Vitest tests

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required properties and methods that the test subject uses in Vitest tests

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/core/src/csf-tools/index.ts
📚 Learning: 2025-11-24T17:49:31.838Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T17:49:31.838Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Follow the spy mocking rules defined in `.cursor/rules/spy-mocking.mdc` for consistent mocking patterns with Vitest

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Group related mocks together in Vitest tests

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/core/src/csf-tools/index.ts
📚 Learning: 2025-12-22T22:03:40.123Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : Write meaningful unit tests that actually import and call the functions being tested, not just verify syntax patterns

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/core/src/csf-tools/index.ts
📚 Learning: 2025-09-18T20:51:06.618Z
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.

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/transformer.ts
📚 Learning: 2025-12-22T22:03:40.123Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Export functions from modules if they need to be tested

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
  • code/core/src/csf-tools/index.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Each mock implementation should return a Promise for async functions in Vitest

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/component-transformer.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock at the highest level of abstraction needed in Vitest tests

Applied to files:

  • code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts
📚 Learning: 2025-12-22T22:03:40.123Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx} : Use `logger` from `storybook/internal/node-logger` for server-side logging in Node.js code

Applied to files:

  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/core/src/csf-tools/index.ts
📚 Learning: 2025-12-22T22:03:40.123Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to code/{renderers}/**/*.{ts,tsx,js,jsx} : Use `logger` from `storybook/internal/client-logger` for client-side logging in browser code

Applied to files:

  • code/addons/vitest/src/vitest-plugin/index.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/addons/vitest/src/vitest-plugin/index.ts
📚 Learning: 2025-09-17T07:31:04.432Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32484
File: code/core/package.json:326-326
Timestamp: 2025-09-17T07:31:04.432Z
Learning: In Storybook's core package, dependencies like `open` are bundled into the final distribution during the build process, so they should remain in devDependencies rather than being moved to dependencies. End users don't need these packages as separate runtime dependencies since they're included in the bundled code.

Applied to files:

  • code/addons/vitest/src/vitest-plugin/index.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/addons/vitest/src/vitest-plugin/index.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/addons/vitest/src/vitest-plugin/index.ts
📚 Learning: 2025-12-22T22:03:40.123Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : Use `vi.mock()` to mock file system, loggers, and other external dependencies in tests

Applied to files:

  • code/core/src/csf-tools/index.ts
🧬 Code graph analysis (3)
code/core/src/csf-tools/vitest-plugin/component-transformer.ts (2)
code/core/src/core-server/utils/get-dummy-props-for-args.ts (1)
  • generateDummyPropsFromArgTypes (18-48)
code/core/src/csf-tools/vitest-plugin/transformer.ts (1)
  • createTestGuardDeclaration (54-106)
code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts (2)
code/core/src/csf-tools/index.ts (1)
  • componentTransform (7-7)
code/core/src/csf-tools/vitest-plugin/component-transformer.ts (1)
  • componentTransform (264-373)
code/addons/vitest/src/vitest-plugin/index.ts (3)
code/core/src/common/utils/envs.ts (1)
  • optionalEnvToBoolean (59-70)
code/core/src/csf-tools/index.ts (1)
  • componentTransform (7-7)
code/core/src/csf-tools/vitest-plugin/component-transformer.ts (1)
  • componentTransform (264-373)
⏰ 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: normal
  • GitHub Check: nx
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (8)
code/core/src/csf-tools/index.ts (1)

7-7: LGTM!

The new export follows the established pattern and correctly exposes componentTransform from the public API, consistent with the existing vitestTransform export.

code/core/src/csf-tools/vitest-plugin/component-transformer.test.ts (1)

206-214: LGTM!

Good coverage of the early-return path when no JSX-containing components are found. This ensures non-component exports remain untouched.

code/core/src/csf-tools/vitest-plugin/transformer.ts (2)

46-106: Good refactor to extract reusable guard logic.

The function is well-documented and properly typed. The TODO comments (lines 67-68, 78-79) correctly reference the upstream Vitest issue for future cleanup.


230-235: LGTM!

Clean usage of the extracted helper. The guard declaration is properly integrated with the existing transform flow.

code/addons/vitest/src/vitest-plugin/index.ts (1)

504-506: LGTM!

The conditional plugin insertion correctly gates the component transform plugin behind the environment variable, avoiding unnecessary plugin registration when not needed.

code/core/src/csf-tools/vitest-plugin/component-transformer.ts (3)

38-55: LGTM!

The JSX detection is efficient with early exit on first match, and correctly handles both JSXElement and JSXFragment nodes.


69-90: LGTM!

Good implementation of import deduplication that avoids duplicate specifiers while maintaining proper import ordering.


13-13: Verify cross-module import is intentional.

This imports from ../../core-server/utils/get-dummy-props-for-args, crossing from csf-tools to core-server. If this utility is meant to be shared, consider re-exporting it from a common location or moving it to a shared utils module.

@storybook-app-bot
Copy link

Package Benchmarks

Commit: da79150, ran on 7 January 2026 at 20:48:31 UTC

The following packages have significant changes to their size or dependencies:

storybook

Before After Difference
Dependency count 49 49 0
Self size 20.19 MB 20.20 MB 🚨 +10 KB 🚨
Dependency size 16.52 MB 16.52 MB 0 B
Bundle Size Analyzer Link Link

@storybook/cli

Before After Difference
Dependency count 183 183 0
Self size 775 KB 775 KB 🚨 +84 B 🚨
Dependency size 67.25 MB 67.26 MB 🚨 +11 KB 🚨
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 176 176 0
Self size 30 KB 30 KB 0 B
Dependency size 65.82 MB 65.83 MB 🚨 +10 KB 🚨
Bundle Size Analyzer Link Link

create-storybook

Before After Difference
Dependency count 50 50 0
Self size 999 KB 999 KB 🚨 +42 B 🚨
Dependency size 36.71 MB 36.72 MB 🚨 +10 KB 🚨
Bundle Size Analyzer node node

@yannbf yannbf requested a review from kasperpeulen January 8, 2026 15:45
Base automatically changed from yann/refactor-story-generation to yann/ghost-stories-research January 9, 2026 11:46
@yannbf yannbf merged commit 1658085 into yann/ghost-stories-research Jan 9, 2026
10 of 15 checks passed
@yannbf yannbf deleted the yann/vitest-component-transformer branch January 9, 2026 11:50
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.

3 participants