?vscode-coi=on を使わないで WebContainer を利用できる拡張機能を作ったけど、デフォルトが変わりそうで少し涙目な話
vscode.dev でもターミナルを使いたくなったので、WebContainer を利用する拡張機能を作ろうと思ったのが 4 月の末頃。
WebView を利用すれば対応できるかと考えていたが、WebContainer は Cross Origin isolation(coi) を必要とするのが難点。
- vscode.dev のデフォルトでは coi が有効になっていない
- クエリーパラメーターに
?vscode-coi=on
を指定すると coi になるが、多くの場合で外部の画像などが表示できなくなる
外部の画像が表示されないのでは困りそうなので試行錯誤してみたところ、回避する方法がわかってきたので拡張機能として実装してみた。
図 1 こういう感じで利用できている
そして、当初の予定ではマーケットプレイスに publish した後、「いやー、苦労したっすよ(ドヤ顔)」って記事を書くつもりでいたら、Run WebAssemblies in VS Code for the Web によるとデフォルトが ?vscode-coi=on
へ変わるのかもという話。
?vscode-coi=on
を使わないで WebContainer を利用する方法
今回は Nodebox を利用することにより coi を有効化した外部タブを作りました。
Nodebox もブラウザー上で Node.js 的なランタイムを提供するものですが、こちらは coi を必要としません[1]。
- WebView で Nodebox のランタイムを動かす
- Nodebox のランタイムで Vite と WebSocket のサーバーを動かす
- Vite サーバーは coi を有効にしておき WebContainer ランタイムを動かすページを配信する
- 外部タブを開き Vite サーバーへ接続し WebContainer のランタイムを動かす
- 外部タブと WebView は WebSocket と Nodebox の API(stdio)経由で通信する
このようにして coi が有効化された外部タブを作ることにより、coi 無しの vscode.dev と WebContainer が通信できる状態になります。 (実際には postMessage とのすり合わせとかいろいろあるのですがその辺は割愛します)
使ってみる
上記を実装したのが Start WebContaner 拡張機能となります。
リポジトリを開いてコマンドパレットから Start WebContainer: Start
を実行すると外部タブが開き WebContainer が開始されます。
Workspace のファイルは WebContaner 側へロードされているので、あとは、Jsh 用のターミナルで普通に npm ci
してからファイルを編集するような感じです。
図 2-1 WebContaine を開始し npm ci
を実行
図 2-2 エディターでの編集は WebContainer 側へ反映される
リポジトリを用意するのは面倒という場合、ローカルのフォルダーも利用できます。空のフォルダーを開いた後に Start WebContainer: Start
でターミナルを開き、npm create next-app@latest
などでプロジェクトを作成するといった感じです。
図 2-3 空のフォルダーを開き、npm create
を実行
作ったプロジェクトを編集する場合は、コマンドパレットから Start WebContainer: Pick up all files from a container
を実行します。これで各ファイルが Workspace に取り込まれます。
図 2-4 作成したファイルを取り込み、エディターで開く
と、ここまでは良い感じですが、いくつかやりにくいところもあります。
-
Nodebox と WebContainer のランタイムを展開するので重い
-
バックグラウンドでのファイル同期がない
- WebContianer の FileSystemAPI に watch 的なものがないのと同期の処理は大変なので実装していません
-
NPM スクリプトやタスクを UI から利用できない
-
Platform 別のバインディングが必要な場合は動かないこともある[2]
-
node_modules
が同期されてないなどから定義の参照などができない -
時々ターミナルが反応しなくなってしてしまう
こんな感じで少し扱いにくいところもありますが、気楽にファイルを編集しながらターミナルで npm
も使えるのでわりと気に入っています。
on
になっている
Insiders 版のデフォルトは 今回作った拡張機能はわりと気に入ったので、未実装な機能の補完や安定化を進めようと思っていました。ですが README を記述するために vscode-coi
関連のことを検索していると vscode.dev で coi がデフォルトになりそうな記事がヒットしました。
Run WebAssemblies in VS Code for the Web によると insiders.vscode.dev では既にデフォルトが ?vscode-coi=on
に変更されたようです[3](wasm-wasi のために変更したのか不明ですが、他に記述が見つからないのでここから引用しています)。
The only difficulty with this approach is that
SharedArrayBuffer
andAtomics
require the site to be cross-origin isolated, which, because CORS is very viral, can be an endeavor by itself. This is why it is currently only enabled by default on the Insiders version insiders.vscode.dev and must be enabled using the query parameter?vscode-coi=on
on vscode.dev
実際に試してみてもそのような挙動になっています。
下記の検証は Cross-Browser support with Cross-Origin isolation で利用されている画像を用いて行っています。
図 3-1 https://insiders.vscode.dev/
では外部リソースに cors corp などの対応が必要
図 3-2 https://insiders.vscode.dev/?vscode-coi=off
で off にすると普通に表示される
そして、Start WebContainer 拡張機能では従来との互換性を優先して Nodebox を利用していますが、Nodebox はランタイムを iframe
内に展開しています。このとき iframe
は Cross-Origin-Embedder-Policy
が設定されていないリソースを指しているので、coi が有効になっていると逆に動作しません。
図 3-3 Nodebox のリソースは coi では読み込めない
実際のところ、vscode.dev でも on になるのかはわかりませんが、on になれば Start WebContainer 拡張機能はおしまいといった感じになります [4]。
その他
coi には直接関係ないのですが、今回の拡張機能を作るときに作った Rollup のプラグインについても少し。
Nodebox 内にファイルを配置するには、ファイルツリーをオブジェクトとして記述し nodebox.fs.init
API へわたすことになります。
リスト 4-1 nodebox.fs.init
のサンプル(ドキュメントより引用)
await nodebox.fs.init({
'index.js': `
import { greet } from './greet'
greet('Hello world')
`,
'greet.js': `
export function greet(message) {
console.log(message)
}
`,
});
これは面倒そうだったので Rollup のプラグインを作って外部のファイルツリーを差し込めるようしました。
テキストファイルだけでなく画像などのバイナリーファイルも扱えたりします。Nodebox でちょっと込み入ったコードを扱うとき、よければ試してみてください。
おわりに
デフォルトの設定を尊重して拡張機能を作ったら、デフォルト設定が変わって動かなくなるというピタゴラ的展開で少し涙目になったので、次に作るときは coi が on のことも想定していこうかと思っています。とほほ。
とか言いながらも、vscode.dev でも WASIX は気にしているぽいので、内心では wasm-wasi 用拡張機能を試してみたくてウキウキなんですけどね。
- There is also the WASIX effort that extends WASI with additional operating system-like features such as process or futex. We will continue to watch this work.
-
だったら Nodebox 使えばいいんじゃね?となりそうですが、Nodebox は Node.js との互換性が低いことと、シェル的なものがないので今回の用途には微妙に合致しませんでした。 ↩︎
-
swc は binding がないのでエラーになりました。Next.js では swc を使っているようですが、こちらは専用(?)の wasm バインディングがあります。この辺は StackBlitz の WebContainer による IDE 環境でも同じようになりそうですが、何かノウハウ的なものがあるのでしょうか? ↩︎
-
最初に試したときは Insiders 版でも off だったと思うのですが、きちんとメモしていなかったので少しあやふやです。4 月末より前から on だったかもしれません。 ↩︎
-
Nodebox が coi 対応するかもしれません。しかし、coi がデフォルトになったら WebView で直接 WebContainer を扱えるのはずなので、どちらにしても Nodebox なしで作り直した方がシンプルでよいのかなと。 ↩︎
Discussion