Fragments de page

Sur cette page

Le hook page:fragments apporte des scripts ou du HTML brut au head ou au body d’une page publique rendue. Utilisez-le pour du code navigateur que les métadonnées structurées ne peuvent pas exprimer, comme un chargeur d’analytique avec un repli <noscript>.

La sortie des fragments s’exécute comme code de première partie de la page. EmDash n’invoque ce hook que pour les plugins de confiance en processus ; les plugins sandboxés n’apportent jamais de fragments. Si la page a besoin de balises meta, de liens canoniques ou alternate, ou de JSON-LD, utilisez plutôt le hook page:metadata compatible sandbox.

Déclarer la capacité et le hook

page:fragments exige hooks.page-fragments:register dans la définition du runtime natif. Le hook suivant ajoute un script externe et un repli HTML uniquement lorsqu’un ID d’analytique configuré existe :

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

Les contributions HTML brutes sont insérées telles quelles. Gardez le fragment statique lorsque c’est possible ; sinon, échappez toute valeur provenant des paramètres, du contenu, des données de requête ou d’un service externe.

Pour les plugins natifs, déclarez la capacité dans definePlugin(). Le descripteur n’a pas besoin d’une seconde copie car createPlugin() fournit la liste des capacités du runtime.

Si la capacité manque, EmDash consigne un avertissement et n’enregistre pas le hook. Les erreurs de hook sont consignées et n’empêchent pas le rendu de la page.

Ajouter les points d’insertion de la mise en page

Le thème hôte choisit les placements qu’il prend en charge. Il doit passer le même PublicPageContext aux composants EmDash correspondants :

  1. Construisez le contexte de page une fois dans la mise en page.

    ---
    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. Rendez chaque placement pris en charge dans la partie correspondante du document.

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

EmDashHead rend les fragments head ainsi que les métadonnées EmDash et de plugins. EmDashBodyStart rend les fragments body:start juste après le <body> d’ouverture, et EmDashBodyEnd rend les fragments body:end après le contenu de la page. Un thème qui omet un composant ne rend pas les fragments pour ce placement. Documentez tout point d’insertion requis dans le README du plugin.

Référence des contributions

Un hook peut renvoyer une contribution, un tableau ou null. Les formes de contribution prises en charge sont :

kindChamps obligatoiresChamps optionnelsComportement de sortie
external-scriptplacement, srcasync, defer, attributes, keyRend un élément <script src="…">.
inline-scriptplacement, codeattributes, keyRend code dans un élément <script>.
htmlplacement, htmlkeyInsère html sans le sanitizer.

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

EmDash échappe en HTML les noms et valeurs d’attributs et supprime les attributs dont le nom commence par on. Pour les scripts en ligne, il échappe </ pour qu’une valeur ne puisse pas fermer l’élément script. Ces contrôles du moteur de rendu ne rendent pas sûr du HTML ou du JavaScript non fiables ; le plugin doit toujours encoder les données interpolées pour le langage et le contexte où elles sont insérées.

Le hook suivant place en toute sécurité une valeur JSON dans un script en ligne :

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

Au sein d’un placement, les contributions avec la même key conservent la première valeur. Les scripts externes sans clé sont aussi dédupliqués par src. Utilisez une clé stable lorsque deux hooks pourraient décrire le même fragment logique.

Référence du contexte de page

Le hook reçoit { page }. L’objet page provient de la mise en page hôte, avec des valeurs optionnelles du panneau SEO superposées pour une entrée de contenu chargée pendant la requête.

ChampType et signification
urlURL absolue de la page.
pathChemin d’URL.
localeLocale active, ou null.
kindcontent pour une entrée EmDash ou custom pour une autre page.
pageTypeType défini par le thème, souvent article ou website.
title, pageTitleTitre complet du document et titre optionnel propre à la page.
description, canonical, imageValeurs de métadonnées de page, chacune nullable.
contentRéférence optionnelle { collection, id, slug } de l’entrée rendue.
seoRemplacements optionnels de titre, description, image et robots Open Graph.
articleMetaHeure de publication, heure de modification et auteur optionnels.
siteName, siteUrlIdentité publique optionnelle du site et origine.
breadcrumbsÉléments optionnels root-first { name, url }. Un tableau vide signifie explicitement aucune fil d’Ariane.

Utilisez kind, pageType, path, locale ou content pour limiter où un fragment apparaît. Ne récupérez pas une entrée à nouveau lorsque le contexte de page porte déjà l’identité nécessaire à la décision.

Préférer les métadonnées structurées lorsque c’est possible

Utilisez page:metadata pour les sorties suivantes :

  • balises <meta name="…"> et <meta property="…">
  • liens canonical, alternate, author, license, nlweb et site.standard.document
  • graphes JSON-LD

page:metadata valide ses contributions structurées, les déduplique avec les métadonnées de base de la page, et fonctionne dans les plugins natifs et sandboxés. Utilisez page:fragments lorsque le navigateur doit recevoir du code exécutable ou du balisage que le hook structuré ne peut pas représenter.