I'm not entirely sure what the root cause is, but I encountered this issue after upgrading Vite to v6. It seems that Storybook is attempting to list stories within node_modules
. (For context, I'm using TypeScript Project References with PNPM workspaces and NX.)
Fortunately, there's a workaround. We can pass a function—like the findStories
function below—to the stories
property in StorybookConfig
(.storybook/main.ts). This ensures that node_modules
is excluded, since simply adding a negation glob pattern didn't work for me.
You could write a simpler function using the glob
npm package if you'd like. Nevertheless, this fixed the issue for me. Let me know if it helps?
find-stories.ts
import { readdir } from 'fs/promises'
import { join } from 'path'
/**
* Recursively walks through directories and yields file paths matching the RegExp.
* @param {string} dir - Directory to search in.
* @param {RegExp} regex - Regular expression to match file names.
* @param {Set<string>} ignoredDirs - Directories to ignore.
*/
async function* walkDir(
dir: string,
regex: RegExp,
ignoredDirs: Set<string>
): AsyncGenerator<string> {
let entries
try {
entries = await readdir(dir, { withFileTypes: true })
} catch (error) {
console.warn(`Skipping directory due to error: ${dir}`, error)
return
}
for (const entry of entries) {
const fullPath = join(dir, entry.name)
if (entry.isDirectory() && !ignoredDirs.has(entry.name)) {
yield* walkDir(fullPath, regex, ignoredDirs)
} else if (entry.isFile() && regex.test(entry.name)) {
yield fullPath
}
}
}
const STORIES_REGEX = /\.stories\.(jsx?|tsx?)$/i
const IGNORED_DIRECTORIES = new Set(['node_modules', 'dist', 'build', 'out', 'out-tsc'])
export async function findStories(
rootDir: string = join(__dirname, '../../../') // point to your workspace root
): Promise<string[]> {
const files = []
for await (const file of walkDir(rootDir, STORIES_REGEX, IGNORED_DIRECTORIES)) {
files.push(file)
}
return files
}
.storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite'
import { findStories } from './find-stories'
const config: StorybookConfig = {
stories: async () => await findStories(),
addons: ['@storybook/addon-essentials'],
framework: {
name: '@storybook/react-vite',
options: {
builder: {
viteConfigPath: 'vite.config.ts',
},
},
},
core: {
builder: '@storybook/builder-vite',
},
typescript: {
reactDocgen: 'react-docgen-typescript',
},
async viteFinal(config) {
const { mergeConfig } = await import('vite')
return mergeConfig(config, {
build: {
commonjsOptions: { transformMixedEsModules: true },
},
})
},
}
export default config