分發原生外掛

本頁內容

原生外掛是安裝在宿主專案中、並在 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 的拋棄式站台,與外掛目錄並列放置。

  1. 建置並對套件進行型別檢查。

    pnpm typecheck
    pnpm build
  2. 建立 npm tarball,並查看 npm 印出的檔案清單。

    npm pack

    對於範例套件,npm 會建立 example-plugin-activity-0.1.0.tgz。

  3. 確認輸出中包含 dist/index.mjs、dist/index.d.mts,以及可從匯出的 ./admin 和 ./astro 模組存取到的每一個原始檔。

  4. 將產生的 tarball 安裝到拋棄式的 EmDash 站台中。

    cd ../my-emdash-site
    pnpm add ../plugin-activity/example-plugin-activity-0.1.0.tgz
  5. 參照建立並註冊套件,在該拋棄式站台的 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 算繪器、受信任的片段或其他行程內相依,請在透過註冊表發佈之前,先將其轉換為沙箱格式。