恐怖!storybook x viteでbuildコケまくり男
storybook v7と vite でレアな(?)バグを踏み抜き、平日全ての時間をドブに捨てたので紹介します ♪
ちなみに vite 以外でも、webpack の define プラグインとかでも同様のものが発生しうると思います。
概要
バグは突如 npx sb init --builder=vite でセットアップしたプロジェクトで発生しました。
storybook build でstatic export してデプロイを試みたところ以下に遭遇しました。
(ちなみに storybook dev は機嫌よく動いていた)
[commonjs--resolver] Unexpected token (10:8) in /Users/XXXX/YYYY/node_modules/doctrine/lib/utility.js
file: /Users/XXXX/YYYY/node_modules/doctrine/lib/doctrine.js:10:8
8: 'use strict';
9:
10: var "0.3.10";
^
11:
12: VERSION = require('../package.json').version;
=> Failed to build the preview
SyntaxError: Unexpected token (10:8) in /Users/XXXX/YYYY/node_modules/doctrine/lib/utility.js
原因(にたどり着くまで)
このエラーについて、GitHubやインターネット中を探し回りましたが何も情報が無く、自力でどうにかするしかないようでした。
エラーメッセージでは、doctrine というnpm package内で Unexpected token でコケているようなので、この実装である eslint/doctrine をあたってみました。
以下がエラー箇所付近の実装です。
(function () {
'use strict';
var VERSION;
VERSION = require('../package.json').version;
exports.VERSION = VERSION;
変わった記述はありませんが、 var が気になりますね。
エラーの内容としては、この VERSION が何かしらのタイミングで文字列になってしまい、変数として処理できず Unexpected token になっているようです。
ところで、eslint/doctrine はメンテナンスが終了しています。3.0.0 が最後のリリースのようです。
https://github.com/eslint/doctrine/releases/tag/v3.0.0
… と、先程のエラーを思い出すと違和感に気づかれたかもしれません。
8: 'use strict';
9:
10: var "0.3.10";
"0.3.10" になってないか???
え、なんで 3.0.0 じゃなくて 0.3.10 が入るの!?となりますよね。
筆者はこのエラーに遭遇して8時間経過してから気づいたので、多分読者の皆様のほうが聡明です。
文字列になってしまってることも変ですが、せめて 3.0.0 で上書きされていてほしいものです。
ちなみに 0.3.10 とは、筆者がstorybookを使用している環境のpackage.jsonのバージョンです。
storybookのビルドに使用している vite の config を見てみます。
import path from "path";
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";
import svgr from "vite-plugin-svgr";
import pkg from "./package.json";
/** @type {import("vite").UserConfig} */
const config = {
build: {
lib: {
entry: path.resolve(__dirname, "src/index.ts"),
formats: ["es", "cjs"],
fileName: (format) => `${format}.js`,
},
outDir: path.resolve(__dirname, "./dist"),
sourcemap: true,
emptyOutDir: false,
rollupOptions: {
external: [...Object.keys(pkg.peerDependencies || {}), "react/jsx-runtime"],
output: {
globals: {
react: "React",
"react/jsx-runtime": "react/jsx-runtime",
"react-dom": "ReactDOM",
},
},
},
},
define: {
VERSION: JSON.stringify(pkg.version),
},
plugins: [tsconfigPaths(), react(), svgr()],
};
export default defineConfig(config);
define に明らかに怪しいやつがいますね。
// ~~~
define: {
VERSION: JSON.stringify(pkg.version),
},
viteの define で、ライブラリのバージョンを取れるように VERSION プロパティを宣言していたところ、こいつが storybook build の際に doctrine 内部の VERSION を文字列で上書きした結果、冒頭のエラーが発生したようでした。
解決方法
defineで VERSION を宣言するのをやめました。怒りのコメント付きです。
define: {
/**
* storybook build 時に読み込まれる doctrine というパッケージ内に var VERSION という記述があり
* これと同じ名前のプロパティを定義すると衝突するため、VERSION ではなく PKG_VERSION とする。
* @see https://github.com/eslint/doctrine/blob/0e8eba7f80b89cc8185541dda4e90c961d1d3553/lib/utility.js#L10
*/
PKG_VERSION: JSON.stringify(pkg.version),
},
おわりに
変数の名前に気をつけなさい、それはいつか衝突するから。
まあ特に var は今回関係なかったですね。人の書いたJSで見つけたら過剰反応してしまいがちです。
おしまいです。