Tu primer plugin nativo

En esta página

Un plugin nativo es un paquete npm que EmDash importa en el mismo proceso que el sitio Astro. Este tutorial crea un plugin que registra los guardados de contenido, lo instala en un sitio y lo registra en astro.config.mjs.

Usa el formato nativo cuando el plugin necesite una función in-process como componentes de administración React, componentes de renderizado Astro o fragmentos de página de confianza. Si hooks, rutas, storage y Block Kit cubren la función, empieza con un plugin sandboxed. Elegir un formato de plugin compara los formatos.

Requisitos previos

Empieza con un sitio EmDash que use pnpm y pueda ejecutar su servidor de desarrollo. El sitio ya debe depender de emdash, que proporciona el comando emdash usado abajo.

Los comandos llaman al directorio del sitio my-emdash-site y crean plugin-activity a su lado. Sustituye my-emdash-site por el nombre del directorio de tu sitio.

Crear y registrar el paquete

  1. Genera un paquete nativo junto al sitio.

    pnpm exec emdash plugin init --native --name @example/plugin-activity --dir ../plugin-activity

    El comando elimina el scope de npm cuando crea el ID del plugin. El nombre del paquete es @example/plugin-activity, mientras que el ID del plugin es plugin-activity.

  2. Instala las dependencias del paquete.

    cd ../plugin-activity
    pnpm install
  3. Sustituye el src/index.ts generado por un hook de guardado de contenido.

    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 requiere la capability content:read. EmDash omite el hook cuando falta esa capability.

  4. Construye el paquete.

    pnpm build
  5. Instala el paquete local en el sitio.

    cd ../my-emdash-site
    pnpm add ../plugin-activity
  6. Registra la factory del descriptor en la integración 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 })],
            }),
        ],
    });

    Los descriptores nativos pertenecen a plugins, no a sandboxed. EmDash rechaza un descriptor nativo en el array sandboxed.

  7. Arranca el sitio y guarda una entrada en el panel de administración.

    pnpm dev

    El registro del servidor incluye Content saved con la colección, el ID de contenido y si la entrada se creó.

Límite entre descriptor y runtime

La exportación del paquete tiene dos trabajos. EmDash usa cada uno en una etapa distinta:

  • La factory del descriptor, activityPlugin(), se ejecuta mientras Astro evalúa su configuración. Devuelve metadatos serializables de tiempo de build: id, version, format, entrypoint y options. Los entrypoints de React y Astro también pertenecen a este descriptor.
  • La exportación con nombre createPlugin() se ejecuta cuando EmDash se inicializa. EmDash la importa desde entrypoint, pasa las options serializadas y espera un plugin resuelto de definePlugin().

La exportación con nombre createPlugin es obligatoria. Una exportación por defecto puede ser útil para los consumidores del paquete, pero el cargador nativo de EmDash importa createPlugin por nombre.

Mantén id y version idénticos en el descriptor y en definePlugin(). Usa un ID de plugin sin scope y en kebab-case como plugin-activity; conserva el scope de npm en el nombre del paquete y en entrypoint. Así el ID sigue siendo usable como el único segmento de plugin en las URL de rutas de API.

Identidad y versionado del plugin enumera las formas de ID y versión aceptadas.

El comportamiento en runtime pertenece a definePlugin():

  • capabilities y allowedHosts
  • storage
  • hooks y routes
  • declaraciones de ajustes, página, widget y Portable Text de admin

El descriptor lleva las entradas estáticas que Astro debe importar o exponer en tiempo de build. Las guías enfocadas muestran qué campos de administración necesitan declaraciones coincidentes de descriptor y runtime.

Handlers de rutas nativas

Los handlers de rutas nativas reciben un RouteContext. Combina la entrada validada y los datos de la petición con el PluginContext habitual:

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

El handler sandboxed equivalente recibe (routeCtx, ctx) como dos argumentos. Autenticación, permisos, esquemas de entrada y URL de rutas siguen por lo demás el contrato compartido de rutas API.

Envuelve una ruta nativa en definePluginRoute() cuando declare request.body; el helper infiere ctx.input a partir del modo del body. Una ruta nativa con response: "raw" devuelve pluginResponse(). Importa ambos helpers desde emdash. La guía compartida de rutas API enumera los modos de body, límites, política de respuesta y valores predeterminados de compatibilidad.

Añadir otra superficie