reactjsstorybookrollupjsturborepo

Why am I getting this Storybook import error with RollupJS?


I have a turborepo with a react component library which I publish to NPM. Call it @myscope/react — it's bundled with CSS using RollupJS. And turbo has some good docs on publishing local packages here.

In the repo, I also have two apps, a website that uses the React component library and a Storybook. The components are imported correctly by the webapp, and TypeScript is happy even in the Storybook directory. But when Storybook runs, I get:

The requested module '/@fs/Users/{user}/mre/packages/react/dist/index.js' does not provide an export named 'MyComponent'

Why? I've looked at dist/index.js and it seems like it's exported, but is this a limitation of the module strategy that Storybook supports?

I've created a minimum reproduction of the error here.

.storybook/main.ts:

import type { StorybookConfig } from "@storybook/react-vite";

import { join, dirname } from "path";

/**
 * This function is used to resolve the absolute path of a package.
 * It is needed in projects that use Yarn PnP or are set up within a monorepo.
 */
function getAbsolutePath(value: string): any {
  return dirname(require.resolve(join(value, "package.json")));
}
const config: StorybookConfig = {
  stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
  addons: [
    getAbsolutePath("@storybook/addon-onboarding"),
    getAbsolutePath("@storybook/addon-links"),
    getAbsolutePath("@storybook/addon-essentials"),
    getAbsolutePath("@chromatic-com/storybook"),
    getAbsolutePath("@storybook/addon-interactions"),
  ],
  framework: {
    name: getAbsolutePath("@storybook/react-vite"),
    options: {},
  },
};
export default config;

Storybook package.json:

{
  "name": "my-storybook",
  "private": true,
  "version": "0.1.2",
  "type": "module",
  "scripts": {
    "dev": "storybook dev -p 6006",
    "build": "storybook build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview",
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build"
  },
  "dependencies": {
    "@myscope/react": "workspace:*",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "storybook": "^8.2.7"
  },
  "devDependencies": {
    "@chromatic-com/storybook": "^1.6.1",
    "@storybook/addon-essentials": "^8.2.7",
    "@storybook/addon-interactions": "^8.2.7",
    "@storybook/addon-links": "^8.2.7",
    "@storybook/addon-onboarding": "^8.2.7",
    "@storybook/blocks": "^8.2.7",
    "@storybook/react": "^8.2.7",
    "@storybook/react-vite": "^8.2.7",
    "@storybook/test": "^8.2.7",
    "@types/react": "^18.3.3",
    "@types/react-dom": "^18.3.0",
    "@typescript-eslint/eslint-plugin": "^7.15.0",
    "@typescript-eslint/parser": "^7.15.0",
    "@vitejs/plugin-react": "^4.3.1",
    "eslint": "^8.57.0",
    "eslint-plugin-react-hooks": "^4.6.2",
    "eslint-plugin-react-refresh": "^0.4.7",
    "eslint-plugin-storybook": "^0.8.0",
    "tsconfig": "workspace:*",
    "typescript": "^5.2.2",
    "vite": "^5.3.4"
  }
}

vite.config.ts:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
})

I have reviewed this discussion and I proceeded anyway. But I would love to at least understand why this doesn't work.

Edit

For my esteemed colleagues voting to close my question without comment due to insufficient debugging info when I have linked a minimum reproduction GitHub repo, here's a screenshot of the error I'm receiving at http://localhost:6006/?path=/docs/example-mycomponent--docs

enter image description here

I would expect the component to render as it does at localhost:3000

enter image description here


Solution

  • The problem is your apps/storybook/package.json file uses "type": "module" and is built with vite so defaults to using ES modules, but your packages/react/rollup.config.js specifies a build output of format: 'cjs'.

    All you have to do is change to format: 'es' and your problem goes away.

    So you have to decide if you still want your previous cjs build, or es, or a dual build of packages/react.

    Changing to format: 'es' is the simplest path. A dual build would be the next simplest, but I would recommend using exports in the packages/react/package.json to better support that. You can also probably get vite to support commonjs by adding a babel preset to the @vitejs/plugin-react configuration but I wouldn't take this path.

    Dual build is the most versatile, but you probably really don't need CJS either.