原生外掛是安裝在宿主專案中、並在 astro.config.mjs 中註冊的 npm 套件。該套件需要一個已建置的伺服器端進入點,用於提供其描述符和 createPlugin()。如果它還附帶 React 或 Astro 元件,請將這些元件作為單獨的原始碼進入點匯出,以便宿主能夠針對正確的環境編譯它們。
套件版面
下面的版面配置將伺服器端執行階段與瀏覽器和 Astro 原始碼分開:
plugin-activity/
├── src/
│ ├── index.ts
│ ├── admin/
│ │ ├── index.tsx
│ │ └── ActivityPage.tsx
│ └── astro/
│ ├── index.ts
│ └── ActivityBlock.astro
├── dist/
│ ├── index.mjs
│ └── index.d.mts
├── package.json
├── tsconfig.json
└── README.md
dist/ 是產生的目錄。請在發佈的 tarball 中保留 src/admin/ 和 src/astro/,因為宿主的 Vite 和 Astro 建置必須處理這些進入點。
套件匯出
下面的 package.json 會建置伺服器端進入點,並發佈全部三個進入點:
{
"name": "@example/plugin-activity",
"version": "0.1.0",
"type": "module",
"main": "./dist/index.mjs",
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
},
"./admin": "./src/admin/index.tsx",
"./astro": "./src/astro/index.ts"
},
"files": ["dist", "src/admin", "src/astro"],
"scripts": {
"build": "tsdown src/index.ts --format esm --dts --clean",
"dev": "tsdown src/index.ts --format esm --dts --watch",
"typecheck": "tsc --noEmit",
"prepublishOnly": "pnpm typecheck && pnpm build"
},
"peerDependencies": {
"@cloudflare/kumo": "*",
"@emdash-cms/admin": "*",
"@lingui/core": "*",
"@lingui/react": "*",
"@tanstack/react-query": "*",
"astro": ">=6.0.0-beta.0",
"emdash": "*",
"react": "^18.0.0 || ^19.0.0"
},
"devDependencies": {
"@types/react": "^19.0.0",
"tsdown": "^0.20.0",
"typescript": "^5.9.0"
},
"keywords": ["emdash", "emdash-plugin"],
"license": "MIT"
}
如果外掛沒有受信任的 React UI,請移除 ./admin、src/admin 以及僅用於管理後台的 peer 相依套件。如果外掛沒有 Portable Text 算繪器,請移除 ./astro、src/astro 以及 astro peer。對於匯出的原始碼進入點所匯入的每一個由宿主持有的函式庫,都要新增對應的 peer 相依套件;這樣可以防止管理後台套件中混入第二份 React、Kumo、Lingui 或 React Query 執行個體。
這些進入點有不同的使用方:
| 匯出 | 何時必要 | 使用方 |
|---|---|---|
. | 一律必要 | Astro 設定匯入描述符工廠;EmDash 在執行階段匯入具名的 createPlugin()。 |
./admin | 設定了 adminEntry 時 | 宿主的瀏覽器建置會匯入 React 元件對應表。 |
./astro | 設定了 componentsEntry 時 | 宿主的 Astro 建置會匯入 blockComponents。 |
描述符和執行階段中的模組指定符必須與這些匯出一致:
export function activityPlugin(): PluginDescriptor {
return {
id: "plugin-activity",
version: "0.1.0",
format: "native",
entrypoint: "@example/plugin-activity",
adminEntry: "@example/plugin-activity/admin",
componentsEntry: "@example/plugin-activity/astro",
};
}
export function createPlugin() {
return definePlugin({
id: "plugin-activity",
version: "0.1.0",
admin: {
entry: "@example/plugin-activity/admin",
},
});
}
請保持 npm 套件版本、描述符版本和 definePlugin() 的版本同步。站台管理員看到的版本來自外掛定義,而不會自動取自 package.json。
外掛識別與版本
definePlugin() 接受兩種 ID:由小寫字母、數字和連字號組成的無作用域 ID,或形如 @scope/name 的帶作用域 ID。站台外掛請使用無作用域的 kebab-case ID,因為該 ID 還會在 /_emdash/api/plugins/<plugin-id>/<route> 中佔據一個路徑區段。
下面的值展示了可接受的形式,以及外掛 ID 與 npm 套件名稱之間建議的區分方式:
id: "plugin-activity"; // Recommended: valid in plugin route URLs
id: "@example/plugin-activity"; // Accepted by definePlugin(), but not one URL segment
entrypoint: "@example/plugin-activity"; // The npm package may stay scoped
版本必須以語意化的 major.minor.patch 序列開頭。描述符和執行階段都請使用完整的語意化版本:
version: "1.0.0"; // Valid
version: "1.2.3-beta.1"; // Valid prerelease
version: "1.0"; // Invalid: missing patch version
TypeScript 設定
下面的 tsconfig.json 適用於包含 React 和 Astro 原始碼的原生外掛:
{
"compilerOptions": {
"target": "ES2022",
"module": "preserve",
"moduleResolution": "bundler",
"strict": true,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src",
"jsx": "react-jsx",
"types": ["astro/client"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
打包之前,請針對這些原始碼進入點執行 pnpm typecheck。build 指令碼只編譯 src/index.ts;宿主在使用該套件時,才會編譯匯出的管理後台和 Astro 原始碼。
檢查套件
發佈之前,請測試 tarball 的確切內容。下面的指令假設有一個名為 my-emdash-site 的拋棄式站台,與外掛目錄並列放置。
-
建置並對套件進行型別檢查。
pnpm typecheck pnpm build -
建立 npm tarball,並查看 npm 印出的檔案清單。
npm pack對於範例套件,npm 會建立
example-plugin-activity-0.1.0.tgz。 -
確認輸出中包含
dist/index.mjs、dist/index.d.mts,以及可從匯出的./admin和./astro模組存取到的每一個原始檔。 -
將產生的 tarball 安裝到拋棄式的 EmDash 站台中。
cd ../my-emdash-site pnpm add ../plugin-activity/example-plugin-activity-0.1.0.tgz -
參照建立並註冊套件,在該拋棄式站台的
astro.config.mjs中匯入並註冊描述符工廠。然後建置宿主站台。pnpm build開啟每一個外掛管理介面,並算繪每一個貢獻的 Portable Text 區塊。在建置之前先註冊,可以讓 Astro 解析 tarball 的
./admin和./astro匯出;僅測試伺服器端的套件測試無法發現缺失的瀏覽器原始碼或.astro原始檔。
README 內容
請給營運者提供足夠的資訊,使其無需閱讀原始碼就能安裝和評估該套件。內容應包括:
- 一句話描述以及所支援的 EmDash 版本
- 安裝指令以及完整的
astro.config.mjs註冊方式 - 原生信任邊界,以及外掛為什麼需要原生執行
- 每一項已宣告的能力和允許的主機,以及使用它的功能
- 設定及其預設值
- 所需的版面配置元件,例如用於 body-end 片段的
EmDashBodyEnd - 需要營運者操作的變更所對應的升級步驟
不要把能力宣告描述為隔離邊界。它們只是限制對 ctx API 的存取,但原生程式碼仍然可以使用宿主程序可用的匯入、環境變數和直接網路呼叫。
發佈到 npm
tarball 測試通過後再發佈:
npm publish --access public
帶作用域的套件首次公開發佈時需要 --access public。後續版本請使用語意化版本。對於建構函式選項、已儲存資料、所需的宿主變更、套件匯出或外掛信任要求的變化,請將其視為相容性決策。如果升級需要新的能力或新的允許主機,即使原生安裝沒有能力授權提示,也請在發佈說明中明確指出。
從 npm 安裝
營運者在 EmDash 站台中安裝已發佈的套件:
pnpm add @example/plugin-activity
然後按照建立並註冊套件中所示,在 astro.config.mjs 中匯入並註冊其描述符工廠。僅安裝相依套件並不會啟用外掛;只有修改 Astro 設定並部署站台,安裝才算完成。
針對宿主站台開發
以監看模式建置外掛:
pnpm dev
在宿主站台中安裝本機目錄:
pnpm add ../plugin-activity
在 astro.config.mjs 中註冊外掛的描述符工廠,然後啟動宿主的開發伺服器。變更描述符中繼資料或套件匯出之後,請重新啟動伺服器。如果在你的環境中,套件管理員的 file 相依是複製檔案而不是連結檔案,請在重新建置後重新安裝它;而使用工作區相依或 pnpm link,可以在開發期間保持本機套件的連結。
註冊表邊界
原生套件無法發佈到 EmDash 註冊表。註冊表外掛使用沙箱套件格式、簽署發佈工作流程和安裝授權流程。如果外掛不再需要 React 管理後台程式碼、Astro 算繪器、受信任的片段或其他行程內相依,請在透過註冊表發佈之前,先將其轉換為沙箱格式。