storybookjs / storybook

Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation
https://storybook.js.org
MIT License
84.7k stars 9.33k forks source link

[Bug]: Different JSX runtime config for component library and Storybook fails (v7-alpha, Vite, React) #19722

Open zettadam opened 2 years ago

zettadam commented 2 years ago

Describe the bug

I want to create a library of React components and document them with Storybook. Since I use vite in library mode to build components I thought of using Storybook with vite builder. Storybook v7-alpha is using the same vite configuration, but in library mode I want to build my components with "classic" JSX runtime configured with @vitejs/plugin-react to avoid shipping JSX runtime with my component library--it's up to library consumers to configure it on their side.

Below is my vite.config.ts

/// <reference types="vite/client" />
/// <reference types="vitest" />

import { resolve } from 'path'
import { defineConfig } from 'vite'
import reactPlugin from '@vitejs/plugin-react'
import postcssPresetEnv from 'postcss-preset-env'

export default defineConfig({
  build: {
    lib: {
      entry: resolve(__dirname, 'src/index.ts'),
      formats: ['es'],
      fileName: '[name]',
    },
    // Let the library user control minification in their own bundler
    minify: false,
    rollupOptions: {
      external: ['react', 'react-dom'],
      output: {
        dir: 'dist',
        globals: {
          react: 'React',
          'react-dom': 'ReactDOM',
        },
        inlineDynamicImports: false,
        preserveModules: true,
      },
    },
    sourcemap: false,
    target: 'esnext',
  },
  css: {
    postcss: {
      plugins: [
        postcssPresetEnv({
          /* use stage 3 features + css nesting rules */
          stage: 3,
          features: {
            'nesting-rules': true,
          },
        }),
      ],
    },
  },
  plugins: [
    reactPlugin({
      // Exclude storybook stories
      exclude: /\.stories\.(ts|js|md)x?$/,
      include: '**/*.tsx',
      jsxRuntime: 'classic',
    }),
  ],
  resolve: {
    alias: {
      '@': resolve(__dirname, './src'),
    },
  },
  test: {
    coverage: {
      all: true,
      reporter: ['text', 'json', 'html'],
      include: ['src/**'],
      exclude: ['src/index.ts', '**/constants.ts'],
    },
    globals: true,
    environment: 'happy-dom',
    setupFiles: './setupTests.ts',
  },
})

However, this setting breaks Storybook and this is where having zero configuration approach is not working as expected or documented with async viteFinal () override in ./storybook/main.js (I use Typescript & .tsx files with library source files, but JS & .{jsx|mdx} files with stories).

Below is my ./storybook/main.js configuration:

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

export default {
  stories: ['../stories/**/*.stories.@(jsx|mdx)'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
  ],
  features: {
    // previewMdx2: true, // experimental
  },

  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
  core: {
    disableTelemetry: true,
  },
  async viteFinal(config) {
    // return the customized config
    return mergeConfig(config, {
      build: '',
      // customize the Vite config here
      plugins: [reactPlugin()],
      test: '',
    })
  },
}

I receive errors related to duplicate React/JSX runtime environments, deprecated Babel transform-react-jsx-self plugin, like:

[vite] Internal server error: /Users/sg0223770/Work/lab/vite-react-component-library-ts/stories/components/Button.stories.jsx: Duplicate __self prop found. You are most likely using the deprecated transform-react-jsx-self Babel plugin. Both __source and __self are automatically set when using the automatic runtime. Please remove transform-react-jsx-source and transform-react-jsx-self from your Babel config.

I recently filed a bug report related to "classic" JSX runtime in library mode with Vite project and it got fixed on their side. I was thinking that with async viteFinal() function I could override library @vitejs/plugin-react configuration, but I run into these issues.

Is there a documented and tested way of specifying different JSX runtime configuration for libraries and storybook re-/using the same vite.config.{js|ts} configuration?

To Reproduce

https://github.com/zettadam/vite-react-component-library-ts

System

I don't want to migrate :)

Additional context

My package.json:

{
  "name": "vite-react-component-library-ts",
  "private": true,
  "sideEffects": false,
  "type": "module",
  "exports": "./dist/index.js",
  "main": "./dist/index.js",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "scripts": {
    "audit": "npm audit --registry=https://registry.npmjs.org/",
    "build": "tsc && vite build && npm run dts",
    "build:tsup": "tsup",
    "clean": "rm -rf node_modules && rm -rf dist && rm -rf coverage",
    "dts": "tsup src/index.ts --dts-only --format esm",
    "lint": "eslint --ext .tsx,.ts lib/",
    "preversion": "npm run test && npm run build",
    "postversion": "git push --follow-tags",
    "prettier:check": "prettier --check lib/",
    "prettier:fix": "prettier --write",
    "storybook": "storybook dev -p 6006",
    "storybook:build": "storybook build",
    "test": "vitest run",
    "test:ci": "vitest run",
    "test:coverage": "vitest run --coverage",
    "test:ui": "vitest --ui",
    "test:watch": "vitest"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/zettadam/vite-react-component-library-ts.git"
  },
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/zettadam/vite-react-component-library-ts/issues"
  },
  "homepage": "https://github.com/zettadam/vite-react-component-library-ts#readme",
  "peerDependencies": {
    "react": "17.x || 18.x",
    "react-dom": "17.x || 18.x"
  },
  "devDependencies": {
    "@storybook/addon-essentials": "7.0.0-alpha.45",
    "@storybook/addon-interactions": "7.0.0-alpha.45",
    "@storybook/addon-links": "7.0.0-alpha.45",
    "@storybook/react": "7.0.0-alpha.45",
    "@storybook/react-vite": "7.0.0-alpha.45",
    "@storybook/testing-library": "0.0.13",
    "@testing-library/jest-dom": "5.16.5",
    "@testing-library/react": "13.4.0",
    "@types/node": "18.11.9",
    "@types/postcss-preset-env": "7.7.0",
    "@types/react": "18.0.24",
    "@types/react-dom": "18.0.8",
    "@vitejs/plugin-react": "2.2.0",
    "@vitest/ui": "0.24.5",
    "c8": "7.12.0",
    "eslint": "8.26.0",
    "eslint-plugin-react": "7.31.10",
    "eslint-plugin-react-hooks": "4.6.0",
    "happy-dom": "7.6.6",
    "postcss-preset-env": "7.8.2",
    "prettier": "2.7.1",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "storybook": "7.0.0-alpha.45",
    "ts-node": "10.9.1",
    "tsup": "6.3.0",
    "typescript": "4.8.4",
    "vite": "3.2.2",
    "vitest": "0.24.5"
  }
}
renato-bohler commented 1 year ago

Exact same problem here.

I found a dirty workaround, replacing react({ jsxRuntime: 'classic' }) (from vite.config.ts) with react():

// main.ts
import { StorybookConfig } from '@storybook/react-vite';
import react from '@vitejs/plugin-react';

const config: StorybookConfig = {
  core: {
    builder: '@storybook/builder-vite',
  },
  framework: {
    name: '@storybook/react-vite',
  },
  // ... other stuff
  viteFinal: (config) => ({
    ...config,
    plugins: config.plugins?.map((plugin) => {
      if (plugin?.[0]?.name === 'vite:react-babel') return react();
      return plugin;
    }),
  }),
};

export default config;

But yeah, I'm also interested in non-hacky way to separately configure the JSX Runtime for Storybook.

spsaucier commented 1 year ago

I'm getting this same error on Storybook React Native without Vite.

shilman commented 1 year ago

@spsaucier Perhaps file an issue on the RN repo? https://github.com/storybookjs/react-native

shilman commented 1 year ago

For anybody who's experiencing this, is it still a problem on 7.0?

Migration guide: https://storybook.js.org/migration-guides/7.0