はじめてのネイティブ型プラグイン
このページで分かること
- ネイティブ型を選ぶ場合(Reactの管理画面コンポーネント、Astroの描画コンポーネント、ページフラグメントが必要なとき)と、コンテンツの保存を記録するプラグインを作ってサイトに登録する手順
- ディスクリプター(ビルド時の情報)と
createPlugin()(実行時の処理)の役割の分担 - ネイティブ型のルートハンドラーの形と、分離の境界がないことによる注意点
このページの目次
ネイティブ型プラグインは、EmDashがAstroのサイトと同じプロセスにインポートするnpmパッケージです。このチュートリアルでは、コンテンツの保存を記録するプラグインを作成し、サイトにインストールして、astro.config.mjs に登録します。
プラグインに、Reactの管理画面コンポーネント、Astroの描画コンポーネント、信頼されたページフラグメントなど、同じプロセスで動かす必要がある機能が必要な場合は、ネイティブ形式を使います。フック、ルート、ストレージ、Block Kitで機能をまかなえる場合は、サンドボックス型プラグインから始めます。形式の比較はプラグイン形式の選び方にあります。
やさしい解説
公式の対応表では、WordPressのプラグインは、EmDashのサンドボックス型またはネイティブ型のプラグインにあたります。ネイティブ型プラグインはサイトと同じプロセスで動くため、Reactで管理画面を作る、Astroのコンポーネントで公開ページに描画するといったことができます。フック・ルート・ストレージ・Block Kitで足りる場合は、制限のあるサンドボックス型から始めるよう公式は案内しています。
前提条件
pnpmを使っていて、開発サーバーを起動できるEmDashのサイトを用意します。サイトはすでに emdash に依存している必要があります。以下で使う emdash コマンドは、このパッケージが提供します。
以下のコマンドでは、サイトのディレクトリを my-emdash-site と呼び、その隣に plugin-activity を作成します。my-emdash-site は、自分のサイトのディレクトリ名に置き換えます。
パッケージの作成と登録
-
サイトの隣に、ネイティブ型のパッケージの雛形を作ります。
pnpm exec emdash plugin init --native --name @example/plugin-activity --dir ../plugin-activityこのコマンドは、プラグインIDを作るときにnpmのスコープを取り除きます。パッケージ名は
@example/plugin-activityで、プラグインIDはplugin-activityです。 -
パッケージの依存パッケージをインストールします。
cd ../plugin-activity pnpm install -
生成された
src/index.tsを、コンテンツの保存時に動くフックに置き換えます。src/index.ts import { definePlugin } from "emdash"; import type { PluginDescriptor } from "emdash"; export interface ActivityPluginOptions { logUpdates?: boolean; } export function activityPlugin( options: ActivityPluginOptions = {}, ): PluginDescriptor<ActivityPluginOptions> { return { id: "plugin-activity", version: "0.1.0", format: "native", entrypoint: "@example/plugin-activity", options, }; } export function createPlugin(options: ActivityPluginOptions = {}) { return definePlugin({ id: "plugin-activity", version: "0.1.0", capabilities: ["content:read"], hooks: { "content:afterSave": async (event, ctx) => { if (!event.isNew && options.logUpdates === false) return; ctx.log.info("Content saved", { collection: event.collection, contentId: event.content.id, isNew: event.isNew, }); }, }, }); } export default createPlugin;content:afterSaveにはcontent:read権限が必要です。この権限がない場合、EmDashはこのフックをスキップします。 -
パッケージをビルドします。
pnpm build -
ローカルのパッケージをサイトにインストールします。
cd ../my-emdash-site pnpm add ../plugin-activity -
EmDashのインテグレーションに、ディスクリプターのファクトリーを登録します。
astro.config.mjs import { defineConfig } from "astro/config"; import emdash from "emdash/astro"; import { activityPlugin } from "@example/plugin-activity"; export default defineConfig({ integrations: [ emdash({ plugins: [activityPlugin({ logUpdates: true })], }), ], });ネイティブ型のディスクリプターは、
sandboxedではなくpluginsに入れます。sandboxedの配列にネイティブ型のディスクリプターがあると、EmDashはそれを拒否します。 -
サイトを起動し、管理画面でエントリーを保存します。
pnpm devサーバーのログに、コレクション、コンテンツID、エントリーが新規作成かどうかとともに、
Content savedが表示されます。
ディスクリプターと実行時の境界
パッケージのエクスポートには2つの役割があります。EmDashは、それぞれを別の段階で使います。
- ディスクリプターのファクトリー
activityPlugin()は、Astroが設定を評価するときに実行されます。シリアライズできるビルド時のメタデータ(id、version、format、entrypoint、options)を返します。ReactとAstroのエントリーポイントも、このディスクリプターに書きます。 - 名前付きのエクスポート
createPlugin()は、EmDashの初期化時に実行されます。EmDashはこれをentrypointからインポートし、シリアライズされたoptionsを渡して、definePlugin()で作られたプラグインが返されることを想定しています。
名前付きのエクスポート createPlugin は必須です。デフォルトエクスポートはパッケージの利用者にとって便利な場合がありますが、EmDashのネイティブ型のローダーは createPlugin を名前でインポートします。
id と version は、ディスクリプターと definePlugin() で同じにします。プラグインIDには、plugin-activity のような、スコープなしのケバブケースを使います。npmのスコープは、パッケージ名と entrypoint に残します。こうすることで、IDをAPIルートのURLにおけるプラグインの1つのセグメントとして使えます。
受け付けられるIDとバージョンの形式は、プラグインのIDとバージョンに一覧があります。
実行時の動作は definePlugin() に書きます。
capabilitiesとallowedHostsstoragehooksとroutesadminの設定、ページ、ウィジェット、Portable Textの宣言
ディスクリプターには、Astroがビルド時にインポートまたは公開する必要がある静的な項目を持たせます。管理画面のどのフィールドで、ディスクリプターと実行時の両方に対応する宣言が必要になるかは、それぞれのテーマのガイドで説明しています。
やさしい解説
ネイティブ型プラグインは、同じパッケージから2つのものをエクスポートします。1つは astro.config.mjs で呼び出すディスクリプター(activityPlugin())で、ビルドの時点でIDやバージョンなどの情報だけを渡します。もう1つは createPlugin() で、サイトが動き始めたときに呼ばれ、フックなどの実際の処理を返します。id と version は両方で同じ値にする必要があります。
ネイティブ型のルートハンドラー
ネイティブ型のルートハンドラーは、1つの RouteContext を受け取ります。RouteContext は、検証済みの入力とリクエストのデータを、通常の PluginContext と組み合わせたものです。
routes: {
status: {
permission: "plugins:read",
handler: async (ctx) => ({
pluginId: ctx.plugin.id,
callerId: ctx.user?.id ?? null,
}),
},
},
同等のサンドボックス型のハンドラーは、(routeCtx, ctx) を2つの引数として受け取ります。それ以外の、認証、権限、入力のスキーマ、ルートのURLは、共通のAPIルートの取り決めに従います。
機能の追加
- Reactの管理画面のページとウィジェットでは、設定、独自のページ、ダッシュボードのウィジェット、編集画面のパネル、一覧の列を説明しています。
- Portable Textレンダリングコンポーネントでは、プラグインのブロック用のAstroコンポーネントを登録します。
- ページフラグメントでは、信頼されたスクリプトやHTMLを公開ページに追加します。
- ネイティブ型プラグインの配布では、ビルドとソースのエントリーポイントをnpm用にパッケージにまとめます。