Migrar para a CLI de plugins

Nesta página

Este guia é para autores de plugins sandboxed escritos contra a forma anterior de definePlugin(). Percorra as alterações incompatíveis em ordem. Nenhuma muda o comportamento dos hooks ou rotas em runtime; mudam como o plugin é declarado, construído e publicado.

Para a lista completa de alterações em cada pacote, consulte a entrada na página de releases.

Alterações incompatíveis

Renomeado: @emdash-cms/registry-cli agora é @emdash-cms/plugin-cli

Releases anteriores distribuíam a CLI como @emdash-cms/registry-cli, com um binário emdash-registry.

O pacote agora é @emdash-cms/plugin-cli e o binário é emdash-plugin. O pacote antigo não é mais publicado.

O que devo fazer?

Substitua a dependência:

pnpm remove @emdash-cms/registry-cli
pnpm add -D @emdash-cms/plugin-cli

Substitua emdash-registry por emdash-plugin em todos os lugares onde o chamar. Cada subcomando mantém o nome (bundle, publish, login, whoami, switch, validate), e init, build e dev são adicionados. Veja The plugin CLI.

Renomeado: nomes de capabilities usam ortografia resource-first

Manifestos anteriores usavam nomes de capability como read:content e network:fetch. O manifesto de authoring aceita apenas os nomes atuais, embora o runtime ainda normalize nomes legacy em bundles já publicados durante a janela de compatibilidade.

O que devo fazer?

Substitua cada nome legacy no manifesto:

Nome anteriorNome atual
network:fetchnetwork:request
network:fetch:anynetwork:request:unrestricted
read:contentcontent:read
write:contentcontent:write
read:mediamedia:read
write:mediamedia:write
read:usersusers:read
email:providehooks.email-transport:register
email:intercepthooks.email-events:register
page:injecthooks.page-fragments:register

Use network:request com uma lista allowedHosts não vazia. Use network:request:unrestricted com uma lista vazia somente quando um operador escolher o destino em runtime. Capabilities and security explica as permissões e regras de rede atuais.

Alterado: plugins sandboxed usam uma anotação explícita SandboxedPlugin

Releases anteriores envolviam os hooks e rotas do plugin em definePlugin() importado de emdash, com os parâmetros de cada handler anotados à mão.

Um plugin sandboxed atribui sua definição a uma constante tipada como SandboxedPlugin e exporta essa constante como default. Importe o tipo de emdash/plugin com import type; o bundler apaga essa importação. O mesmo subcaminho também exporta os helpers de runtime leves pluginRoute() e pluginResponse(). O TypeScript infere o event e o ctx de cada handler a partir do nome do hook ou rota, portanto os parâmetros do handler não precisam de anotações. A anotação explícita também mantém declarações geradas portáteis sob layouts isolados do gerenciador de pacotes.

O que devo fazer?

Faça quatro alterações no arquivo-fonte do plugin. Substitua a importação:

import { definePlugin, type ContentHookEvent, type PluginContext } from "emdash";
import type { SandboxedPlugin } from "emdash/plugin";

Substitua o wrapper definePlugin() por uma constante tipada explicitamente:

export default definePlugin({ /* hooks, routes */ });
const plugin: SandboxedPlugin = { /* hooks, routes */ };

export default plugin;

Remova as anotações de parâmetro de cada handler:

handler: async (event: ContentHookEvent, ctx: PluginContext) => {
handler: async (event, ctx) => {

O resultado é um objeto exportado por padrão:

import type { SandboxedPlugin } from "emdash/plugin";

const plugin: SandboxedPlugin = {
	hooks: {
		"content:beforeSave": {
			handler: async (event, ctx) => {
				return event.content;
			},
		},
	},
};

export default plugin;

Para nomear um tipo de evento em uma função auxiliar, importe-o de emdash/plugin:

import type { ContentHookEvent, PluginContext } from "emdash/plugin";

O event de um handler é sempre o tipo canônico daquele hook. Anotar um handler com uma interface mais estreita não passa mais no type-check. Valide em runtime quaisquer campos de que dependa com uma verificação typeof ou um guard, a abordagem correta para dados que vêm de fora do sistema de tipos.

Alterado: um plugin é um src/plugin.ts mais emdash-plugin.jsonc

Releases anteriores dividiam um plugin em dois arquivos: src/index.ts retornava um PluginDescriptor (id, version, capabilities, storage, entrypoint), e src/sandbox-entry.ts continha os hooks e rotas.

Um plugin agora é um arquivo de runtime, src/plugin.ts (hooks e rotas), e um manifesto editado à mão, emdash-plugin.jsonc (identidade e o contrato de confiança). Os campos entrypoint e format sumiram; o build os conecta.

O que devo fazer?

Mova os hooks e rotas para src/plugin.ts usando a forma acima. Mova os metadados do descriptor para emdash-plugin.jsonc ao lado de package.json. O id do descriptor vira o slug do manifesto; capabilities, allowedHosts e storage mantêm a forma; version é lida de package.json, então omita.

O exemplo a seguir mostra o equivalente em manifesto de um descriptor que declarava uma collection de storage:

{
	"$schema": "./node_modules/@emdash-cms/plugin-cli/schemas/emdash-plugin.schema.json",

	"slug": "plugin-hello",
	"publisher": "did:plc:abc123def456",

	"license": "MIT",
	"author": { "name": "Jane Doe", "url": "https://example.com" },
	"security": { "email": "security@example.com" },

	"capabilities": [],
	"allowedHosts": [],
	"storage": { "events": { "indexes": ["timestamp"] } }
}

Veja The plugin manifest para cada campo, e Publisher pinning para o campo publisher.

Em package.json, aponte o export "./sandbox" para o arquivo de runtime construído:

"./sandbox": "./dist/sandbox-entry.mjs"
"./sandbox": "./dist/plugin.mjs"

Adicione o manifesto a files para que seja distribuído com o pacote:

"files": ["dist"]
"files": ["dist", "emdash-plugin.jsonc"]

Alterado: construir com emdash-plugin build

Releases anteriores construíam os dois arquivos-fonte com um script tsdown escrito à mão.

emdash-plugin build lê emdash-plugin.jsonc e src/plugin.ts e emite os artefatos dist/. emdash-plugin dev observa e reconstrói.

O que devo fazer?

Substitua o script de build e adicione um script de watch:

"scripts": {
	"build": "tsdown src/index.ts src/sandbox-entry.ts --format esm --dts --clean"
	"build": "emdash-plugin build",
	"dev": "emdash-plugin dev"
}

Em seguida, valide e construa:

emdash-plugin validate
emdash-plugin build

Removido: exports de tipos e funções de formato padrão de emdash

Releases anteriores exportavam StandardPluginDefinition, StandardHookHandler, StandardHookEntry, StandardRouteHandler, StandardRouteEntry e a função isStandardPluginDefinition de emdash.

Eles foram removidos. Eram aliases auxiliares para a forma anterior de definePlugin.

O que devo fazer?

Use SandboxedPlugin de emdash/plugin para o mesmo propósito. A definição exportada de um plugin sandboxed já é tipada pela anotação SandboxedPlugin, então não há substituto para isStandardPluginDefinition; identifique um plugin pela estrutura ({ hooks?, routes? }) se precisar.

Renomeado: handles do sandbox-runner usam SandboxedPluginInstance

Isso afeta apenas autores de um SandboxRunner personalizado, como @emdash-cms/cloudflare. A maioria dos autores de plugins pode pular.

O tipo voltado ao autor SandboxedPlugin está disponível no ponto de entrada de authoring emdash/plugin. O handle de runtime retornado por SandboxRunner.load é exportado de emdash como SandboxedPluginInstance.

O que devo fazer?

Se você importa SandboxedPlugin de emdash para tipar um sandbox runner ou manter handles de plugins em runtime, mude a importação para SandboxedPluginInstance:

import type { SandboxedPlugin } from "emdash";
import type { SandboxedPluginInstance } from "emdash";

Informe seus usuários

Sites que instalam seu plugin também precisam mudar a importação. Aponte-os para a nova forma: remova as chaves e o ().

import { helloPlugin } from "@my-org/plugin-hello";
import hello from "@my-org/plugin-hello";

export default defineConfig({
	integrations: [
		emdash({
			sandboxed: [helloPlugin()],
			sandboxed: [hello],
		}),
	],
});

Se seu plugin aceitava configuração pela factory, mova essa configuração para uma página de configurações de administração e leia de ctx.settings. Descriptors de plugins sandboxed são objetos plain e não podem receber opções de construtor. Veja Settings.

Verificar o plugin migrado

Execute os testes do plugin, valide o manifesto de authoring e execute as verificações completas de build e bundle:

pnpm test
pnpm exec emdash-plugin validate
pnpm exec emdash-plugin build
pnpm exec emdash-plugin bundle --validate-only

Em seguida, instale o pacote local em um site de desenvolvimento e exercite cada hook e rota migrados. O build pode confirmar nomes e formas, mas não pode confirmar que uma rota retorna os dados pretendidos ou que um hook preserva o conteúdo corretamente.

Próximo