Um plugin nativo é um pacote npm que o EmDash importa no mesmo processo do site Astro. Este tutorial cria um plugin que registra em log os salvamentos de conteúdo, instala-o em um site e o registra em astro.config.mjs.
Use o formato nativo quando o plugin precisar de um recurso no mesmo processo, como componentes de administração em React, componentes de renderização do Astro ou fragmentos de página confiáveis. Se hooks, rotas, armazenamento e Block Kit cobrirem o recurso, comece com um plugin sandboxed. Escolher um formato de plugin compara os formatos.
Pré-requisitos
Comece com um site EmDash que use pnpm e consiga executar seu servidor de desenvolvimento.
Os comandos chamam o diretório do site de my-emdash-site e criam plugin-activity ao lado dele. Substitua my-emdash-site pelo nome do diretório do seu site.
Criar e registrar o pacote
-
Crie um diretório de pacote nativo ao lado do site.
mkdir -p ../plugin-activity/src cd ../plugin-activity -
Adicione os metadados do pacote e os scripts de build.
{ "name": "@example/plugin-activity", "version": "0.1.0", "type": "module", "main": "./dist/index.mjs", "exports": { ".": { "types": "./dist/index.d.mts", "import": "./dist/index.mjs" } }, "files": ["dist"], "scripts": { "build": "tsdown src/index.ts --format esm --dts --clean", "dev": "tsdown src/index.ts --format esm --dts --watch", "typecheck": "tsc --noEmit" }, "peerDependencies": { "emdash": "*" }, "devDependencies": { "emdash": "*", "tsdown": "^0.20.0", "typescript": "^5.9.0" } }O nome do pacote npm é
@example/plugin-activity. O código de runtime abaixo usa o ID de plugin sem escopoplugin-activitypara que ele caiba nas URLs das rotas de API do plugin. -
Adicione a configuração do TypeScript.
{ "compilerOptions": { "target": "ES2022", "module": "preserve", "moduleResolution": "bundler", "strict": true, "esModuleInterop": true, "declaration": true, "outDir": "./dist", "rootDir": "./src" }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } -
Instale as dependências do pacote.
pnpm install -
Crie
src/index.tscom um hook de salvamento de conteúdo.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:afterSaveexige a capabilitycontent:read. O EmDash ignora o hook quando essa capability está ausente. -
Faça o build do pacote.
pnpm build -
Instale o pacote local no site.
cd ../my-emdash-site pnpm add ../plugin-activity -
Registre a factory do descritor na integração do 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 })], }), ], });Descritores nativos pertencem a
plugins, não asandboxed. O EmDash rejeita um descritor nativo no arraysandboxed. -
Inicie o site e salve uma entrada no painel de administração.
pnpm devO log do servidor inclui
Content savedcom a coleção, o ID do conteúdo e se a entrada foi criada.
Limite entre descritor e runtime
A exportação do pacote tem duas funções. O EmDash usa cada uma em uma etapa diferente:
- A factory do descritor,
activityPlugin(), é executada enquanto o Astro avalia sua configuração. Ela retorna metadados serializáveis de build:id,version,format,entrypointeoptions. Os pontos de entrada de React e Astro também pertencem a esse descritor. - A exportação nomeada
createPlugin()é executada quando o EmDash inicializa. O EmDash a importa deentrypoint, passa asoptionsserializadas e espera um plugin resolvido dedefinePlugin().
A exportação nomeada createPlugin é obrigatória. Uma exportação padrão pode ser útil para quem consome o pacote, mas o carregador nativo do EmDash importa createPlugin pelo nome.
Mantenha id e version idênticos no descritor e em definePlugin(). Use um ID de plugin sem escopo, em kebab-case, como plugin-activity; mantenha o escopo do npm no nome do pacote e em entrypoint. Assim o ID continua utilizável como único segmento de plugin nas URLs das rotas de API.
Identidade e versionamento do plugin lista as formas aceitas de ID e de versão.
O comportamento de runtime pertence a definePlugin():
capabilitieseallowedHostsstoragehookseroutes- declarações de
adminpara configurações, páginas, widgets e Portable Text
O descritor carrega as entradas estáticas que o Astro precisa importar ou expor em tempo de build. Os guias específicos mostram quais campos de administração precisam de declarações correspondentes no descritor e no runtime.
Handlers de rotas nativos
Os handlers de rotas nativos recebem um único RouteContext. Ele combina a entrada validada e os dados da requisição com o PluginContext habitual:
routes: {
status: {
permission: "plugins:read",
handler: async (ctx) => ({
pluginId: ctx.plugin.id,
callerId: ctx.user?.id ?? null,
}),
},
},
O handler sandboxed equivalente recebe (routeCtx, ctx) como dois argumentos. Autenticação, permissões, schemas de entrada e URLs de rotas seguem, no restante, o contrato compartilhado das rotas de API.
Envolva uma rota nativa em definePluginRoute() quando ela declarar request.body; o helper infere
ctx.input a partir do modo do corpo. Uma rota nativa com response: "raw" retorna pluginResponse().
Importe ambos os helpers de emdash. O guia compartilhado de rotas de API lista os modos de corpo, os limites, a política
de resposta e os padrões de compatibilidade.
Ler secrets e bindings
Os handlers de hooks e de rotas em um plugin nativo recebem o contexto do plugin, não o contexto do Astro, portanto Astro.locals não está disponível neles. Leia secrets e bindings da plataforma a partir do ambiente de runtime.
Leia os secrets de implantação em process.env. No Node.js, o gerenciador de secrets da plataforma de hospedagem os fornece. No Cloudflare Workers, os secrets definidos com wrangler secret put chegam a process.env por meio do flag nodejs_compat com uma data de compatibilidade de 2025-04-01 ou posterior, conforme descrito em Secrets do Worker.
Para uma credencial que um administrador do site informa, declare em vez disso um campo secret no formulário de configurações gerado. O EmDash criptografa o valor com EMDASH_ENCRYPTION_KEY antes de armazená-lo, portanto defina essa chave antes de um administrador salvar o campo. O plugin lê o valor com ctx.settings.get().
No Cloudflare Workers, bindings como uma fila, um bucket R2 ou send_email são objetos em env de cloudflare:workers. O Astro carrega astro.config.mjs no Node.js, e esse arquivo importa o pacote do plugin para a factory do descritor. O Node.js não consegue carregar cloudflare:workers, então importe-o dentro do handler em vez de no topo do módulo. O hook a seguir envia o ID de cada entrada salva para uma fila vinculada como ACTIVITY_QUEUE:
hooks: {
"content:afterSave": async (event) => {
const { env } = await import("cloudflare:workers");
await env.ACTIVITY_QUEUE.send({ contentId: event.content.id });
},
},
O módulo cloudflare:workers só é resolvido no runtime do Workers, portanto um plugin que o importa só funciona em sites que usam o adaptador da Cloudflare.
Para verificar os tipos da importação, adicione @cloudflare/workers-types às devDependencies do pacote e a compilerOptions.types em tsconfig.json. Depois declare cada binding que o plugin lê na interface Cloudflare.Env. A declaração a seguir tipa a fila do exemplo anterior:
declare namespace Cloudflare {
interface Env {
ACTIVITY_QUEUE: Queue;
}
}
Adicionar outra superfície
- Páginas e widgets de administração em React cobre configurações, páginas personalizadas, widgets do painel, widgets de campo, painéis do editor e colunas de lista.
- Componentes de renderização do Portable Text registra componentes Astro para os blocos do plugin.
- Fragmentos de página adiciona scripts ou HTML confiáveis às páginas públicas.
- Distribuir plugins nativos empacota os pontos de entrada de build e de código-fonte para o npm.