恐怖!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で見つけたら過剰反応してしまいがちです。

    おしまいです。

    この記事を共有する