【解決談】React & ViteのstorybookをGitHub PagesにデプロイしたらCanvasが真っ白だった話
問題
個人でReactアプリを書くときによく使う汎用レイアウトコンポーネントを、どこからでも呼び出せるようにパッケージ化。
意気揚々とstorybookでドキュメントも整備したのだが、ローカルが正常に表示されているものがデプロイした途端まったく表示されず。
表示されないと言えば語弊がある。
厳密に言えば、storybookのCanvas・Docs部分だけが綺麗に真っ白だった。
かなりトリッキーな方法で解決したので、同様の事例があるのかはわからないがとりあえずメモしておく。
問題が起きた環境
前にもstorybookのgh-pagesデプロイは経験しているのだが、その時は何事もなかった。
変わったことと言えば、@storybook/builder-viteを導入したくらい。
前回から変わったことと言えば、
- @storybook/builder-viteを導入
- ライブラリ公開のため、reactとreact-domをpeerDependenciesに設定
しかし、storybook-staticに吐き出された静的サイトをnpx http-server storybook-static
でプレビューしたところ問題なく表示されたので、決してビルドに失敗しているわけではないのだ。
Failed to load resource
問題1.デプロイした真っ白サイトで開発者ツールを覗くと、なんらかのファイルに対してFailed to load resource
エラーが吐き出されていた。
これに関しては、storybook-static直下に.nojekyll
という空ファイルを設置することで見事解決した。
ERR_ABORTED 404
問題2. ところが、今度はERR_ABORTED 404
エラーが吐き出されるように。
しかし状況は大きく進展していて、今度はどのファイルでエラーが発生しているのかが明示されるようになったので、ここで原因が判明する。
原因
ERR_ABORTED 404
エラーは、https://USERNAME.github.io/assets/iframe.*.js
に対して起こっていたのだが、まあ当たり前である。
GitHub PagesのルートURLはhttps://USERNAME.github.io/REPO
なのだから、正しいiframe.*.js
のURLはhttps://USERNAME.github.io/REPO/assets/iframe.*.js
であるはずだ。
assets/iframe.*.js
はiframe.html
から呼び出されているため、iframe.html
の
<script type="module" crossorigin src="/assets/iframe.b0087190.js"></script>
の部分を、
<script type="module" crossorigin src="/REPO/assets/iframe.b0087190.js"></script>
に修正してやれば問題は解決するだろう。
ビルドの設定でなんとかならないものかと調べていろいろと試してみたものの、vite.config.ts
や.storybook/main.cjs
の改変では状況がびくともしなかったので、最終的に諦めてパス修正スクリプトを書くことにした。
最終的なビルドスクリプト
bashで書くと癖の強い文法やpermission deniedが面倒なので、最近はTypeScriptでbashと同様のスクリプトが書けるzxがお気に入り。
#!/usr/bin/env zx
/* eslint-disable no-use-before-define */
/* eslint-disable no-undef */
import 'zx/globals'
const REPO_NAME = 'polym-generic-layout' // 今回の場合
// ビルド
await $`yarn build-storybook`
// ビルドされたものの修正
await within(async () => {
cd('storybook-static')
// .nojekyllファイルを作成
await $`touch .nojekyll`
// iframe.htmlがassets/iframe.*.jsを読み込めるように修正
const iframehtml = await fs.readFile('iframe.html', 'utf-8')
await fs.writeFile(
'iframe.html',
iframehtml.replace(/\/assets/g, `/${REPO_NAME}/assets`)
)
// assets内の*.jsや*.map.jsファイルにも同様のパス修正が必要
const assetjs_path = await glob('assets/*.@(map|js)')
await assetjs_path.forEach(async (path) => {
const assetjs = await fs.readFile('./' + path, 'utf-8')
await fs.writeFile(
'./' + path,
assetjs.replace(/assets\//g, `${REPO_NAME}/assets/`)
)
})
})
// キャッシュ削除
await $`rm -rf node_modules/.cache/gh-pages`
// デプロイ
await $`gh-pages -d storybook-static`
動かすための依存関係やらスクリプトやらはこんな感じ。(...
は割愛部分)
{
...
"repository": {
"type": "git",
"url": "git+https://github.com/USER/REPO.git"
},
"bugs": {
"url": "https://github.com/USER/REPO/issues"
},
"homepage": "https://USER.github.io/REPO",
...
"scripts": {
...
"build-storybook": "build-storybook -s STATIC_FILES_PATH",
"deploy-story": "vite-node ./script/deploy.zx.ts"
},
...
"devDependencies": {
...
"gh-pages": "^4.0.0",
"vite-node": "^0.23.4",
"zx": "^7.0.8"
},
...
}
UpperCase(全大文字)で書かれているところは適宜置き換えてください。
-
USER
はGitHubのユーザ名 -
REPO
はリポジトリ名 -
STATIC_FILES_PATH
は、storybookで表示したい画像等が置かれているフォルダのパス(例えば私の場合はsrc/stories/assets
)
yarn deploy-story
でデプロイしてみると、コンソールを開いても何一つ警告が表示されることなく、正常に表示されました✌️
追記(.storybook/main.*の設定)
.storybook/main.(js|cjs|ts)
を次のように設定したら、パスの修正は不要になりました。
+ const path = require('path')
+ const { loadConfigFromFile, mergeConfig } = require('vite')
module.exports = {
stories: [
...
],
addons: [
...
],
framework: '@storybook/react',
core: {
builder: '@storybook/builder-vite'
},
features: {
storyStoreV7: true
},
+ async viteFinal(config, { configType }) {
+ return mergeConfig(config, {
+. // リポジトリ名を設定
+ base: '/REPO/',
+ plugins: []
+ })
+ }
}
しかし相変わらず.nojekyll
という空ファイルは必要な模様。
そんなわけで、ビルドスクリプトはこれで良さそう。
#!/usr/bin/env zx
/* eslint-disable no-use-before-define */
/* eslint-disable no-undef */
import 'zx/globals'
// ビルド
await $`yarn build-storybook`
// storybook-staticディレクトリ内での処理
await within(async () => {
cd('storybook-static')
// .nojekyllファイルを作成
await $`touch .nojekyll`
})
// gh-pages実行時にエラーが出た場合はアンコメント
//await $`rm -rf node_modules/.cache/gh-pages`
// デプロイ
await $`gh-pages -d storybook-static`
Discussion