Open23

Pluggable なブラウザアプリケーションの仕様を考える

やまゆやまゆ

npm を使わず、ブラウザ上のランタイム環境で任意のスクリプトをインポートし、それを実行出来る仕組み。プラグイン。エクステンション。 Chrome Extension や RPG ツクール MZ のプラグインがかなり近い。それらのプラグインを管理する側の実装についての仕様を考えたい。

やまゆやまゆ

「任意のスクリプトを実行」で一番最初に思い浮かぶのは eval だが、これでは「任意のスクリプトにインターフェースの制約をかけることが出来ない」問題がある。

やまゆやまゆ

プラグインの実例。

メニューバープラグイン

  • メニューバー DOM 要素を埋め込む機能
  • メニューを追加する API を他のプラグインに提供する機能
やまゆやまゆ

Babylon.js プラグイン

  • canvas DOM 要素を埋め込む機能
  • メニューバープラグインの API を使ってメニューバーにメニューを追加する機能
  • canvas DOM 要素を使って Babylon.js のランタイムを動作する機能
  • Babylon.js のランタイムオブジェクトを編集する機能
やまゆやまゆ

バンドルプラグイン

  • ゲームランタイムをまとめて単体で実行可能なファイルにまとめる機能
やまゆやまゆ

同期プラグイン

  • 各種プラグインやランタイムの状態をリモートと同期するサーバーを介した機能
やまゆやまゆ

安全(globalThis を汚染しない)に class を評価する機能があれば作れそう?

やまゆやまゆ

プラグインは任意の npm package を読み込めるようにしたい(超難しそう)

やまゆやまゆ

Web Extensions は、 1 つの manifest.json ファイルから必要なファイルを読み込み、その場で実行する。このスタイルは良さそう。

やまゆやまゆ

任意のスクリプトはどう頑張っても汚染のリスクがある?サンドボックス化出来れば安全になるかも。

やまゆやまゆ

例えば最初のプラグインは console.log('Hello world'); を実行するプラグインのはず。完全に破壊的なプラグインになるので、そこは割り切ってもいいかも。

やまゆやまゆ

ServiceWorker ?など、ブラウザ側が持っている機能でスクリプトをサンドボックス化出来れば、その中で実行して結果を RPC すれば良い気がする。

やまゆやまゆ

スクリプトの文字列を Blob を使って Object URL 化して、それを new Worker(blobUrl); すれば任意の文字列をワーカーとして起動することが可能。

やまゆやまゆ
window.addEventListener("load", () => {
    console.log("a");
    document.body.addEventListener("drop", (e) => {
        e.preventDefault();

        if (e.dataTransfer?.items) {
            [...e.dataTransfer.items].forEach((item) => {
                if (item.kind === "file") {
                    const file = item.getAsFile();
                    console.log(file);
                    if (file) {
                        const worker = new Worker(URL.createObjectURL(file));
                    }
                }
            });
        } else if (e.dataTransfer?.files) {
            [...e.dataTransfer.files].forEach((file) => {
                console.log(file);
            });
        }
    });
    document.body.addEventListener("dragover", (e) => {
        e.preventDefault();
    });
});

これでドラッグアンドドロップした JavaScript ファイルを Worker 内で実行することが出来た。

やまゆやまゆ

index.ts を渡すと invalid type video/vnd.dlna.mpeg-tts と mpeg のファイルタイプと認識されてしまう。

やまゆやまゆ
index.ts
const worker = new Worker(URL.createObjectURL(file), {
    credentials: "omit",
    name: file.name,
    type: "module",
});
worker.js
console.log("Hello worker.js");

import("https://cdn.skypack.dev/lodash").then((lodash) => {
    console.log("lodash", lodash);
});

module 形式にすることによって、 skypack から任意のパッケージを import 出来るようになった。

やまゆやまゆ
import { esm } from "https://esm.sh/build";

const text = await file.text();
const result = await esm(text);
console.log(result);

で TypeScript や jsx をトランスパイルして export された結果を取得出来る。

やまゆやまゆ
import { esm } from "https://esm.sh/build";

この記法が node.js だと多分まだサポートされていないので、 vscode 上でエラーが出る。ここから Developer Experience を上げていくために調べる。

やまゆやまゆ
  • エディタのコア部分(プラグインの管理)は vscode 上で行う。 deno でも node.js でも良い。
  • プラグインの開発も vscode 上で行う。リロード機構がうまくできている方が良い。 TypeScript で書けると良い。 deno でも node.js でも良い。
  • どちらも正しく型を解決できる必要がある。