Fragmentos de página

En esta página

El hook page:fragments aporta scripts o HTML en bruto al head o al body de una página pública renderizada. Úsalo para código del navegador que los metadatos estructurados no pueden expresar, como un cargador de analítica con un respaldo <noscript>.

La salida de los fragmentos se ejecuta como código de primera parte de la página. EmDash invoca este hook solo para plugins de confianza en proceso; los plugins en sandbox nunca aportan fragmentos. Si la página necesita meta tags, enlaces canónicos o alternate, o JSON-LD, usa en su lugar el hook page:metadata compatible con sandbox.

Declarar la capacidad y el hook

page:fragments requiere hooks.page-fragments:register en la definición del runtime nativo. El siguiente hook añade un script externo y un respaldo HTML solo cuando existe un ID de analítica 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",
				},
			];
		},
	},
});

Las aportaciones de HTML en bruto se insertan tal cual. Mantén el fragmento estático cuando sea posible; de lo contrario, escapa todo valor que pueda proceder de ajustes, contenido, datos de la solicitud o un servicio externo.

Para plugins nativos, declara la capacidad en definePlugin(). El descriptor no necesita una segunda copia porque createPlugin() suministra la lista de capacidades del runtime.

Si falta la capacidad, EmDash registra una advertencia y no registra el hook. Los errores del hook se registran y no impiden que la página se renderice.

Añadir los puntos de inserción del diseño

El tema anfitrión elige qué placements admite. Debe pasar el mismo PublicPageContext a los componentes EmDash correspondientes:

  1. Construye el contexto de la página una vez en el diseño.

    ---
    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. Renderiza cada placement admitido en la parte correspondiente del 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 así como metadatos de EmDash y de plugins. EmDashBodyStart renderiza fragmentos body:start justo después del <body> de apertura, y EmDashBodyEnd renderiza fragmentos body:end después del contenido de la página. Un tema que omite un componente no renderiza fragmentos para ese placement. Documenta cualquier punto de inserción requerido en el README del plugin.

Referencia de aportaciones

Un hook puede devolver una aportación, una matriz o null. Las formas de aportación admitidas son:

kindCampos obligatoriosCampos opcionalesComportamiento de salida
external-scriptplacement, srcasync, defer, attributes, keyRenderiza un elemento <script src="…">.
inline-scriptplacement, codeattributes, keyRenderiza code dentro de un elemento <script>.
htmlplacement, htmlkeyInserta html sin sanitizarlo.

placement acepta head, body:start o body:end.

EmDash escapa en HTML los nombres y valores de atributos y elimina los atributos cuyos nombres empiezan por on. En los scripts en línea, escapa </ para que un valor no pueda cerrar el elemento script. Estas comprobaciones del renderizador no hacen seguro el HTML o JavaScript no fiables; el plugin debe seguir codificando los datos interpolados para el lenguaje y el contexto donde se insertan.

El siguiente hook coloca de forma segura un valor JSON en un script en línea:

"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 un placement, las aportaciones con la misma key conservan el primer valor. Los scripts externos sin clave también se deduplican por src. Usa una clave estable cuando dos hooks puedan describir el mismo fragmento lógico.

Referencia del contexto de página

El hook recibe { page }. El objeto page proviene del diseño anfitrión, con valores opcionales del panel SEO superpuestos para una entrada de contenido cargada durante la solicitud.

CampoTipo y significado
urlURL absoluta de la página.
pathNombre de ruta de la URL.
localeLocale activo, o null.
kindcontent para una entrada EmDash o custom para otra página.
pageTypeTipo definido por el tema, habitualmente article o website.
title, pageTitleTítulo completo del documento y título opcional solo de página.
description, canonical, imageValores de metadatos de la página, cada uno nullable.
contentReferencia opcional { collection, id, slug } de la entrada renderizada.
seoAnulaciones opcionales de título, descripción, imagen y robots de Open Graph.
articleMetaTiempo de publicación, tiempo de modificación y autor opcionales.
siteName, siteUrlIdentidad pública opcional del sitio y origen.
breadcrumbsElementos opcionales root-first { name, url }. Una matriz vacía significa explícitamente que no hay breadcrumbs.

Usa kind, pageType, path, locale o content para limitar dónde aparece un fragmento. No vuelvas a obtener una entrada cuando el contexto de la página ya lleva la identidad necesaria para la decisión.

Preferir metadatos estructurados cuando sea posible

Usa page:metadata para la siguiente salida:

  • etiquetas <meta name="…"> y <meta property="…">
  • enlaces canonical, alternate, author, license, nlweb y site.standard.document
  • grafos JSON-LD

page:metadata valida sus aportaciones estructuradas, las deduplica con los metadatos base de la página y funciona en plugins nativos y en sandbox. Usa page:fragments cuando el navegador deba recibir código ejecutable o marcado que el hook estructurado no pueda representar.