@storyblok/richtext
Version:
Storyblok RichText Resolver
257 lines (256 loc) • 9.42 kB
JavaScript
/**
* name: @storyblok/richtext
* (c) 2025
* description: Storyblok RichText Resolver
* author: Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)
*/
function k(e, n) {
if (!n)
return { src: e, attrs: {} };
let m = 0, S = 0;
const g = {}, c = [];
function v(l, E, $, L, I) {
typeof l != "number" || l <= E || l >= $ ? console.warn(`[StoryblokRichText] - ${L.charAt(0).toUpperCase() + L.slice(1)} value must be a number between ${E} and ${$} (inclusive)`) : I.push(`${L}(${l})`);
}
if (typeof n == "object") {
if (typeof n.width == "number" && n.width > 0 ? (g.width = n.width, m = n.width) : console.warn("[StoryblokRichText] - Width value must be a number greater than 0"), n.height && typeof n.height == "number" && n.height > 0 ? (g.height = n.height, S = n.height) : console.warn("[StoryblokRichText] - Height value must be a number greater than 0"), n.loading && ["lazy", "eager"].includes(n.loading) && (g.loading = n.loading), n.class && (g.class = n.class), n.filters) {
const { filters: l } = n || {}, { blur: E, brightness: $, fill: L, format: I, grayscale: R, quality: O, rotate: C } = l || {};
E && v(E, 0, 100, "blur", c), O && v(O, 0, 100, "quality", c), $ && v($, 0, 100, "brightness", c), L && c.push(`fill(${L})`), R && c.push("grayscale()"), C && [0, 90, 180, 270].includes(n.filters.rotate || 0) && c.push(`rotate(${C})`), I && ["webp", "png", "jpeg"].includes(I) && c.push(`format(${I})`);
}
n.srcset && (g.srcset = n.srcset.map((l) => {
if (typeof l == "number")
return `${e}/m/${l}x0/${c.length > 0 ? `filters:${c.join(":")}` : ""} ${l}w`;
if (Array.isArray(l) && l.length === 2) {
const [E, $] = l;
return `${e}/m/${E}x${$}/${c.length > 0 ? `filters:${c.join(":")}` : ""} ${E}w`;
} else {
console.warn("[StoryblokRichText] - srcset entry must be a number or a tuple of two numbers");
return;
}
}).join(", ")), n.sizes && (g.sizes = n.sizes.join(", "));
}
let A = `${e}/m/`;
return m > 0 && S > 0 && (A = `${A}${m}x${S}/`), c.length > 0 && (A = `${A}filters:${c.join(":")}`), {
src: A,
attrs: g
};
}
var f = /* @__PURE__ */ ((e) => (e.DOCUMENT = "doc", e.HEADING = "heading", e.PARAGRAPH = "paragraph", e.QUOTE = "blockquote", e.OL_LIST = "ordered_list", e.UL_LIST = "bullet_list", e.LIST_ITEM = "list_item", e.CODE_BLOCK = "code_block", e.HR = "horizontal_rule", e.BR = "hard_break", e.IMAGE = "image", e.EMOJI = "emoji", e.COMPONENT = "blok", e.TABLE = "table", e.TABLE_ROW = "tableRow", e.TABLE_CELL = "tableCell", e.TABLE_HEADER = "tableHeader", e))(f || {}), b = /* @__PURE__ */ ((e) => (e.BOLD = "bold", e.STRONG = "strong", e.STRIKE = "strike", e.UNDERLINE = "underline", e.ITALIC = "italic", e.CODE = "code", e.LINK = "link", e.ANCHOR = "anchor", e.STYLED = "styled", e.SUPERSCRIPT = "superscript", e.SUBSCRIPT = "subscript", e.TEXT_STYLE = "textStyle", e.HIGHLIGHT = "highlight", e))(b || {}), y = /* @__PURE__ */ ((e) => (e.TEXT = "text", e))(y || {}), B = /* @__PURE__ */ ((e) => (e.SELF = "_self", e.BLANK = "_blank", e))(B || {}), w = /* @__PURE__ */ ((e) => (e.URL = "url", e.STORY = "story", e.ASSET = "asset", e.EMAIL = "email", e))(w || {});
const W = [
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"link",
"meta",
"param",
"source",
"track",
"wbr"
], X = (e = {}) => Object.keys(e).map((n) => `${n}="${e[n]}"`).join(" "), M = (e = {}) => Object.keys(e).map((n) => `${n}: ${e[n]}`).join("; ");
function J(e) {
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
}
const x = (e) => Object.fromEntries(Object.entries(e).filter(([n, m]) => m !== void 0));
function H(e, n = {}, m) {
const S = X(n), g = S ? `${e} ${S}` : e, c = Array.isArray(m) ? m.join("") : m || "";
if (e) {
if (W.includes(e))
return `<${g}>`;
} else return c;
return `<${g}>${c}</${e}>`;
}
function Q(e = {}) {
const n = /* @__PURE__ */ new Map(), {
renderFn: m = H,
textFn: S = J,
resolvers: g = {},
optimizeImages: c = !1,
keyedResolvers: v = !1
} = e, A = m !== H, l = (t) => (s, r) => {
const a = s.attrs || {};
return r.render(t, a, s.children || null);
}, E = (t, s) => {
const { src: r, alt: a, title: i, srcset: o, sizes: u } = t.attrs || {};
let d = r, h = {};
if (c) {
const { src: Y, attrs: q } = k(r, c);
d = Y, h = q;
}
const K = {
src: d,
alt: a,
title: i,
srcset: o,
sizes: u,
...h
};
return s.render("img", x(K));
}, $ = (t, s) => {
const { level: r, ...a } = t.attrs || {};
return s.render(`h${r}`, a, t.children);
}, L = (t, s) => {
var a, i, o, u;
const r = s.render("img", {
src: (a = t.attrs) == null ? void 0 : a.fallbackImage,
alt: (i = t.attrs) == null ? void 0 : i.alt,
style: "width: 1.25em; height: 1.25em; vertical-align: text-top",
draggable: "false",
loading: "lazy"
});
return s.render("span", {
"data-type": "emoji",
"data-name": (o = t.attrs) == null ? void 0 : o.name,
"data-emoji": (u = t.attrs) == null ? void 0 : u.emoji
}, r);
}, I = (t, s) => s.render(
"pre",
t.attrs || {},
s.render("code", {}, t.children || "")
), R = (t, s = !1) => ({ text: r, attrs: a }, i) => {
const { class: o, id: u, ...d } = a || {}, h = s ? {
class: o,
id: u,
style: M(d) || void 0
} : a || {};
return i.render(t, x(h), r);
}, O = (t) => _(t), C = (t) => {
const { marks: s, ...r } = t;
if ("text" in t) {
if (s)
return s.reduce(
(i, o) => O({ ...o, text: i }),
O({ ...r, children: r.children })
);
const a = t.attrs || {};
if (v) {
const i = n.get("txt") || 0;
n.set("txt", i + 1), a.key = `txt-${i}`;
}
return S(r.text, a);
}
return "";
}, j = (t, s) => {
const { linktype: r, href: a, anchor: i, ...o } = t.attrs || {};
let u = "";
switch (r) {
case w.ASSET:
case w.URL:
u = a;
break;
case w.EMAIL:
u = `mailto:${a}`;
break;
case w.STORY:
u = a, i && (u = `${u}#${i}`);
break;
default:
u = a;
break;
}
const d = { ...o };
return u && (d.href = u), s.render("a", d, t.text);
}, D = (t, s) => {
var r, a;
return console.warn("[StoryblokRichtText] - BLOK resolver is not available for vanilla usage"), s.render("span", {
blok: (r = t == null ? void 0 : t.attrs) == null ? void 0 : r.body[0],
id: (a = t.attrs) == null ? void 0 : a.id,
style: "display: none"
});
}, P = (t, s) => {
const r = {}, a = s.render("tbody", {}, t.children);
return s.render("table", r, a);
}, U = (t, s) => {
const r = {};
return s.render("tr", r, t.children);
}, G = (t, s) => {
const { colspan: r, rowspan: a, colwidth: i, backgroundColor: o, ...u } = t.attrs || {}, d = {
...u
};
r > 1 && (d.colspan = r), a > 1 && (d.rowspan = a);
const h = [];
return i && h.push(`width: ${i}px;`), o && h.push(`background-color: ${o};`), h.length > 0 && (d.style = h.join(" ")), s.render("td", x(d), t.children);
}, z = (t, s) => {
const { colspan: r, rowspan: a, colwidth: i, backgroundColor: o, ...u } = t.attrs || {}, d = {
...u
};
r > 1 && (d.colspan = r), a > 1 && (d.rowspan = a);
const h = [];
return i && h.push(`width: ${i}px;`), o && h.push(`background-color: ${o};`), h.length > 0 && (d.style = h.join(" ")), s.render("th", x(d), t.children);
}, p = /* @__PURE__ */ new Map([
[f.DOCUMENT, l("")],
[f.HEADING, $],
[f.PARAGRAPH, l("p")],
[f.UL_LIST, l("ul")],
[f.OL_LIST, l("ol")],
[f.LIST_ITEM, l("li")],
[f.IMAGE, E],
[f.EMOJI, L],
[f.CODE_BLOCK, I],
[f.HR, l("hr")],
[f.BR, l("br")],
[f.QUOTE, l("blockquote")],
[f.COMPONENT, D],
[y.TEXT, C],
[b.LINK, j],
[b.ANCHOR, j],
[b.STYLED, R("span", !0)],
[b.BOLD, R("strong")],
[b.TEXT_STYLE, R("span", !0)],
[b.ITALIC, R("em")],
[b.UNDERLINE, R("u")],
[b.STRIKE, R("s")],
[b.CODE, R("code")],
[b.SUPERSCRIPT, R("sup")],
[b.SUBSCRIPT, R("sub")],
[b.HIGHLIGHT, R("mark")],
[f.TABLE, P],
[f.TABLE_ROW, U],
[f.TABLE_CELL, G],
[f.TABLE_HEADER, z]
]), N = new Map([
...p,
...Object.entries(g).map(([t, s]) => [t, s])
]), F = () => ({
render: (r, a = {}, i) => {
if (v && r) {
const o = n.get(r) || 0;
n.set(r, o + 1), a.key = `${r}-${o}`;
}
return m(r, a, i);
},
originalResolvers: p,
mergedResolvers: N
});
function T(t) {
const s = N.get(t.type);
if (!s)
return console.error("<Storyblok>", `No resolver found for node type ${t.type}`), "";
const r = F();
if (t.type === "text")
return s(t, r);
const a = t.content ? t.content.map(_) : void 0;
return s({
...t,
children: a
}, r);
}
function _(t) {
return t.type === "doc" ? A ? t.content.map(T) : t.content.map(T).join("") : Array.isArray(t) ? t.map(T) : T(t);
}
return {
render: _
};
}
export {
f as BlockTypes,
B as LinkTargets,
w as LinkTypes,
b as MarkTypes,
y as TextTypes,
Q as richTextResolver
};