Un plugin nativo è un pacchetto npm che EmDash importa nello stesso processo del sito Astro. Questo tutorial crea un plugin che registra i salvataggi dei contenuti, lo installa in un sito e lo registra in astro.config.mjs.
Usa il formato nativo quando il plugin ha bisogno di una funzionalità in-process, come componenti di amministrazione React, componenti di rendering Astro o frammenti di pagina attendibili. Se hook, route, storage e Block Kit coprono la funzionalità, inizia con un plugin sandboxed. Scegliere un formato di plugin confronta i formati.
Prerequisiti
Parti da un sito EmDash che usa pnpm e può eseguire il proprio server di sviluppo.
I comandi chiamano la directory del sito my-emdash-site e creano plugin-activity accanto a essa. Sostituisci my-emdash-site con il nome della directory del tuo sito.
Creare e registrare il pacchetto
-
Crea una directory per il pacchetto nativo accanto al sito.
mkdir -p ../plugin-activity/src cd ../plugin-activity -
Aggiungi i metadati del pacchetto e gli script di 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" } }Il nome del pacchetto npm è
@example/plugin-activity. Il codice di runtime qui sotto usa l’ID del plugin senza scopeplugin-activity, in modo che si adatti agli URL delle route API del plugin. -
Aggiungi la configurazione TypeScript.
{ "compilerOptions": { "target": "ES2022", "module": "preserve", "moduleResolution": "bundler", "strict": true, "esModuleInterop": true, "declaration": true, "outDir": "./dist", "rootDir": "./src" }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } -
Installa le dipendenze del pacchetto.
pnpm install -
Crea
src/index.tscon un hook di salvataggio dei contenuti.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:afterSaverichiede la capabilitycontent:read. EmDash salta l’hook quando manca quella capability. -
Compila il pacchetto.
pnpm build -
Installa il pacchetto locale nel sito.
cd ../my-emdash-site pnpm add ../plugin-activity -
Registra la factory del descrittore nell’integrazione 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 })], }), ], });I descrittori nativi vanno in
plugins, non insandboxed. EmDash rifiuta un descrittore nativo nell’arraysandboxed. -
Avvia il sito e salva una voce nel pannello di amministrazione.
pnpm devIl log del server include
Content savedcon la collezione, l’ID del contenuto e l’indicazione se la voce è stata creata.
Confine tra descrittore e runtime
L’export del pacchetto ha due compiti. EmDash usa ciascuno in una fase diversa:
- La factory del descrittore,
activityPlugin(), viene eseguita mentre Astro valuta la propria configurazione. Restituisce metadati serializzabili di build:id,version,format,entrypointeoptions. Anche i punti di ingresso React e Astro appartengono a questo descrittore. - L’export con nome
createPlugin()viene eseguito quando EmDash si inizializza. EmDash lo importa daentrypoint, gli passa leoptionsserializzate e si aspetta un plugin risolto dadefinePlugin().
L’export con nome createPlugin è obbligatorio. Un export predefinito può essere utile a chi consuma il pacchetto, ma il loader nativo di EmDash importa createPlugin per nome.
Mantieni id e version identici nel descrittore e in definePlugin(). Usa un ID di plugin senza scope in kebab-case, come plugin-activity; mantieni lo scope npm nel nome del pacchetto e in entrypoint. Così l’ID resta utilizzabile come unico segmento di plugin negli URL delle route API.
Identità e versionamento del plugin elenca le forme accettate per ID e versione.
Il comportamento di runtime appartiene a definePlugin():
capabilitieseallowedHostsstoragehookseroutes- dichiarazioni
adminper impostazioni, pagine, widget e Portable Text
Il descrittore trasporta le voci statiche che Astro deve importare o esporre in fase di build. Le guide specifiche mostrano quali campi di amministrazione richiedono dichiarazioni corrispondenti nel descrittore e nel runtime.
Handler di route nativi
Gli handler di route nativi ricevono un unico RouteContext. Combina input validato e dati della richiesta con il normale PluginContext:
routes: {
status: {
permission: "plugins:read",
handler: async (ctx) => ({
pluginId: ctx.plugin.id,
callerId: ctx.user?.id ?? null,
}),
},
},
L’handler sandboxed equivalente riceve (routeCtx, ctx) come due argomenti. Autenticazione, permessi, schemi di input e URL delle route seguono per il resto il contratto condiviso delle route API.
Avvolgi una route nativa in definePluginRoute() quando dichiara request.body; l’helper deduce
ctx.input dalla modalità del body. Una route nativa con response: "raw" restituisce pluginResponse().
Importa entrambi gli helper da emdash. La guida condivisa alle route API elenca le modalità del body, i limiti, la policy
di risposta e i valori predefiniti di compatibilità.
Leggere secret e binding
Gli handler di hook e route in un plugin nativo ricevono il contesto del plugin, non il contesto Astro, quindi Astro.locals non è disponibile al loro interno. Leggi secret e binding della piattaforma dall’ambiente di runtime.
Leggi i secret di distribuzione da process.env. Su Node.js li fornisce il gestore dei secret della piattaforma di hosting. Su Cloudflare Workers, i secret impostati con wrangler secret put raggiungono process.env tramite il flag nodejs_compat con una data di compatibilità del 2025-04-01 o successiva, come descritto in Secret del Worker.
Per una credenziale che inserisce un amministratore del sito, dichiara invece un campo secret nel modulo delle impostazioni generato. EmDash cifra il valore con EMDASH_ENCRYPTION_KEY prima di memorizzarlo, quindi imposta quella chiave prima che un amministratore salvi il campo. Il plugin legge il valore con ctx.settings.get().
Su Cloudflare Workers, i binding come una coda, un bucket R2 o send_email sono oggetti su env da cloudflare:workers. Astro carica astro.config.mjs in Node.js, e quel file importa il pacchetto del plugin per la factory del descrittore. Node.js non può caricare cloudflare:workers, quindi importalo dentro l’handler anziché in cima al modulo. Il hook seguente invia l’ID di ogni voce salvata a una coda associata come ACTIVITY_QUEUE:
hooks: {
"content:afterSave": async (event) => {
const { env } = await import("cloudflare:workers");
await env.ACTIVITY_QUEUE.send({ contentId: event.content.id });
},
},
Il modulo cloudflare:workers si risolve solo nel runtime Workers, quindi un plugin che lo importa funziona solo sui siti che usano l’adattatore Cloudflare.
Per controllare i tipi dell’import, aggiungi @cloudflare/workers-types alle devDependencies del pacchetto e a compilerOptions.types in tsconfig.json. Poi dichiara ogni binding letto dal plugin sull’interfaccia Cloudflare.Env. La dichiarazione seguente tipizza la coda dell’esempio precedente:
declare namespace Cloudflare {
interface Env {
ACTIVITY_QUEUE: Queue;
}
}
Aggiungere un’altra superficie
- Pagine e widget di amministrazione React copre impostazioni, pagine personalizzate, widget della dashboard, widget di campo, pannelli dell’editor e colonne degli elenchi.
- Componenti di rendering Portable Text registra componenti Astro per i blocchi del plugin.
- Frammenti di pagina aggiunge script o HTML attendibili alle pagine pubbliche.
- Distribuire plugin nativi impacchetta i punti di ingresso di build e di sorgente per npm.