このサイトは非公式の日本語訳です。Cloudflare・EmDashプロジェクトが運営するサイトではありません。

このページで分かること

  • ネイティブ型を選ぶ場合(Reactの管理画面コンポーネント、Astroの描画コンポーネント、ページフラグメントが必要なとき)と、コンテンツの保存を記録するプラグインを作ってサイトに登録する手順
  • ディスクリプター(ビルド時の情報)と createPlugin()(実行時の処理)の役割の分担
  • ネイティブ型のルートハンドラーの形と、分離の境界がないことによる注意点
難易度
上級
読む時間
3分
このページの目次

ネイティブ型プラグインは、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 は、自分のサイトのディレクトリ名に置き換えます。

パッケージの作成と登録

  1. サイトの隣に、ネイティブ型のパッケージの雛形を作ります。

    pnpm exec emdash plugin init --native --name @example/plugin-activity --dir ../plugin-activity
    

    このコマンドは、プラグインIDを作るときにnpmのスコープを取り除きます。パッケージ名は @example/plugin-activity で、プラグインIDは plugin-activity です。

  2. パッケージの依存パッケージをインストールします。

    cd ../plugin-activity
    pnpm install
    
  3. 生成された 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はこのフックをスキップします。

  4. パッケージをビルドします。

    pnpm build
    
  5. ローカルのパッケージをサイトにインストールします。

    cd ../my-emdash-site
    pnpm add ../plugin-activity
    
  6. 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はそれを拒否します。

  7. サイトを起動し、管理画面でエントリーを保存します。

    pnpm dev
    

    サーバーのログに、コレクション、コンテンツID、エントリーが新規作成かどうかとともに、Content saved が表示されます。

ディスクリプターと実行時の境界

パッケージのエクスポートには2つの役割があります。EmDashは、それぞれを別の段階で使います。

  • ディスクリプターのファクトリー activityPlugin() は、Astroが設定を評価するときに実行されます。シリアライズできるビルド時のメタデータ(idversionformatentrypointoptions)を返します。ReactとAstroのエントリーポイントも、このディスクリプターに書きます。
  • 名前付きのエクスポート createPlugin() は、EmDashの初期化時に実行されます。EmDashはこれを entrypoint からインポートし、シリアライズされた options を渡して、definePlugin() で作られたプラグインが返されることを想定しています。

名前付きのエクスポート createPlugin は必須です。デフォルトエクスポートはパッケージの利用者にとって便利な場合がありますが、EmDashのネイティブ型のローダーは createPlugin を名前でインポートします。

idversion は、ディスクリプターと definePlugin() で同じにします。プラグインIDには、plugin-activity のような、スコープなしのケバブケースを使います。npmのスコープは、パッケージ名と entrypoint に残します。こうすることで、IDをAPIルートのURLにおけるプラグインの1つのセグメントとして使えます。

受け付けられるIDとバージョンの形式は、プラグインのIDとバージョンに一覧があります。

実行時の動作は definePlugin() に書きます。

  • capabilitiesallowedHosts
  • storage
  • hooksroutes
  • admin の設定、ページ、ウィジェット、Portable Textの宣言

ディスクリプターには、Astroがビルド時にインポートまたは公開する必要がある静的な項目を持たせます。管理画面のどのフィールドで、ディスクリプターと実行時の両方に対応する宣言が必要になるかは、それぞれのテーマのガイドで説明しています。

本サイトの補足 やさしい解説

ネイティブ型プラグインは、同じパッケージから2つのものをエクスポートします。1つは astro.config.mjs で呼び出すディスクリプター(activityPlugin())で、ビルドの時点でIDやバージョンなどの情報だけを渡します。もう1つは createPlugin() で、サイトが動き始めたときに呼ばれ、フックなどの実際の処理を返します。idversion は両方で同じ値にする必要があります。

ネイティブ型のルートハンドラー

ネイティブ型のルートハンドラーは、1つの RouteContext を受け取ります。RouteContext は、検証済みの入力とリクエストのデータを、通常の PluginContext と組み合わせたものです。

src/index.ts
routes: {
	status: {
		permission: "plugins:read",
		handler: async (ctx) => ({
			pluginId: ctx.plugin.id,
			callerId: ctx.user?.id ?? null,
		}),
	},
},

同等のサンドボックス型のハンドラーは、(routeCtx, ctx) を2つの引数として受け取ります。それ以外の、認証、権限、入力のスキーマ、ルートのURLは、共通のAPIルートの取り決めに従います。

機能の追加