Skip to content

Builder-Vite: Support configLoader via builder options#34080

Open
holvi-sebastian wants to merge 5 commits into
storybookjs:nextfrom
holvi-sebastian:next
Open

Builder-Vite: Support configLoader via builder options#34080
holvi-sebastian wants to merge 5 commits into
storybookjs:nextfrom
holvi-sebastian:next

Conversation

@holvi-sebastian
Copy link
Copy Markdown

@holvi-sebastian holvi-sebastian commented Mar 9, 2026

This PR allows the configLoader option / command-line arg to be passed to vite by builder-vite.

This solves a problem that occurs when importing from another package in vite.config.ts that exports .ts files directly. This is a common scenario in monorepos configured in a 'just-in-time' setup, and has been a broadly requested issue in vite: vitejs/vite#5370

Closes #

This PR relates to the following feature requests:
#33390
#32886

Oddly, there don't seem to be any issues that relate to this topic, or if there are I haven't found the right keywords to find them.

What I did

This PR adds pass-through support for the configLoader param on vite's loadConfigFromFile call in commonConfig, by reading the value from builder options. This is equivalent to passing --configLoader <value> to vite at the command line.

Checklist for Contributors

Testing

Didn't see an obvious place where this option could be tested. I looked for an equivalent test for the viteConfigPath option, but didn't find it, so I added it to vite-config.test.ts.

The changes in this PR are covered in the following automated tests:

  • unit tests

Manual testing

  1. Create a sandbox for template, e.g. yarn task --task sandbox --start-from auto --template react-vite/default-ts
  2. Import a .ts file into vite.config.ts of the template from another workspace project in the monorepo.
  3. Run storybook build

Without this PR, storybook will fail, expecting .js to exist, but instead finding .ts... it is unable to determine what to do with the `.ts file.

With the PR, the build completes as normal.

Documentation

  • Add or update documentation reflecting your changes

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 pull request has been released as version 0.0.0-pr-34080-sha-088d952e. Try it out in a new sandbox by running npx storybook@0.0.0-pr-34080-sha-088d952e sandbox or in an existing project with npx storybook@0.0.0-pr-34080-sha-088d952e upgrade.

More information
Published version 0.0.0-pr-34080-sha-088d952e
Triggered by @valentinpalkovic
Repository holvi-sebastian/storybook
Branch next
Commit 088d952e
Datetime Fri Mar 20 12:48:50 UTC 2026 (1774010930)
Workflow run 23343516202

To request a new release of this pull request, mention the @storybookjs/core team.

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

Summary by CodeRabbit

  • New Features

    • Vite builder adds a new configLoader option allowing 'bundle', 'runner', or 'native' selection.
  • Documentation

    • Added examples and docs demonstrating how to use the new configLoader with various frameworks and main config variants.
  • Tests

    • Added unit coverage and test-runner updates to propagate and honor the new configLoader option.

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Mar 9, 2026

View your CI Pipeline Execution ↗ for commit 9d88c25

Command Status Duration Result
nx run-many -t compile -c production --parallel=1 ✅ Succeeded 7m 53s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-18 19:15:13 UTC

@holvi-sebastian holvi-sebastian marked this pull request as ready for review March 9, 2026 14:32
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 9, 2026

📝 Walkthrough

Walkthrough

Adds an optional configLoader?: 'bundle' | 'runner' | 'native' to the Vite builder options and threads it through Vite config loading and the Vitest addon/test-runner (types, tests, docs, env wiring).

Changes

Cohort / File(s) Summary
Vite builder: types & config
code/builders/builder-vite/src/types.ts, code/builders/builder-vite/src/vite-config.ts, code/builders/builder-vite/src/vite-config.test.ts
Add `configLoader?: 'bundle'
Vitest addon: runtime wiring
code/addons/vitest/src/node/boot-test-runner.ts, code/addons/vitest/src/node/test-manager.ts, code/addons/vitest/src/node/vitest-manager.ts, code/addons/vitest/src/node/vitest.ts, code/addons/vitest/src/preset.ts
Propagate configLoader through TestManager/preset APIs, set STORYBOOK_CONFIG_LOADER env for spawned test-runner processes, and forward configLoader into createVitest options and TestManager initialization.
Addons devDependency
code/addons/vitest/package.json
Add @storybook/builder-vite: "workspace:*" to devDependencies.
Documentation/snippets
docs/_snippets/main-config-builder-configLoader.md, docs/builders/vite.mdx
Add docs/examples and MDX content showing configLoader usage for the Vite builder across frameworks and module systems.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as Storybook CLI
    participant Builder as Builder-Vite
    participant Vite as Vite (loadConfigFromFile)
    participant Preset as Vitest Preset
    participant Manager as Vitest Manager / TestRunner

    CLI->>Builder: start build/serve (options include configLoader)
    Builder->>Vite: loadConfigFromFile(..., configLoader)
    Note over Builder,Vite: Vite resolves user config using specified loader
    CLI->>Preset: initialize addons (reads core.builder.options)
    Preset->>Manager: runTestRunner(..., configLoader)
    Manager->>Manager: set env STORYBOOK_CONFIG_LOADER=configLoader
    Manager->>Manager: spawn child test-runner process (reads env)
    Manager->>Manager: start Vitest with createVitest({ configLoader })
Loading

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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
code/builders/builder-vite/src/vite-config.test.ts (2)

11-15: Consider using spy: true option for the Vite mock.

Per coding guidelines, vi.mock() should use the spy: true option for all package mocks. This applies to the existing mock setup as well.

♻️ Suggested fix
-vi.mock('vite', async (importOriginal) => ({
+vi.mock('vite', { spy: true }, async (importOriginal) => ({
   ...(await importOriginal<typeof import('vite')>()),
   loadConfigFromFile: vi.fn(async () => ({})),
   defaultClientConditions: undefined,
 }));

As per coding guidelines: "Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests".

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

In `@code/builders/builder-vite/src/vite-config.test.ts` around lines 11 - 15, The
test's module mock for 'vite' uses vi.mock(...) without the required spy option;
update the vi.mock call for 'vite' to include the spy: true option so the mock
follows the repository guideline (i.e., call vi.mock('vite', async
(importOriginal) => ({ ... }), { spy: true });) while keeping the existing
overrides for loadConfigFromFile and defaultClientConditions intact; ensure the
vi.mock invocation that references loadConfigFromFile and
defaultClientConditions is the one updated.

113-119: Move mock implementation to beforeEach block per coding guidelines.

The mock behavior is implemented inline within the test case. Per coding guidelines, mock implementations should be placed in beforeEach blocks to maintain consistency across tests.

However, since this test requires a specific mock return value different from other tests, the current approach with mockReturnValueOnce is acceptable for this isolated case. Consider documenting why this inline mock is necessary.

As per coding guidelines: "Implement mock behaviors in beforeEach blocks in Vitest tests" and "Avoid inline mock implementations within test cases in Vitest tests".

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

In `@code/builders/builder-vite/src/vite-config.test.ts` around lines 113 - 119,
Move the inline mockReturnValueOnce call for loadConfigFromFileMock into the
test suite's beforeEach to follow the Vitest guideline (or, if this test truly
requires a unique return value, add a short comment above the inline mock
explaining why it must remain inline); locate the mock usage on
loadConfigFromFileMock in vite-config.test.ts and either initialize the same
Promise.resolve({ config: {}, path: '', dependencies: [] }) in the beforeEach
setup or add a one-line justification comment next to the mockReturnValueOnce so
reviewers understand this exception.
code/builders/builder-vite/src/types.ts (1)

24-24: Missing trailing semicolon for consistency.

Line 23 (viteConfigPath) has a semicolon, but line 24 (configLoader) does not. Add a semicolon for consistency with the rest of the codebase.

✏️ Suggested fix
-  configLoader?: 'bundle' | 'runner' | 'native'
+  configLoader?: 'bundle' | 'runner' | 'native';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/builders/builder-vite/src/types.ts` at line 24, The property declaration
for configLoader in types.ts is missing a trailing semicolon; update the
configLoader?: 'bundle' | 'runner' | 'native' declaration to include a trailing
semicolon to match the style of viteConfigPath and the rest of the file (i.e.,
add ";" at the end of the configLoader line).
🤖 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/builders/builder-vite/src/vite-config.test.ts`:
- Around line 92-131: The test that asserts configLoader is passed to
loadConfigFromFile is declared at top-level and should be moved inside the
existing describe('commonConfig') block to keep tests organized and use the same
setup/teardown; relocate the entire it('should pass configLoader option to
loadConfigFromFile', ...) block so it sits within the describe('commonConfig')
scope that contains the other commonConfig tests, ensuring it still references
commonConfig and loadConfigFromFileMock as before.

---

Nitpick comments:
In `@code/builders/builder-vite/src/types.ts`:
- Line 24: The property declaration for configLoader in types.ts is missing a
trailing semicolon; update the configLoader?: 'bundle' | 'runner' | 'native'
declaration to include a trailing semicolon to match the style of viteConfigPath
and the rest of the file (i.e., add ";" at the end of the configLoader line).

In `@code/builders/builder-vite/src/vite-config.test.ts`:
- Around line 11-15: The test's module mock for 'vite' uses vi.mock(...) without
the required spy option; update the vi.mock call for 'vite' to include the spy:
true option so the mock follows the repository guideline (i.e., call
vi.mock('vite', async (importOriginal) => ({ ... }), { spy: true });) while
keeping the existing overrides for loadConfigFromFile and
defaultClientConditions intact; ensure the vi.mock invocation that references
loadConfigFromFile and defaultClientConditions is the one updated.
- Around line 113-119: Move the inline mockReturnValueOnce call for
loadConfigFromFileMock into the test suite's beforeEach to follow the Vitest
guideline (or, if this test truly requires a unique return value, add a short
comment above the inline mock explaining why it must remain inline); locate the
mock usage on loadConfigFromFileMock in vite-config.test.ts and either
initialize the same Promise.resolve({ config: {}, path: '', dependencies: [] })
in the beforeEach setup or add a one-line justification comment next to the
mockReturnValueOnce so reviewers understand this exception.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 73ec27cc-b218-423c-b8fe-03161a4c5cc7

📥 Commits

Reviewing files that changed from the base of the PR and between f700486 and b25cdc2.

📒 Files selected for processing (5)
  • code/builders/builder-vite/src/types.ts
  • code/builders/builder-vite/src/vite-config.test.ts
  • code/builders/builder-vite/src/vite-config.ts
  • docs/_snippets/main-config-builder-configLoader.md
  • docs/builders/vite.mdx

Comment on lines +92 to +131
it('should pass configLoader option to loadConfigFromFile', async () => {
const optionsWithConfigLoader: Options = {
...dummyOptions,
presets: {
apply: async (key: string) =>
({
framework: { name: '' },
addons: [],
core: {
builder: {
name: '@storybook/builder-vite',
options: {
configLoader: 'native'
}
},
},
options: {},
})[key],
} as Presets,
};

loadConfigFromFileMock.mockReturnValueOnce(
Promise.resolve({
config: {},
path: '',
dependencies: [],
})
);

await commonConfig(optionsWithConfigLoader, 'development');

// Verify loadConfigFromFile was called with configLoader as the 6th argument
expect(loadConfigFromFileMock).toHaveBeenCalledWith(
expect.objectContaining({ command: 'serve' }),
undefined,
expect.any(String),
undefined,
undefined,
'native'
);
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.

🛠️ Refactor suggestion | 🟠 Major

Test should be inside a describe block for proper organization.

This test is defined at the top level (outside the describe('commonConfig') block ending at line 52). Since this test exercises commonConfig, it should be placed inside that describe block for consistency and proper test organization.

♻️ Suggested fix

Move the test inside the describe('commonConfig') block:

 describe('commonConfig', () => {
   it('should set configFile to false and include plugins', async () => {
     // ... existing test
   });
+
+  it('should pass configLoader option to loadConfigFromFile', async () => {
+    // ... test implementation
+  });
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/builders/builder-vite/src/vite-config.test.ts` around lines 92 - 131,
The test that asserts configLoader is passed to loadConfigFromFile is declared
at top-level and should be moved inside the existing describe('commonConfig')
block to keep tests organized and use the same setup/teardown; relocate the
entire it('should pass configLoader option to loadConfigFromFile', ...) block so
it sits within the describe('commonConfig') scope that contains the other
commonConfig tests, ensuring it still references commonConfig and
loadConfigFromFileMock as before.

@valentinpalkovic
Copy link
Copy Markdown
Contributor

Hi @holvi-sebastian

Thank you for the PR.

Vite 8 will no longer use esbuild under the hood, but instead uses a combination of Oxc transform tools and Rolldown. Very likely, the issue will be resolved in the future. Could you report back on whether you're encountering the same issues in the latest Vite 8 beta? If the issue is resolved, I don't think we will introduce a flag that only covers legacy versions of Vite.

@holvi-sebastian
Copy link
Copy Markdown
Author

As of vite@8.0.0-beta.18 the same issue occurs. I don't think this is a legacy option, and it isn't related to esbuild or rolldown... but it's more of a broader configuration on how vite handles dependencies in vite.config.ts.

I think the future plan is to default to native which would expect that node strip the types from .ts files. But migrating to node@25 is not an option for everyone and the runner option should stay relevant.

@valentinpalkovic
Copy link
Copy Markdown
Contributor

and it isn't related to esbuild or rolldown

My understanding is that there may have been a chance your specific issue was resolved, because in Vite 8, not esbuild, but rolldown is used to bundle the config into a temporary file and load it, and that rolldown may behave differently or be more advanced with TypeScript handling in config files.

I'll take a look at your PR. I think this change doesn't cover @storybook/addon-vitest and the Testing module, though. But don't worry. I'll take care of that.

@valentinpalkovic valentinpalkovic self-assigned this Mar 11, 2026
@valentinpalkovic valentinpalkovic changed the title builder-vite: support configLoader via builder options Builder-Vite: Support configLoader via builder options Mar 11, 2026
@valentinpalkovic
Copy link
Copy Markdown
Contributor

@holvi-sebastian

Your reproduction is incomplete and hard to follow. I've created a reproduction here: https://github.com/valentinpalkovic/config-loader-repo

But I actually can't reproduce the issue. packages/storybook/vite.config.ts imports import { foo } from "@monorepo/foobar" and the import succeeds, although @monorepo/foobar only exports an index.ts file.

Can you please take a look, create a fork, and change the reproduction so it reproduces the issue? Thanks!

@valentinpalkovic valentinpalkovic added the needs reproduction Indicates that a reproduction repository must be provided for this issue label Mar 11, 2026
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/addons/vitest/src/node/test-manager.ts`:
- Around line 64-65: The configLoader property is declared private in
TestManager but VitestManager accesses this.testManager.configLoader; change the
declaration of configLoader in test-manager.ts from private to public readonly
so it is accessible but immutable (i.e., update the member signature for
configLoader: TestManagerOptions['configLoader'] to be public readonly). Ensure
any constructor/assignment still assigns the value and update any references
assuming mutability if needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 145d94d3-780f-4d69-9f4b-35456022c45b

📥 Commits

Reviewing files that changed from the base of the PR and between b25cdc2 and 9d88c25.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (6)
  • code/addons/vitest/package.json
  • code/addons/vitest/src/node/boot-test-runner.ts
  • code/addons/vitest/src/node/test-manager.ts
  • code/addons/vitest/src/node/vitest-manager.ts
  • code/addons/vitest/src/node/vitest.ts
  • code/addons/vitest/src/preset.ts

Comment on lines +64 to +65
private configLoader?: TestManagerOptions['configLoader'];

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Declaration in TestManager:"
rg -n -C2 'configLoader' code/addons/vitest/src/node/test-manager.ts

echo
echo "External access from VitestManager:"
rg -n -C2 'testManager\.configLoader' code/addons/vitest/src/node/vitest-manager.ts

Repository: storybookjs/storybook

Length of output: 970


configLoader cannot be private if VitestManager reads it.

Line 127 in vitest-manager.ts accesses this.testManager.configLoader, but the member is declared private here. This violates TypeScript strict mode. Change it to public readonly.

Fix
-  private configLoader?: TestManagerOptions['configLoader'];
+  public readonly configLoader?: TestManagerOptions['configLoader'];
📝 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
private configLoader?: TestManagerOptions['configLoader'];
public readonly configLoader?: TestManagerOptions['configLoader'];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/addons/vitest/src/node/test-manager.ts` around lines 64 - 65, The
configLoader property is declared private in TestManager but VitestManager
accesses this.testManager.configLoader; change the declaration of configLoader
in test-manager.ts from private to public readonly so it is accessible but
immutable (i.e., update the member signature for configLoader:
TestManagerOptions['configLoader'] to be public readonly). Ensure any
constructor/assignment still assigns the value and update any references
assuming mutability if needed.

@holvi-sebastian
Copy link
Copy Markdown
Author

Done, at: valentinpalkovic/config-loader-repo#1

@valentinpalkovic valentinpalkovic removed the needs reproduction Indicates that a reproduction repository must be provided for this issue label Mar 19, 2026
@valentinpalkovic valentinpalkovic moved this to In Progress in Core Team Projects Mar 19, 2026
@valentinpalkovic
Copy link
Copy Markdown
Contributor

I want to get a second opinion from the team regarding this feature request:
@ndelangen What are your thoughts on this?

@valentinpalkovic
Copy link
Copy Markdown
Contributor

I've discussed it internally, and we want to proceed with this fix. I'll try to get CI green and then build a canary release to test the fix!

@valentinpalkovic
Copy link
Copy Markdown
Contributor

valentinpalkovic commented Mar 20, 2026

Fix verified in valentinpalkovic/config-loader-repo#1. Though, addon-vitest crashes. I'll take a further look!

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
code/addons/vitest/src/node/boot-test-runner.ts (1)

87-94: ⚠️ Potential issue | 🟡 Minor

Conditionally set STORYBOOK_CONFIG_LOADER only when defined.

When configLoader is undefined, omit STORYBOOK_CONFIG_LOADER from the spawned child's env object entirely. According to Node.js documentation, passing undefined values in the env option to spawn causes them to be ignored (not set), but explicitly omitting the key when undefined is clearer and avoids reliance on this behavior.

The downstream code in code/addons/vitest/src/node/vitest.ts (line 53) casts the env value directly: configLoader: process.env.STORYBOOK_CONFIG_LOADER as BuilderOptions['configLoader']. If not set, it will be undefined, which is the correct behavior.

Apply the fix
       env: {
         VITEST: 'true',
         TEST: 'true',
         VITEST_CHILD_PROCESS: 'true',
         NODE_ENV: process.env.NODE_ENV ?? 'test',
         STORYBOOK_CONFIG_DIR: normalize(options.configDir),
-        STORYBOOK_CONFIG_LOADER: configLoader,
+        ...(configLoader ? { STORYBOOK_CONFIG_LOADER: configLoader } : {}),
       },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/addons/vitest/src/node/boot-test-runner.ts` around lines 87 - 94, The
env object currently always includes STORYBOOK_CONFIG_LOADER with value
configLoader; change this so STORYBOOK_CONFIG_LOADER is only added when
configLoader is not undefined—e.g., build the env object for the spawn call and
conditionally add the key (using an if or a spread like ...(configLoader !==
undefined ? { STORYBOOK_CONFIG_LOADER: configLoader } : {})); update the code
around where env is constructed in boot-test-runner.ts so VITEST/TEST/etc remain
unchanged but STORYBOOK_CONFIG_LOADER is omitted entirely when configLoader is
undefined to ensure downstream code in vitest.ts receives undefined when not
set.
♻️ Duplicate comments (1)
code/addons/vitest/src/node/test-manager.ts (1)

64-64: ⚠️ Potential issue | 🔴 Critical

configLoader is read externally and cannot remain private.

VitestManager.startVitest reads this.testManager.configLoader (vitest-manager.ts L127), but this declaration is private, which TypeScript will reject. Switch to public readonly (or expose via a getter).

🔧 Fix
-  private configLoader?: TestManagerOptions['configLoader'];
+  public readonly configLoader?: TestManagerOptions['configLoader'];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/addons/vitest/src/node/test-manager.ts` at line 64, The field
configLoader on TestManager is declared private but is accessed externally
(VitestManager.startVitest reads this.testManager.configLoader), so change its
visibility to a public readonly field or add a public getter; update the
declaration "private configLoader?: TestManagerOptions['configLoader'];" to
"public readonly configLoader?: TestManagerOptions['configLoader'];" (or
implement a public get configLoader(): TestManagerOptions['configLoader'] {
return this._configLoader; }) and ensure any internal assignments use the
backing field if you choose the getter approach.
🧹 Nitpick comments (1)
code/addons/vitest/src/preset.ts (1)

87-89: Optional: simplify the configLoader derivation.

The current expression yields a false | 'bundle' | 'runner' | 'native' | undefined union (because of the &&), which is why both call sites need configLoader || undefined to launder the false. A direct ternary or optional chaining is clearer and avoids the type assertion.

♻️ Suggested simplification
-  const configLoader =
-    typeof core.builder !== 'string' &&
-    (core.builder?.options?.configLoader as BuilderOptions['configLoader']);
+  const configLoader: BuilderOptions['configLoader'] | undefined =
+    typeof core.builder === 'string' ? undefined : core.builder?.options?.configLoader;

…and then drop || undefined at the two runTestRunner({...}) call sites.

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

In `@code/addons/vitest/src/preset.ts` around lines 87 - 89, The expression
assigning configLoader currently uses && which produces a false | ... union;
change it to a clear conditional so configLoader is either the
BuilderOptions['configLoader'] value or undefined (for example use a ternary:
check typeof core.builder !== 'string' ? core.builder?.options?.configLoader :
undefined or an equivalent optional-chaining conditional) and then remove the
now-unnecessary `|| undefined` at the runTestRunner(...) call sites; update
references to the variable name configLoader and the call sites that pass it to
runTestRunner so they pass the value directly.
🤖 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/builders/builder-vite/src/vite-config.ts`:
- Around line 45-53: Detect when the user provided BuilderOptions.configLoader
and the installed Vite version is older than 6.1.0, and surface a clear runtime
error; inside vite-config.ts (where getBuilderOptions and loadConfigFromFile are
used and the local variable configLoader is passed into loadConfigFromFile),
read the installed Vite version (e.g. require('vite/package.json').version or
import) and use semver comparison (require('semver').lt or similar) to check if
version < 6.1.0; if so, throw or processLogger.error+process.exit(1) with a
short message explaining that configLoader (including 'native') requires Vite
>=6.1.0 and instruct the user to upgrade Vite or remove configLoader, otherwise
proceed to call loadConfigFromFile as currently implemented.

---

Outside diff comments:
In `@code/addons/vitest/src/node/boot-test-runner.ts`:
- Around line 87-94: The env object currently always includes
STORYBOOK_CONFIG_LOADER with value configLoader; change this so
STORYBOOK_CONFIG_LOADER is only added when configLoader is not undefined—e.g.,
build the env object for the spawn call and conditionally add the key (using an
if or a spread like ...(configLoader !== undefined ? { STORYBOOK_CONFIG_LOADER:
configLoader } : {})); update the code around where env is constructed in
boot-test-runner.ts so VITEST/TEST/etc remain unchanged but
STORYBOOK_CONFIG_LOADER is omitted entirely when configLoader is undefined to
ensure downstream code in vitest.ts receives undefined when not set.

---

Duplicate comments:
In `@code/addons/vitest/src/node/test-manager.ts`:
- Line 64: The field configLoader on TestManager is declared private but is
accessed externally (VitestManager.startVitest reads
this.testManager.configLoader), so change its visibility to a public readonly
field or add a public getter; update the declaration "private configLoader?:
TestManagerOptions['configLoader'];" to "public readonly configLoader?:
TestManagerOptions['configLoader'];" (or implement a public get configLoader():
TestManagerOptions['configLoader'] { return this._configLoader; }) and ensure
any internal assignments use the backing field if you choose the getter
approach.

---

Nitpick comments:
In `@code/addons/vitest/src/preset.ts`:
- Around line 87-89: The expression assigning configLoader currently uses &&
which produces a false | ... union; change it to a clear conditional so
configLoader is either the BuilderOptions['configLoader'] value or undefined
(for example use a ternary: check typeof core.builder !== 'string' ?
core.builder?.options?.configLoader : undefined or an equivalent
optional-chaining conditional) and then remove the now-unnecessary `||
undefined` at the runTestRunner(...) call sites; update references to the
variable name configLoader and the call sites that pass it to runTestRunner so
they pass the value directly.
🪄 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: 74789f56-89e0-4f45-a789-5f3c3a415085

📥 Commits

Reviewing files that changed from the base of the PR and between 088d952 and 895e009.

📒 Files selected for processing (8)
  • code/addons/vitest/package.json
  • code/addons/vitest/src/node/boot-test-runner.ts
  • code/addons/vitest/src/node/test-manager.ts
  • code/addons/vitest/src/node/vitest-manager.ts
  • code/addons/vitest/src/node/vitest.ts
  • code/addons/vitest/src/preset.ts
  • code/builders/builder-vite/src/vite-config.test.ts
  • code/builders/builder-vite/src/vite-config.ts
✅ Files skipped from review due to trivial changes (1)
  • code/addons/vitest/package.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • code/addons/vitest/src/node/vitest.ts
  • code/builders/builder-vite/src/vite-config.test.ts

Comment on lines +45 to +53
const { viteConfigPath, configLoader } = await getBuilderOptions<BuilderOptions>(options);

const projectRoot = resolve(options.configDir, '..');

// I destructure away the `build` property from the user's config object
// I do this because I can contain config that breaks storybook, such as we had in a lit project.
// If the user needs to configure the `build` they need to do so in the viteFinal function in main.js.
const { config: { build: buildProperty = undefined, ...userConfig } = {} } =
(await loadConfigFromFile(configEnv, viteConfigPath, projectRoot)) ?? {};
(await loadConfigFromFile(configEnv, viteConfigPath, projectRoot, undefined, undefined, configLoader)) ?? {};
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect builder-vite's Vite dependency range and any version checks.
fd -t f 'package.json' code/builders/builder-vite -x cat {}
echo '---'
# Look for any runtime Vite version guards in the package.
rg -nP --type=ts -C2 '\b(vite/package|VITE_VERSION|version)\b' code/builders/builder-vite

Repository: storybookjs/storybook

Length of output: 4225


🌐 Web query:

In which Vite version was the configLoaderoption (with values 'bundle' | 'runner' | 'native') added toloadConfigFromFile?

💡 Result:

Vite version 6.1.0

Citations:


🏁 Script executed:

# Check for any version guards or conditionals around configLoader in vite-config.ts
rg -n 'configLoader' code/builders/builder-vite/src/vite-config.ts -B3 -A3

# Check the full context of where configLoader is used
cat -n code/builders/builder-vite/src/vite-config.ts | sed -n '40,60p'

# Look for any version checks related to configLoader elsewhere
rg -n 'configLoader' code/builders/builder-vite/src --type ts

Repository: storybookjs/storybook

Length of output: 3127


🏁 Script executed:

# Check BuilderOptions type definition for any comments about configLoader version requirements
rg -n -A5 'configLoader' code/builders/builder-vite/src/types.ts

# Check if there's a minimum version requirement documented or enforced in peerDependencies
cat code/builders/builder-vite/package.json | grep -A2 -B2 'peerDependencies'

# Search for any changelog or recent commits mentioning configLoader
git log --oneline --all --grep='configLoader' code/builders/builder-vite 2>/dev/null | head -5 || echo "No git log available"

# Check if there are any version guards elsewhere in the codebase
rg 'version.*6\.1' code/builders/builder-vite --type ts

Repository: storybookjs/storybook

Length of output: 255


LGTM — clean pass-through, but version compatibility gap remains unresolved.

The change correctly threads configLoader from builder options into Vite's loadConfigFromFile as the 6th positional argument. When undefined (the default), prior behavior is preserved. The position and typing match the test in vite-config.test.ts and the type definition in types.ts.

However, configLoader (including the 'native' value) was added in Vite 6.1.0. The peer dependency range still permits Vite 5.0.0+, so users on Vite 5.x or 6.0.x who explicitly set configLoader: 'native' will have it silently ignored by Vite with no warning or error.

Either bump the minimum peer dependency to ^6.1.0 || ^7.0.0 || ^8.0.0, add a runtime version check with a helpful error message, or at minimum document in BuilderOptions that configLoader requires Vite 6.1.0+.

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

In `@code/builders/builder-vite/src/vite-config.ts` around lines 45 - 53, Detect
when the user provided BuilderOptions.configLoader and the installed Vite
version is older than 6.1.0, and surface a clear runtime error; inside
vite-config.ts (where getBuilderOptions and loadConfigFromFile are used and the
local variable configLoader is passed into loadConfigFromFile), read the
installed Vite version (e.g. require('vite/package.json').version or import) and
use semver comparison (require('semver').lt or similar) to check if version <
6.1.0; if so, throw or processLogger.error+process.exit(1) with a short message
explaining that configLoader (including 'native') requires Vite >=6.1.0 and
instruct the user to upgrade Vite or remove configLoader, otherwise proceed to
call loadConfigFromFile as currently implemented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

2 participants