社内ではドキュメントの共有に Scrapbox が活発に使われており、するといきおい UserCSS や UserScript もさかんである。具体的には、/customize という共有のプロジェクトがあってみんなの自慢の装飾やカスタマイズが共有されている。これを個々人で import
して使うんである。
こんな感じ。
自分の場合は /motemen/UserCSS/common に常に適用したいスタイルを書いておいて各プロジェクトから読み込んでいる。このページからさらに、共有プロジェクトや他人の個人プロジェクトページからよさそうな設定を import
している次第。
つまりは多段インポート。こういうことを続けていると、だんだんと読み込みの遅さが気になってくる。こういうのはバンドルすればいいのだけど、巷のツールを普通に使うことはできない。インポートしてるリソースに認証がかかっているからだ。
とするとブラウザの上でバンドラを動かしたくなる。認証情報の利用やオリジン越えが発生するので、今回は拡張で実装することにした。
バンドラには esbuild を利用する。別に何でもいいのだけど、WebAssembly で動かす API があるというので触ってみたかった。
chrome-extension-esbuild
GitHub - motemen/chrome-extension-esbuild
この拡張は DevTools にパネルを一枚追加する。バンドルしたい JS や CSS を開いてインスペクタを開き、esbuild タブから Build ボタンを押せばリソースを解決してバンドルしてくれる。
URL.createObjectURL()
で生成した URL は Chrome だと名前を付けて保存できなかったので、data: URL を作っている。長さに制限があるかもしれない。
Manifest V3 で WebAssembly を使う
Chrome 拡張はそろそろ Manifest V3 というやつに移行するようアナウンスされているのでこれを使う。
通常の popup 内で WebAssembly を使うことはできず、sandbox 内で呼び出す必要があるっぽい。manifest.json にこういう指定をする必要があった。
"content_security_policy": { "sandbox": "sandbox allow-scripts; script-src 'self' 'unsafe-eval'; worker-src blob:" },
esbuild のプラグインを書く
あとは普通に sandbox 内で esbuild の API を呼び出せばいい。import
される JS や CSS を fetch()
経由で読み込むためプラグインを定義するが、これはドキュメントにある HTTP Plugin がほぼそのまま使えた。認証情報を利用した fetch は sandbox 内で行えないので親フレームに依頼する。
import した先でさらに別の import がなされていることもあるので、URL を resolveDir に含めておき、その後の相対パスの解決に利用している。
ログは content script で吐いている
最初、パネル内にログを表示しようとしていたのだけど見た目を作るのが面倒になってしまった。考えたら DevTools を開いてるんだからそこに流したらいいじゃん、ということで content script 経由で console.log()
することにした。DevTools を開いたときだけスクリプトを注入するなどしたほうがいいのかもしれない。このへんは secretlint/webextension を参考にしている。