プラグイン形式の選び方
このページで分かること
- サンドボックス型とネイティブ型の違い(作り方、インストール方法、動く場所、権限の強制、リソース制限、配布方法、管理画面の作り方など)の比較表
- ネイティブ型を選んだ場合の負担(サイトごとのインストール、分離がないこと、利用者が信頼する必要があること)と、ネイティブ型が必要になる3つの場合
- 同梱されている2つのサンドボックスランナー(Cloudflare、Node.jsの
workerd)と、ランナーが使えない環境での扱い
このページの目次
EmDashのプラグインは、サンドボックス型(sandboxed)とネイティブ型(native)の2つの形式のどちらかを使います。書き方、インストール方法、信頼境界が形式によって異なるため、プラグインを書き始める前に形式を選びます。
プラグインがネイティブ型でしかできない連携を必要としない限り、サンドボックス型を選びます。サンドボックス型プラグインはプラグインレジストリに公開でき、管理画面からインストールできます。ネイティブ型プラグインはnpmパッケージです。サイトの運営者がプロジェクトにインストールし、astro.config.mjs に追加してから、再デプロイします。
比較表
| サンドボックス型 | ネイティブ型 | |
|---|---|---|
| 書き方 | emdash-plugin.jsonc + src/plugin.ts |
definePlugin() のディスクリプター |
| インストール方法 | 管理画面のレジストリからワンクリック | npm install + astro.config の編集 |
| 動く場所 | サンドボックスランナーが提供する分離されたランタイム | Astroサイトと同じプロセス |
権限(Capability)で制限される ctx のAPI |
サンドボックスのブリッジが強制する | PluginContext が制限するが、セキュリティ境界ではない |
| リソース制限 | CPU、サブリクエスト、実時間(wall time)のランナーの制限と、プラットフォームのメモリー上限 | プラグインごとの制限なし |
| ネットワークアクセス | ctx.http(宣言したアクセスに制限される) |
ctx.http は宣言に従う。ネイティブのコードは fetch() も呼び出せる |
fetch()/process.env の直接利用 |
ランナーがブロックする | 可能(プラグインのコードはランタイムを共有する) |
| 配布方法 | プラグインレジストリの署名付きリリース | npmパッケージ |
| 管理画面 | Block Kit(JSONで記述する)のルート | Reactコンポーネント、またはBlock Kit |
| 設定画面 | Block Kitのページ+KVの読み込み | admin.settingsSchema(自動生成のフォーム)またはBlock Kit |
| Portable Textの描画コンポーネント | 使えない | componentsEntry がAstroコンポーネントを提供する |
| ページのメタデータへの追加 | page:metadata フック(meta/propertyタグ、許可リストにある <link> のrel、JSON-LD) |
page:metadata フック(範囲は同じ) |
| ページフラグメントの挿入 | 使えない(meta/JSON-LDは page:metadata でのみ可能) |
page:fragments フック(インラインスクリプト、外部スクリプト、生のHTML) |
| コンストラクターのオプション | なし(実行時にKVから設定を読み込む) | ディスクリプターの options |
やさしい解説
公式の対応表では、WordPressのプラグインにあたるのは、EmDashのサンドボックス型プラグインまたはネイティブ型プラグインです。ネイティブ型プラグインはnpmでインストールし、サイトと同じプロセスで動き、サイトと同じアクセス権を持ちます。サンドボックス型プラグインは、サイト本体から切り離された環境で動き、ランナーが fetch() や process.env の直接利用をブロックします。そのかわり、管理画面のレジストリからワンクリックでインストールできます。原文は、ネイティブ型でしかできない連携が必要な場合を除いて、サンドボックス型を選ぶよう案内しています。
ネイティブ型プラグインの負担
ネイティブ型プラグインは、インストールと信頼のモデルが異なります。
- プロジェクト単位のインストール。 すべてのサイトが、npmパッケージをインストールし、
astro.config.mjsを編集して、再デプロイする必要があります。 - 分離がない。 プラグインの不具合でホストのプロセスが停止したり、CPUの割り当てを使い切ったりすることがあります。フックの中で処理されなかったPromiseの拒否(unhandled rejection)が、実行中のリクエストごと失敗させることもあります。
- 利用者が信頼する負担。 ネイティブ型プラグインは、ホストのサイトと同じアクセス権を持ちます。権限の宣言だけでは、そのコードができることをすべて示せません。
サンドボックスの中で役割を果たせるプラグインは、サンドボックス型にします。
ネイティブ型を選ぶ場合
ホストのサイトとのビルド時の連携が必要な機能には、ネイティブ型を選びます。
-
独自のReactの管理画面ページやウィジェット。 サンドボックス型プラグインは、管理画面をBlock Kitで記述します。Block Kitは、プラグインに代わって管理画面が描画するJSONのスキーマです。Reactをすべて使う必要がある場合(独自のフック、サードパーティのコンポーネント、複雑な状態管理)は、ネイティブ型が必要です。
-
独自のPortable Textのブロックタイプ。 ブロックタイプの編集の設定と、Astroの描画コンポーネントは、インストールされたnpmパッケージから読み込まれます。このビルド時の仕組みを提供できるのは、ネイティブ型プラグインだけです。
-
公開ページへの生のHTML、スクリプト、スタイルシートの挿入。
page:fragmentsフックは、ファーストパーティのコードを訪問者のブラウザに送ります。このコードはどのサンドボックスの境界の外にもあります。そのため、このフックはネイティブ型プラグインに限定されています。サンドボックス型プラグインも、page:metadataフックを通じて公開ページに情報を追加できます。このフックで、実際の用途の多くをまかなえます。metaタグ(name+content):SEO用の説明文、robotsの指示、Twitterカードpropertyタグ:OpenGraphなど、propertyで指定するメタ情報- セキュリティのために固定されたrelの許可リスト(
canonical、alternate、author、license、nlweb、site.standard.document)を持つlinkタグ。stylesheet、prefetchなど、リソースを読み込むrelは意図的に許可されていません - JSON-LDのグラフ
「ページへの挿入」で必要なものが構造化データやSEO用のメタデータであれば、サンドボックス型のまま
page:metadataを使います。JavaScriptやHTMLを訪問者のブラウザに実際に送る必要がある場合が、ネイティブ型を選ぶ場合です。
これらの機能がどれも当てはまらない場合は、サンドボックス型を使います。
やさしい解説
公開ページに何かを出したいとき、EmDashではプラグインの形式によって使えるフックが違います。訪問者のブラウザにスクリプトやHTMLを送る page:fragments は、ネイティブ型プラグインだけが使えます。サンドボックス型プラグインが公開ページに追加できるのは、page:metadata による meta タグ、property タグ、許可された link タグ、JSON-LDです。SEO用のタグや構造化データを出したいだけなら、サンドボックス型で足ります。
サンドボックスランナーと対応プラットフォーム
サンドボックスそのものは差し替えられます。EmDashは sandboxRunner という設定項目を用意しており、プラグインのコードをどう分離するかはランナーが決めます。プラグインの形式そのものには、Cloudflareに固有のものはありません。
EmDashには2つのランナーが同梱されています。1つは @emdash-cms/cloudflare の sandbox() で、CloudflareのWorker Loaderを通じて、各プラグインをDynamic Workerとして実行します。もう1つは @emdash-cms/sandbox-workerd/sandbox で、Node.js上の workerd の子プロセスでプラグインを実行します。各ランナーの設定方法、ランナーが強制するリソース制限、2つのランナーの違いは、プラグインサンドボックスで説明しています。
ランナーが設定されていない場合、sandboxed: [] に記載したプラグインは読み込まれません。設定したランナーが現在のプラットフォームで使えない場合も読み込まれず、EmDashは起動時に警告をログに出力します。
サンドボックスランナーのないプラットフォームでサンドボックス型プラグインを動かしたい場合は、そのプラグインを sandboxed: [] から plugins: [] の配列に移します。プラグインはプロセス内で実行されます。権限の宣言は引き続き守られます(同じ PluginContext のファクトリーが ctx.content、ctx.http などを制限します)。ただし、分離の境界もリソース制限もなく、不具合のあるプラグインや悪意のあるプラグインは、fetch() を直接呼び出したり、環境変数を読み取ったり、イベントループをブロックしたりできます。サンドボックスランナーが有効でない場合は、信頼の面では、すべてのプラグインをネイティブ型プラグインとして扱ってください。
次のステップ
- はじめてのサンドボックス型プラグイン:コンテンツの保存に反応するサンドボックス型プラグインを作り、登録して動かします。
- ネイティブ型を選ぶ場合:ネイティブ型でしか使えない3つの機能を詳しく説明します。