import { type Block, checkEmbedBlock, checkImageBlock, isLinkContent, Link, LinkContent, } from "@prismicio/types-internal/lib/content" import * as E from "fp-ts/lib/Either" import { type RenderContext, ApiVersion } from "../../../models" import { defaultLink } from "../../../models" import { writeEmptyStringOrNull } from "../../../utils" import LinkRenderer from "../Link" function linkType(link: LinkContent) { if (isLinkContent(link)) { switch (link.value.__TYPE__) { case "DocumentLink": return "document" case "ExternalLink": return "web" case "FileLink": return "media" case "ImageLink": return "media" default: return null } } throw new Error(`[UNREACHABLE] unknown Link type for ${JSON.stringify(link)}`) } function renderStructuredTextLink(link: LinkContent, apiVersion: ApiVersion, ctx: RenderContext) { if (apiVersion === ApiVersion.v1) { return LinkRenderer(ctx).renderV1(link, undefined, { omitKey: true }) } else if (apiVersion === ApiVersion.v2) { return LinkRenderer(ctx).renderV2(defaultLink(linkType(link)), link, undefined, { omitKey: true }) } else if (apiVersion === ApiVersion.mocks) { return LinkRenderer(ctx).renderMocks(defaultLink(linkType(link)), link, { omitKey: true }) } } const BlockRenderer = (ctx: RenderContext) => ({ renderBlock(block: Block): (apiVersion: ApiVersion) => unknown { return (apiVersion: ApiVersion) => { if (checkImageBlock(block)) { const linkTo = (() => { if (block.data.linkTo) { const content: LinkContent = { __TYPE__: "LinkContent", // When a Link is used within a StructuredText, the key is not present, // we need to specify one to respect LinkContent type // but the Renderer will not use it thanks to `omitKey` value. key: "", value: block.data.linkTo, } return { linkTo: renderStructuredTextLink(content, apiVersion, ctx), } } return {} })() const label = block.label ? { label: block.label } : {} const direction = block.direction ? { direction: block.direction } : {} const contentV2 = apiVersion === ApiVersion.v2 ? { id: block.data.origin.id, edit: { x: block.data.edit.crop.x, y: block.data.edit.crop.y, zoom: block.data.edit.zoom, background: block.data.edit.background, }, } : {} return { type: "image", url: ctx.urlRewriter.rewriteImageUrl(block.data), alt: writeEmptyStringOrNull(block.data.alt, ctx.emptyStringInsteadOfNull), copyright: writeEmptyStringOrNull(block.data.credits, ctx.emptyStringInsteadOfNull), dimensions: { width: block.data.width, height: block.data.height, }, ...contentV2, ...linkTo, ...label, ...direction, } } else if (checkEmbedBlock(block)) { const label = block.label ? { label: block.label } : {} const direction = block.direction ? { direction: block.direction } : {} return { type: "embed", oembed: block.data.all, ...label, ...direction, } } else { // TextBlock const label = block.label ? { label: block.label } : {} const direction = block.direction ? { direction: block.direction } : {} const spans = (block.content.spans || []).map((m) => { const partial = { start: m.start, end: m.end, type: m.type, } if (m.type === "hyperlink") { const asMaybeLink = Link.decode(m.data) const asLink = (() => { if (E.isRight(asMaybeLink)) { const content = { __TYPE__: "LinkContent", value: asMaybeLink.right, } as LinkContent return renderStructuredTextLink(content, apiVersion, ctx) } return {} })() return { ...partial, data: asLink, } } else if (m.type === "label") { return { ...partial, data: { label: typeof m.data === "string" ? m.data : "" }, } } else { return partial } }) return { type: block.type, text: block.content.text, ...label, spans, ...direction, } } } }, }) export default BlockRenderer