page:fragments 훅은 렌더링된 공개 페이지의 head 또는 body에 스크립트나 원시 HTML을 제공합니다. 구조화된 메타데이터로 표현할 수 없는 브라우저 코드, 예를 들어 <noscript> 폴백이 있는 애널리틱스 로더에 사용하세요.
프래그먼트 출력은 퍼스트파티 페이지 코드로 실행됩니다. EmDash는 이 훅을 신뢰할 수 있는 인프로세스 플러그인에만 호출하며, 샌드박스 플러그인은 프래그먼트를 기여하지 않습니다. 페이지에 meta 태그, canonical·alternate 링크, JSON-LD가 필요하면 대신 샌드박스 호환 page:metadata 훅을 사용하세요.
기능과 훅 선언하기
page:fragments에는 네이티브 런타임 정의의 hooks.page-fragments:register가 필요합니다. 다음 훅은 구성된 애널리틱스 ID가 있을 때만 외부 스크립트와 HTML 폴백을 추가합니다.
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",
},
];
},
},
});
원시 HTML 기여는 그대로 삽입됩니다. 가능하면 프래그먼트를 정적으로 유지하고, 그렇지 않으면 설정·콘텐츠·요청 데이터·외부 서비스에서 올 수 있는 모든 값을 이스케이프하세요.
네이티브 플러그인에서는 definePlugin()에서 기능을 선언합니다. createPlugin()이 런타임 기능 목록을 제공하므로 디스크립터에 두 번째 사본은 필요하지 않습니다.
기능이 없으면 EmDash는 경고를 기록하고 훅을 등록하지 않습니다. 훅 오류는 기록되며 페이지 렌더링을 막지 않습니다.
레이아웃 삽입 지점 추가하기
호스트 테마가 지원할 placement를 선택합니다. 해당하는 EmDash 컴포넌트에 동일한 PublicPageContext를 전달해야 합니다.
-
레이아웃에서 페이지 컨텍스트를 한 번 구성합니다.
--- 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, }); --- -
지원하는 각 placement를 문서의 해당 위치에 렌더링합니다.
<html lang="en"> <head> <title>{title}</title> <EmDashHead page={page} /> </head> <body> <EmDashBodyStart page={page} /> <slot /> <EmDashBodyEnd page={page} /> </body> </html>
EmDashHead는 head 프래그먼트와 함께 EmDash 및 플러그인 메타데이터를 렌더링합니다. EmDashBodyStart는 여는 <body> 직후에 body:start 프래그먼트를, EmDashBodyEnd는 페이지 콘텐츠 뒤에 body:end 프래그먼트를 렌더링합니다. 컴포넌트를 생략한 테마는 해당 placement의 프래그먼트를 렌더링하지 않습니다. 필요한 삽입 지점은 플러그인 README에 문서화하세요.
기여 참조
훅은 하나의 기여, 배열 또는 null을 반환할 수 있습니다. 지원되는 기여 형태는 다음과 같습니다.
kind | 필수 필드 | 선택 필드 | 출력 동작 |
|---|---|---|---|
external-script | placement, src | async, defer, attributes, key | <script src="…"> 요소를 렌더링합니다. |
inline-script | placement, code | attributes, key | <script> 요소 안에 code를 렌더링합니다. |
html | placement, html | key | html을 살균하지 않고 삽입합니다. |
placement는 head, body:start, body:end를 받습니다.
EmDash는 속성 이름과 값을 HTML 이스케이프하고, 이름이 on으로 시작하는 속성을 제거합니다. 인라인 스크립트에서는 </를 이스케이프하여 값이 스크립트 요소를 닫지 못하게 합니다. 이러한 렌더러 검사는 신뢰할 수 없는 HTML이나 JavaScript를 안전하게 만들지 않습니다. 플러그인은 삽입되는 언어와 컨텍스트에 맞게 보간 데이터를 계속 인코딩해야 합니다.
다음 훅은 인라인 스크립트에 JSON 값을 안전하게 배치합니다.
"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",
};
},
한 placement 안에서 같은 key의 기여는 첫 번째 값을 유지합니다. 키가 없는 외부 스크립트는 src로도 중복 제거됩니다. 두 훅이 같은 논리 프래그먼트를 설명할 수 있으면 안정적인 키를 사용하세요.
페이지 컨텍스트 참조
훅은 { page }를 받습니다. page 객체는 호스트 레이아웃에서 오며, 요청 중에 로드된 콘텐츠 항목에는 SEO 패널 값을 선택적으로 겹칩니다.
| 필드 | 타입과 의미 |
|---|---|
url | 절대 페이지 URL. |
path | URL의 pathname. |
locale | 활성 로케일, 또는 null. |
kind | EmDash 항목이면 content, 다른 페이지면 custom. |
pageType | 테마 정의 타입. 보통 article 또는 website. |
title, pageTitle | 전체 문서 제목과 선택적 페이지 전용 제목. |
description, canonical, image | 페이지 메타데이터 값. 각각 nullable. |
content | 렌더링된 항목의 선택적 { collection, id, slug } 참조. |
seo | Open Graph 제목·설명·이미지·robots의 선택적 덮어쓰기. |
articleMeta | 선택적 게시 시각·수정 시각·작성자. |
siteName, siteUrl | 선택적 공개 사이트 신원과 origin. |
breadcrumbs | 선택적 root-first { name, url } 항목. 빈 배열은 명시적으로 빵 부스러기 없음을 의미합니다. |
kind, pageType, path, locale, content로 프래그먼트가 나타나는 위치를 제한하세요. 페이지 컨텍스트가 결정에 필요한 신원을 이미 가지고 있으면 항목을 다시 가져오지 마세요.
가능하면 구조화된 메타데이터를 우선하세요
다음 출력에는 page:metadata를 사용하세요.
<meta name="…">및<meta property="…">태그- canonical, alternate, author, license,
nlweb,site.standard.document링크 - JSON-LD 그래프
page:metadata는 구조화된 기여를 검증하고, 페이지 기본 메타데이터와 중복을 제거하며, 네이티브와 샌드박스 플러그인 모두에서 동작합니다. 구조화된 훅이 표현할 수 없는 실행 가능 코드나 마크업을 브라우저가 받아야 할 때 page:fragments를 사용하세요.