79446603

Date: 2025-02-17 21:24:12
Score: 0.5
Natty:
Report link

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
Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • Ends in question mark (2):
Posted by: Moa