概要
Storybookを使ってみようと思ったらwebpackが遅くてつらかったので,Vite版のbuilderを使おうとしたら沼った。
ここら辺はまだまだ発展途上っぽいのでそのうちこの記事も参考にならなくなりそう。
コード
説明することもないので実際のコードを晒す。
ターゲット
今回バンドルしたいファイルはsrc/main.ts
。
こんな感じになっている。
export { default as Button } from "components/atoms/Button"
ちなみにButton.tsx
はこんな感じ。
import { FC } from "react"
type ButtonProps = {
text: string
handler: React.MouseEventHandler<HTMLButtonElement>
primary: boolean
}
const Button: FC<ButtonProps> = ({ text, handler, primary }) => {
return (
<button
css={{
backgroundColor: primary ? "red" : "blue",
}}
onClick={handler}>
{text}
</button>
)
}
export default Button
package.json
{
...
"exports": {
".": {
"import": "./dist/main.js",
"types": "./types/main.d.ts"
}
},
"files": [
"dist",
"types"
],
"scripts": {
"build": "tsc && vite build",
"build-storybook": "build-storybook",
"storybook": "start-storybook -p 6006"
},
"type": "module"
}
tsconfig.json
型定義のみを出力するようにしている。
noEmit
は無効化する。
{
"compilerOptions": {
...
"emitDeclarationOnly": true,
"declaration": true,
"declarationMap": true,
"declarationDir": "./types",
"jsx": "react-jsx",
"jsxImportSource": "@emotion/react",
"baseUrl": "./src"
}
}
vite.config.js
https://github.com/emotion-js/emotion/issues/2853 が参考になった(というかこれが本質情報)。
本当にありがとうございます。
import { resolve } from "node:path"
import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
import tsconfigPaths from "vite-tsconfig-paths"
import { dependencies } from "./package.json"
const deps = Object.keys(dependencies)
const re = new RegExp(`^(${deps.join("|")})($|/)`)
const external = name => re.test(name)
export default defineConfig({
plugins: [tsconfigPaths(), react({ jsxImportSource: "@emotion/react" })],
build: {
lib: {
entry: resolve(__dirname, "src/main.ts"),
formats: ["es"],
fileName: "main",
},
rollupOptions: { external },
},
})
main.cjs
Storybookのエントリー的なやつ。
Viteを使うのにvite.config.jsを読まないマジキチ仕様なライブラリなのでカオスなことになっている(2022年8月現在)。
const { resolve } = require("node:path")
const { loadConfigFromFile, mergeConfig } = require("vite")
const filter = [
"vite:react-babel",
"vite:react-jsx",
"vite:react-refresh",
]
const filterPlugins = obj => {
if(Array.isArray(obj))
return obj
.map(filterPlugins)
.filter(v => Array.isArray(v) ? v.length : v)
if(filter.includes(obj.name))
return undefined
return obj
}
module.exports = {
...
core: {
builder: "@storybook/builder-vite",
},
viteFinal: async orig => {
const path = resolve(__dirname, "../vite.config.ts")
const { config } = await loadConfigFromFile(path)
orig.plugins = filterPlugins(orig.plugins)
return mergeConfig(orig, config)
},
}
まとめ
つらい