Addon-Vitest: Improve config file detection in monorepos#33814
Conversation
📝 WalkthroughWalkthroughEnhanced Vitest config discovery in monorepos by implementing logic that traverses from the Storybook configDir's parent directory toward the project root, searching for config files (vitest.workspace., vitest.config., vite.config.*) containing the 'storybookTest' string to determine the correct config path. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
✨ Finishing touches
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@code/addons/vitest/src/node/vitest-manager.ts`:
- Around line 96-105: The loop over configFiles that uses find.any and then
readFileSync (within the block referencing maybe and setting
vitestWorkspaceConfig) should guard against unreadable files by wrapping the
readFileSync call in a try/catch; on error (EACCES, EISDIR, or other fs errors)
skip that candidate and continue the loop instead of throwing, ensuring
vitestWorkspaceConfig is only set when the file is successfully read and
contains 'storybookTest'. Keep the check for existsSync as-is but add the
try/catch around readFileSync and continue on failure to match existing
error-handling patterns in this module.
| for (const file of configFiles) { | ||
| const maybe = find.any([file], { cwd: packageRoot, last: getProjectRoot() }); | ||
| if (maybe && existsSync(maybe)) { | ||
| const content = readFileSync(maybe, 'utf8'); | ||
| if (content.includes('storybookTest')) { | ||
| vitestWorkspaceConfig = maybe; | ||
| break; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, locate and examine the vitest-manager.ts file
find . -name "vitest-manager.ts" -type fRepository: storybookjs/storybook
Length of output: 112
🏁 Script executed:
# Check the file size and read the relevant section
wc -l code/addons/vitest/src/node/vitest-manager.ts
head -150 code/addons/vitest/src/node/vitest-manager.ts | tail -80Repository: storybookjs/storybook
Length of output: 3244
🏁 Script executed:
# Look for how readFileSync is used elsewhere in this file and error handling patterns
rg -n "readFileSync|try.*catch|existsSync" code/addons/vitest/src/node/vitest-manager.ts -A 2 -B 2Repository: storybookjs/storybook
Length of output: 687
🏁 Script executed:
# Check for similar config discovery patterns in the vitest addon
fd "\.ts$" code/addons/vitest/src/node/ --exec rg -l "readFileSync|find.any" {} \;Repository: storybookjs/storybook
Length of output: 110
🏁 Script executed:
# Look at the imports and overall structure to understand error handling approach
head -50 code/addons/vitest/src/node/vitest-manager.tsRepository: storybookjs/storybook
Length of output: 2019
🏁 Script executed:
# Check if there's any error handling in similar patterns within the file
rg -n "try|catch" code/addons/vitest/src/node/vitest-manager.ts | head -20Repository: storybookjs/storybook
Length of output: 542
🏁 Script executed:
# Verify imports to confirm readFileSync is from node:fs
head -30 code/addons/vitest/src/node/vitest-manager.ts | grep -E "import|readFileSync"Repository: storybookjs/storybook
Length of output: 799
🏁 Script executed:
# Check if there are other config discovery patterns in the codebase that handle this
rg -B 3 -A 3 "find\.any.*readFileSync|existsSync.*readFileSync" code/addons/vitest/Repository: storybookjs/storybook
Length of output: 334
Guard config discovery against unreadable files.
The existsSync check only verifies file existence, not readability. A file can become unreadable between the existence check and readFileSync call (race condition, permission change), or fail with EACCES/EISDIR. Wrap the read in try/catch to gracefully skip unreadable candidates and continue the search, aligning with the error handling patterns already used throughout this file.
if (maybe && existsSync(maybe)) {
- const content = readFileSync(maybe, 'utf8');
- if (content.includes('storybookTest')) {
- vitestWorkspaceConfig = maybe;
- break;
- }
+ try {
+ const content = readFileSync(maybe, 'utf8');
+ if (content.includes('storybookTest')) {
+ vitestWorkspaceConfig = maybe;
+ break;
+ }
+ } catch {
+ // Skip unreadable config candidates and continue searching
+ }
}🤖 Prompt for AI Agents
In `@code/addons/vitest/src/node/vitest-manager.ts` around lines 96 - 105, The
loop over configFiles that uses find.any and then readFileSync (within the block
referencing maybe and setting vitestWorkspaceConfig) should guard against
unreadable files by wrapping the readFileSync call in a try/catch; on error
(EACCES, EISDIR, or other fs errors) skip that candidate and continue the loop
instead of throwing, ensuring vitestWorkspaceConfig is only set when the file is
successfully read and contains 'storybookTest'. Keep the check for existsSync
as-is but add the try/catch around readFileSync and continue on failure to match
existing error-handling patterns in this module.
|
View your CI Pipeline Execution ↗ for commit b09081a
☁️ Nx Cloud last updated this comment at |
…le-detection Addon-Vitest: Improve config file detection in monorepos (cherry picked from commit d7c87fa)
Closes #33805
What I did
Fixed Vitest config file detection in
@storybook/addon-vitestso it works correctly in monorepo sub-packages.Problem
When using
@storybook/addon-vitestinside a monorepo sub-package (e.g../packages/web-app), the Vitest backend fails to start with the error: "No projects found for [project-name]".This happens because the addon's
VitestManager.startVitest()searches for Vitest config files usingfind.any()starting fromprocess.cwd()up togetProjectRoot(). In a monorepo, both can resolve to the monorepo root (where.gitlives), so avitest.config.tslocated in the sub-package directory is never discovered. This scenario happens ifstorybookis called from the root with--config-dirpointing to a sub-directory.Fix
Three changes in
code/addons/vitest/src/node/vitest-manager.ts:Start config search from the package root instead of
process.cwd()— The StorybookconfigDir(e.g.packages/web-app/.storybook) already identifies the sub-package. We derive the package root asdirname(resolve(configDir))and pass it as thecwdtofind.any(), which then traverses upward togetProjectRoot(). This ensures configs in both sub-packages and the monorepo root are found.Validate found configs contain
storybookTest— When multiple config files exist in the search path, the code now reads each candidate and checks if it contains thestorybookTestplugin reference. This prevents picking up unrelated Vite/Vitest configs that don't include the Storybook test plugin. The search also now includesvite.config.*files in addition tovitest.config.*andvitest.workspace.*.Improved
rootfallback — When no matching config is found, the Vitestrootfalls back topackageRoot(the sub-package directory) instead ofprocess.cwd()(the monorepo root).Behavior in different scenarios
Checklist for Contributors
Testing
The changes in this PR are covered in the following automated tests:
Manual testing
Caution
This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!
Reproduction: https://github.com/valentinpalkovic/valentinpalkovic-turbo-repo-reproduction-no-projects-found
pnpm ipnpm storybookDocumentation
MIGRATION.MD
Checklist for Maintainers
When this PR is ready for testing, make sure to add
ci:normal,ci:mergedorci:dailyGH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found incode/lib/cli-storybook/src/sandbox-templates.tsMake 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-33814-sha-b09081a7. Try it out in a new sandbox by runningnpx storybook@0.0.0-pr-33814-sha-b09081a7 sandboxor in an existing project withnpx storybook@0.0.0-pr-33814-sha-b09081a7 upgrade.More information
0.0.0-pr-33814-sha-b09081a7valentin/improve-config-file-detectionb09081a71770887038)To request a new release of this pull request, mention the
@storybookjs/coreteam.core team members can create a new canary release here or locally with
gh workflow run --repo storybookjs/storybook publish.yml --field pr=33814