你的第一個原生外掛

本頁內容

原生外掛是 EmDash 匯入到與 Astro 網站同一行程中的 npm 套件。本教學建立一個記錄內容儲存的外掛,將其安裝到網站,並在 astro.config.mjs 中註冊。

當外掛需要行程內功能(如 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 取代為內容儲存掛鉤。

    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 整合中註冊描述子工廠。

    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 })],
            }),
        ],
    });

    原生描述子屬於 plugins,不屬於 sandboxed。EmDash 會拒絕 sandboxed 陣列中的原生描述子。

  7. 啟動網站並在管理面板中儲存一筆項目。

    pnpm dev

    伺服器日誌包含帶有集合、內容 ID 以及項目是否新建的 Content saved。

描述子與執行階段邊界

套件匯出有兩項工作。EmDash 在不同階段使用各自:

  • 描述子工廠 activityPlugin() 在 Astro 評估其設定時執行。它回傳可序列化的建置時中繼資料:id、version、format、entrypoint 與 options。React 與 Astro 進入點也屬於此描述子。
  • 具名 createPlugin() 匯出在 EmDash 初始化時執行。EmDash 從 entrypoint 匯入它,傳入序列化的 options,並期望從 definePlugin() 得到已解析的外掛。

具名 createPlugin 匯出是必要的。預設匯出可能對套件使用者有用,但 EmDash 的原生載入器依名稱匯入 createPlugin。

在描述子與 definePlugin() 中保持 id 與 version 相同。使用無作用域、kebab-case 的外掛 ID,如 plugin-activity;將 npm 作用域保留在套件名稱與 entrypoint 中。這樣 ID 可作為 API 路由 URL 中的單一外掛區段使用。

外掛識別與版本 列出可接受的 ID 與版本形式。

執行階段行為屬於 definePlugin():

  • capabilities 與 allowedHosts
  • storage
  • hooks 與 routes
  • admin 設定、頁面、小工具與 Portable Text 宣告

描述子攜帶 Astro 必須在建置時匯入或暴露的靜態項目。專題指南說明哪些管理欄位需要匹配的描述子與執行階段宣告。

原生路由處理常式

原生路由處理常式接收一個 RouteContext。它將已驗證輸入與請求資料與常規 PluginContext 合併:

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

等效的沙箱處理常式以兩個參數接收 (routeCtx, ctx)。身分驗證、權限、輸入 schema 與路由 URL 在其他方面遵循共用的 API 路由 約定。

當原生路由宣告 request.body 時,用 definePluginRoute() 包裝它;該輔助函式從 body 模式推斷 ctx.input。帶 response: "raw" 的原生路由回傳 pluginResponse()。 從 emdash 匯入這兩個輔助函式。共用 API 路由指南列出 body 模式、限制、回應策略與相容性預設值。

新增另一個表面