Fragmentos de página

Nesta página

O hook page:fragments fornece scripts ou HTML bruto ao head ou body de uma página pública renderizada. Use-o para código de navegador que metadados estruturados não conseguem expressar, como um carregador de analytics com um fallback <noscript>.

A saída dos fragmentos é executada como código de primeira parte da página. EmDash chama este hook apenas para plugins confiáveis in-process; plugins sandboxed nunca contribuem com fragmentos. Se a página precisar de meta tags, links canonical ou alternate, ou JSON-LD, use em vez disso o hook page:metadata compatível com sandbox.

Declarar a capability e o hook

page:fragments requer hooks.page-fragments:register na definição do runtime nativo. O hook a seguir adiciona um script externo e um fallback HTML somente quando existe um ID de analytics configurado:

return definePlugin({
	id: "plugin-analytics",
	version: "0.1.0",
	capabilities: ["hooks.page-fragments:register"],
	hooks: {
		"page:fragments": async (event, ctx) => {
			const analyticsId = await ctx.settings.get<string>("analyticsId");
			if (!analyticsId || event.page.path.startsWith("/_emdash/")) return null;

			const encodedId = encodeURIComponent(analyticsId);
			return [
				{
					kind: "external-script",
					placement: "head",
					src: `https://analytics.example.com/client.js?id=${encodedId}`,
					async: true,
					key: "analytics-client",
				},
				{
					kind: "html",
					placement: "body:end",
					html: "<noscript>Analytics requires JavaScript.</noscript>",
					key: "analytics-fallback",
				},
			];
		},
	},
});

Contribuições de HTML bruto são inseridas verbatim. Mantenha o fragmento estático quando possível; caso contrário, faça escape de qualquer valor que possa vir de configurações, conteúdo, dados da requisição ou um serviço externo.

Para plugins nativos, declare a capability em definePlugin(). O descriptor não precisa de uma segunda cópia porque createPlugin() fornece a lista de capabilities do runtime.

Se a capability estiver ausente, EmDash registra um aviso e não registra o hook. Erros do hook são registrados e não impedem a renderização da página.

Adicionar os pontos de inserção do layout

O tema host escolhe quais placements oferece suporte. Ele deve passar o mesmo PublicPageContext aos componentes EmDash correspondentes:

  1. Construa o contexto da página uma vez no layout.

    ---
    import { createPublicPageContext } from "emdash/page";
    import {
        EmDashBodyEnd,
        EmDashBodyStart,
        EmDashHead,
    } from "emdash/ui";
    
    interface Props {
        title: string;
        description?: string;
        content?: { collection: string; id: string; slug?: string | null };
    }
    
    const { title, description, content } = Astro.props;
    const page = createPublicPageContext({
        Astro,
        kind: content ? "content" : "custom",
        pageType: content ? "article" : "website",
        title,
        description,
        content,
    });
    ---
  2. Renderize cada placement suportado na parte correspondente do documento.

    <html lang="en">
        <head>
            <title>{title}</title>
            <EmDashHead page={page} />
        </head>
        <body>
            <EmDashBodyStart page={page} />
            <slot />
            <EmDashBodyEnd page={page} />
        </body>
    </html>

EmDashHead renderiza fragmentos head bem como metadados EmDash e de plugins. EmDashBodyStart renderiza fragmentos body:start logo após o <body> de abertura, e EmDashBodyEnd renderiza fragmentos body:end após o conteúdo da página. Um tema que omite um componente não renderiza fragmentos para aquele placement. Documente quaisquer pontos de inserção necessários no README do plugin.

Referência de contribuições

Um hook pode retornar uma contribuição, um array ou null. As formas de contribuição suportadas são:

kindCampos obrigatóriosCampos opcionaisComportamento da saída
external-scriptplacement, srcasync, defer, attributes, keyRenderiza um elemento <script src="…">.
inline-scriptplacement, codeattributes, keyRenderiza code dentro de um elemento <script>.
htmlplacement, htmlkeyInsere html sem sanitizá-lo.

placement aceita head, body:start ou body:end.

EmDash faz escape HTML dos nomes e valores de atributos e remove atributos cujos nomes começam com on. Para scripts inline, faz escape de </ para que um valor não possa fechar o elemento script. Essas verificações do renderer não tornam HTML ou JavaScript não confiáveis seguros; o plugin ainda deve codificar dados interpolados para a linguagem e o contexto em que são inseridos.

O hook a seguir coloca com segurança um valor JSON em um script inline:

"page:fragments": async (event) => {
	if (event.page.kind !== "content" || !event.page.content) return null;

	return {
		kind: "inline-script",
		placement: "body:start",
		code: `window.currentContent = ${JSON.stringify({
			collection: event.page.content.collection,
			id: event.page.content.id,
		})};`,
		key: "current-content",
	};
},

Dentro de um placement, contribuições com a mesma key mantêm o primeiro valor. Scripts externos sem chave também são deduplicados por src. Use uma chave estável quando dois hooks puderem descrever o mesmo fragmento lógico.

Referência do contexto de página

O hook recebe { page }. O objeto page vem do layout host, com valores opcionais do painel SEO sobrepostos para uma entrada de conteúdo carregada durante a requisição.

CampoTipo e significado
urlURL absoluto da página.
pathPathname da URL.
localeLocale ativo, ou null.
kindcontent para uma entrada EmDash ou custom para outra página.
pageTypeTipo definido pelo tema, geralmente article ou website.
title, pageTitleTítulo completo do documento e título opcional só da página.
description, canonical, imageValores de metadados da página, cada um nullable.
contentReferência opcional { collection, id, slug } da entrada renderizada.
seoSubstituições opcionais de título, descrição, imagem e robots Open Graph.
articleMetaHora de publicação, hora de modificação e autor opcionais.
siteName, siteUrlIdentidade pública opcional do site e origin.
breadcrumbsItens opcionais root-first { name, url }. Um array vazio significa explicitamente nenhum breadcrumb.

Use kind, pageType, path, locale ou content para limitar onde um fragmento aparece. Não busque novamente uma entrada quando o contexto da página já carrega a identidade necessária para a decisão.

Preferir metadados estruturados quando possível

Use page:metadata para a seguinte saída:

  • tags <meta name="…"> e <meta property="…">
  • links canonical, alternate, author, license, nlweb e site.standard.document
  • grafos JSON-LD

page:metadata valida suas contribuições estruturadas, as deduplica com os metadados base da página e funciona em plugins nativos e sandboxed. Use page:fragments quando o navegador precisar receber código executável ou markup que o hook estruturado não consegue representar.