Distribuir plugins nativos

En esta página

Los plugins nativos son paquetes npm instalados en el proyecto host y registrados en astro.config.mjs. El paquete necesita una entrada de servidor construida para su descriptor y createPlugin(). Si también envía componentes React o Astro, expórtalos como entrypoints de fuente separados para que el host pueda compilarlos para el entorno correcto.

Diseño del paquete

El siguiente diseño separa el runtime del servidor del código fuente del navegador y de 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/ se genera. Conserva src/admin/ y src/astro/ en el tarball publicado porque el build de Vite y Astro del host debe procesar esos entrypoints.

Exportaciones del paquete

El siguiente package.json construye la entrada del servidor y publica los tres entrypoints:

{
	"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"
}

Elimina ./admin, src/admin y las peer dependencies solo de admin cuando el plugin no tenga UI React de confianza. Elimina ./astro, src/astro y el peer astro cuando no tenga un renderizador de Portable Text. Añade una peer dependency por cada biblioteca propiedad del host importada por un entrypoint de fuente exportado; esto evita que una segunda instancia de React, Kumo, Lingui o React Query entre en el bundle de administración.

Los entrypoints tienen distintos consumidores:

ExportaciónObligatoria cuandoConsumidor
.SiempreLa configuración de Astro importa la factory del descriptor; EmDash importa el createPlugin() con nombre en runtime.
./adminadminEntry está establecidoEl build del navegador del host importa los mapas de componentes React.
./astrocomponentsEntry está establecidoEl build de Astro del host importa blockComponents.

Los especificadores de módulo en el descriptor y el runtime deben coincidir con estas exportaciones:

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",
		},
	});
}

Mantén sincronizadas la versión del paquete npm, la versión del descriptor y la versión de definePlugin(). La versión mostrada a un administrador del sitio proviene de la definición del plugin, no automáticamente de package.json.

Identidad y versión del plugin

definePlugin() acepta un ID sin scope con letras minúsculas, dígitos y guiones, o un ID con scope en la forma @scope/name. Usa un ID sin scope en kebab-case para un plugin de sitio porque el ID también ocupa un segmento de ruta en /_emdash/api/plugins/<plugin-id>/<route>.

Los siguientes valores muestran las formas aceptadas y la separación recomendada entre el ID del plugin y el nombre del paquete npm:

id: "plugin-activity"; // Recomendado: válido en URL de rutas del plugin
id: "@example/plugin-activity"; // Aceptado por definePlugin(), pero no un segmento de URL

entrypoint: "@example/plugin-activity"; // El paquete npm puede seguir con scope

Las versiones deben comenzar con una secuencia semántica major.minor.patch. Usa una versión semántica completa para el descriptor y el runtime:

version: "1.0.0"; // Válida
version: "1.2.3-beta.1"; // Prerelease válida
version: "1.0"; // Inválida: falta la versión de parche

Configuración de TypeScript

El scaffold nativo crea un tsconfig.json adecuado. Si el plugin añade fuente React y Astro después del scaffolding, incluye ambos entornos JSX:

{
	"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"]
}

Ejecuta pnpm typecheck contra los entrypoints de fuente antes de empaquetar. El script build compila solo src/index.ts; el host compila la fuente admin y Astro exportada cuando consume el paquete.

Inspeccionar el paquete

Prueba el contenido exacto del tarball antes de publicar. Los comandos siguientes asumen que un sitio desechable llamado my-emdash-site está junto al directorio del plugin.

  1. Construye y comprueba tipos del paquete.

    pnpm typecheck
    pnpm build
  2. Crea el tarball npm y revisa la lista de archivos que imprime npm.

    npm pack

    Para el paquete de ejemplo, npm crea example-plugin-activity-0.1.0.tgz.

  3. Confirma que la salida contiene dist/index.mjs, dist/index.d.mts y cada archivo fuente alcanzable desde los módulos exportados ./admin y ./astro.

  4. Instala el tarball producido en el sitio EmDash desechable.

    cd ../my-emdash-site
    pnpm add ../plugin-activity/example-plugin-activity-0.1.0.tgz
  5. Importa y registra la factory del descriptor en el astro.config.mjs del sitio desechable, siguiendo Crear y registrar el paquete. Luego construye el sitio host.

    pnpm build

    Abre cada superficie de administración del plugin y renderiza cada bloque Portable Text aportado. Registrar antes del build hace que Astro resuelva las exportaciones ./admin y ./astro del tarball; una prueba solo de servidor del paquete no puede detectar un archivo fuente de navegador o .astro faltante.

Contenidos del README

Da a un operador suficiente información para instalar y evaluar el paquete sin leer su código fuente. Incluye:

  • una descripción de una frase y la versión de EmDash admitida
  • el comando de instalación y el registro completo en astro.config.mjs
  • el límite de confianza nativo y por qué el plugin necesita ejecución nativa
  • cada capability declarada y host permitido, con la función que lo usa
  • ajustes y sus valores predeterminados
  • componentes de layout necesarios, como EmDashBodyEnd para un fragmento body-end
  • pasos de actualización para cambios que requieren acción del operador

No describas las declaraciones de capability como un límite de aislamiento. Limitan las API de ctx, pero el código nativo aún puede usar imports, variables de entorno y llamadas de red directas disponibles para el proceso host.

Publicar en npm

Publica después de que pase la prueba del tarball:

npm publish --access public

La primera publicación pública de un paquete con scope necesita --access public. Usa versionado semántico para releases posteriores. Trata los cambios en opciones del constructor, datos almacenados, cambios requeridos del host, exportaciones del paquete o requisitos de confianza del plugin como decisiones de compatibilidad. Si una actualización requiere una nueva capability o host permitido, indícalo en las notas de la release aunque las instalaciones nativas no tengan un prompt de consentimiento de capabilities.

Instalar desde npm

Un operador instala el paquete publicado en el sitio EmDash:

pnpm add @example/plugin-activity

Luego importa y registra su factory del descriptor en astro.config.mjs como se muestra en Crear y registrar el paquete. Instalar la dependencia sola no activa el plugin; cambiar la configuración de Astro y desplegar el sitio completa la instalación.

Desarrollar contra un sitio host

Construye el plugin en modo watch:

pnpm dev

Instala el directorio local desde el sitio host:

pnpm add ../plugin-activity

Registra la factory del descriptor del plugin en astro.config.mjs y luego arranca el servidor de desarrollo del host. Reinicia el servidor tras cambiar metadatos del descriptor o exportaciones del paquete. Si una dependencia de archivo del gestor de paquetes copia archivos en lugar de enlazarlos en tu configuración, reinstálala tras reconstruir; una dependencia de workspace o pnpm link mantiene el paquete local conectado durante el desarrollo.

Límite del registro

Los paquetes nativos no se pueden publicar en el registro de EmDash. Los plugins del registro usan el formato de paquete sandboxed, el flujo de release firmado y el flujo de consentimiento de instalación. Si el plugin ya no necesita código de administración React, renderizadores Astro, fragmentos de confianza u otra dependencia in-process, conviértelo al formato sandboxed antes de publicar a través del registro.