@roottale/cms-renderer-next 0.56.0 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/cms-public.css +9 -0
- package/dist/routes.js +1 -1
- package/dist/server.d.ts +48 -3
- package/dist/server.js +386 -210
- package/dist/server.js.map +1 -1
- package/package.json +3 -3
package/dist/server.js
CHANGED
|
@@ -857,115 +857,27 @@ async function RootTaleBlogCategories(props) {
|
|
|
857
857
|
|
|
858
858
|
// src/server/blog-post.tsx
|
|
859
859
|
import {
|
|
860
|
+
POST_FOOTER_PATTERN_SLOT as POST_FOOTER_PATTERN_SLOT2,
|
|
860
861
|
fetchPost,
|
|
861
862
|
fetchPostPreview,
|
|
862
|
-
fetchPosts as
|
|
863
|
+
fetchPosts as fetchPosts3,
|
|
863
864
|
isPreviewExpiredError,
|
|
864
865
|
resolvePostDisplay as resolvePostDisplay2,
|
|
865
866
|
selectRelatedPosts
|
|
866
867
|
} from "@roottale/cms-client/server";
|
|
867
868
|
|
|
868
|
-
// src/server/
|
|
869
|
-
import {
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
"select",
|
|
876
|
-
"radio",
|
|
877
|
-
"button_group",
|
|
878
|
-
"number",
|
|
879
|
-
"range"
|
|
880
|
-
]);
|
|
881
|
-
function archiveGroupTitle(group) {
|
|
882
|
-
if (group.collectionKey === "conditions") {
|
|
883
|
-
return `\uC774 \uC99D\uC0C1\uACFC \uAD00\uB828\uB41C \uC9C8\uD658 ${group.items.length}`;
|
|
884
|
-
}
|
|
885
|
-
if (group.collectionKey === "treatments") {
|
|
886
|
-
return `\uAD00\uB828 \uCE58\uB8CC\uBC95 ${group.items.length}`;
|
|
887
|
-
}
|
|
888
|
-
if (group.collectionKey === "symptoms") {
|
|
889
|
-
return `\uAD00\uB828 \uC99D\uC0C1 ${group.items.length}`;
|
|
890
|
-
}
|
|
891
|
-
return `${group.label} ${group.items.length}`;
|
|
892
|
-
}
|
|
893
|
-
function defaultArchiveBasePath(collectionKey) {
|
|
894
|
-
if (collectionKey === "symptoms") return "/symptoms";
|
|
895
|
-
if (collectionKey === "conditions") return "/conditions";
|
|
896
|
-
if (collectionKey === "treatments") return "/treatments";
|
|
897
|
-
return "/blog";
|
|
898
|
-
}
|
|
899
|
-
function archivePostHref(collectionKey, slug, collections) {
|
|
900
|
-
const collection = collections?.find((item) => item.key === collectionKey);
|
|
901
|
-
const basePath = collection?.basePath || defaultArchiveBasePath(collectionKey);
|
|
902
|
-
const trimmedBase = basePath.replace(/\/+$/, "");
|
|
903
|
-
if (collection?.detail === false) return trimmedBase;
|
|
904
|
-
return `${trimmedBase}/${slug}`;
|
|
905
|
-
}
|
|
906
|
-
function renderArchiveRelated(groups, collections) {
|
|
907
|
-
const visibleGroups = groups.filter((group) => group.items.length > 0);
|
|
908
|
-
if (visibleGroups.length === 0) return null;
|
|
909
|
-
return /* @__PURE__ */ jsx9("aside", { className: "rt-archive-related", "aria-label": "\uC5F0\uAD00 \uC815\uBCF4", children: visibleGroups.map((group) => /* @__PURE__ */ jsxs8("section", { className: "rt-archive-related__group", children: [
|
|
910
|
-
/* @__PURE__ */ jsx9("h2", { className: "rt-archive-related__title", children: archiveGroupTitle(group) }),
|
|
911
|
-
/* @__PURE__ */ jsx9("ul", { className: "rt-archive-related__list", children: group.items.map((item) => /* @__PURE__ */ jsx9("li", { className: "rt-archive-related__item", children: /* @__PURE__ */ jsxs8(
|
|
912
|
-
"a",
|
|
913
|
-
{
|
|
914
|
-
className: "rt-archive-related__link",
|
|
915
|
-
href: archivePostHref(
|
|
916
|
-
item.collectionKey,
|
|
917
|
-
item.slug,
|
|
918
|
-
collections
|
|
919
|
-
),
|
|
920
|
-
children: [
|
|
921
|
-
/* @__PURE__ */ jsx9("span", { className: "rt-archive-related__item-title", children: item.title }),
|
|
922
|
-
item.excerpt ? /* @__PURE__ */ jsx9("span", { className: "rt-archive-related__item-excerpt", children: item.excerpt }) : null
|
|
923
|
-
]
|
|
924
|
-
}
|
|
925
|
-
) }, item.id)) })
|
|
926
|
-
] }, group.collectionKey)) });
|
|
927
|
-
}
|
|
928
|
-
function fieldFactValue(raw, meta) {
|
|
929
|
-
if (raw === null || raw === void 0) return null;
|
|
930
|
-
if (typeof raw === "number") return raw.toLocaleString("ko-KR");
|
|
931
|
-
if (typeof raw !== "string" || raw.trim() === "") return null;
|
|
932
|
-
if (!meta.choices) return raw;
|
|
933
|
-
const choice = meta.choices.find((item) => item.value === raw);
|
|
934
|
-
return choice ? choice.label : raw;
|
|
935
|
-
}
|
|
936
|
-
function renderFieldFacts(post) {
|
|
937
|
-
if (!post.fields) return null;
|
|
938
|
-
const rows = post.fieldsMeta.filter((meta) => FIELD_FACT_TYPES.has(meta.type)).flatMap((meta) => {
|
|
939
|
-
const value = fieldFactValue(post.fields?.[meta.name], meta);
|
|
940
|
-
return value === null ? [] : [{ meta, value }];
|
|
941
|
-
});
|
|
942
|
-
if (rows.length === 0) return null;
|
|
943
|
-
return /* @__PURE__ */ jsx9("aside", { className: "rt-archive-facts", "aria-label": "\uC0C1\uC138 \uC815\uBCF4", children: /* @__PURE__ */ jsx9("dl", { className: "rt-archive-facts__list", children: rows.map(({ meta, value }) => /* @__PURE__ */ jsxs8("div", { className: "rt-archive-facts__row", children: [
|
|
944
|
-
/* @__PURE__ */ jsx9("dt", { className: "rt-archive-facts__label", children: meta.label }),
|
|
945
|
-
/* @__PURE__ */ jsx9(
|
|
946
|
-
"dd",
|
|
947
|
-
{
|
|
948
|
-
className: meta.type === "textarea" ? "rt-archive-facts__value rt-archive-facts__value--multiline" : "rt-archive-facts__value",
|
|
949
|
-
children: value
|
|
950
|
-
}
|
|
951
|
-
)
|
|
952
|
-
] }, meta.name)) }) });
|
|
953
|
-
}
|
|
869
|
+
// src/server/site-patterns.tsx
|
|
870
|
+
import {
|
|
871
|
+
POST_FOOTER_PATTERN_SLOT,
|
|
872
|
+
SITE_PATTERNS_CACHE_TAG,
|
|
873
|
+
fetchSitePatterns,
|
|
874
|
+
selectSitePatternForSlot
|
|
875
|
+
} from "@roottale/cms-client/server";
|
|
954
876
|
|
|
955
|
-
// src/server/
|
|
956
|
-
import {
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
return html.replace(/<[^>]*>/g, "").replace(/ /gi, " ").trim().length > 0;
|
|
960
|
-
}
|
|
961
|
-
function resolvePostBodySource(bodyHtml) {
|
|
962
|
-
if (typeof bodyHtml !== "string") return { kind: "json" };
|
|
963
|
-
const trimmed = bodyHtml.trim();
|
|
964
|
-
if (trimmed.length === 0) return { kind: "json" };
|
|
965
|
-
const sanitized = sanitizeHtml2(trimmed);
|
|
966
|
-
if (!hasVisibleContent(sanitized)) return { kind: "json" };
|
|
967
|
-
return { kind: "html", html: sanitized };
|
|
968
|
-
}
|
|
877
|
+
// src/server/render-tiptap.tsx
|
|
878
|
+
import {
|
|
879
|
+
internalContentLinkResolverFrom
|
|
880
|
+
} from "@roottale/cms-core";
|
|
969
881
|
|
|
970
882
|
// src/server/tiptap-node-renderer.tsx
|
|
971
883
|
import { Fragment as Fragment4 } from "react";
|
|
@@ -975,12 +887,13 @@ import {
|
|
|
975
887
|
boundFieldValue,
|
|
976
888
|
ensureImportedHtmlDocumentRoots,
|
|
977
889
|
normalizeImageAlt,
|
|
890
|
+
renderInternalContentLinksInHtml,
|
|
978
891
|
scopeImportedCss
|
|
979
892
|
} from "@roottale/cms-core";
|
|
980
893
|
|
|
981
894
|
// src/server/tiptap-embed-renderer.tsx
|
|
982
895
|
import { isAllowedEmbedSrc } from "@roottale/cms-core";
|
|
983
|
-
import { jsx as
|
|
896
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
984
897
|
function isSafeFallbackHref(value) {
|
|
985
898
|
if (typeof value !== "string") return false;
|
|
986
899
|
const href = value.trim();
|
|
@@ -994,13 +907,13 @@ function renderEmbedNode(node, key) {
|
|
|
994
907
|
const src = node.attrs?.src;
|
|
995
908
|
if (isAllowedEmbedSrc(src)) {
|
|
996
909
|
const provider = typeof node.attrs?.provider === "string" ? node.attrs.provider : void 0;
|
|
997
|
-
return /* @__PURE__ */
|
|
910
|
+
return /* @__PURE__ */ jsx9(
|
|
998
911
|
"div",
|
|
999
912
|
{
|
|
1000
913
|
className: "rt-embed",
|
|
1001
914
|
"data-provider": provider,
|
|
1002
915
|
style: { aspectRatio: "16 / 9" },
|
|
1003
|
-
children: /* @__PURE__ */
|
|
916
|
+
children: /* @__PURE__ */ jsx9(
|
|
1004
917
|
"iframe",
|
|
1005
918
|
{
|
|
1006
919
|
src,
|
|
@@ -1018,9 +931,17 @@ function renderEmbedNode(node, key) {
|
|
|
1018
931
|
}
|
|
1019
932
|
const originalUrl = node.attrs?.originalUrl;
|
|
1020
933
|
if (!isSafeFallbackHref(originalUrl)) return null;
|
|
1021
|
-
return /* @__PURE__ */
|
|
934
|
+
return /* @__PURE__ */ jsx9("p", { className: "rt-embed-fallback", children: /* @__PURE__ */ jsx9("a", { href: originalUrl, rel: "noopener noreferrer", target: "_blank", children: originalUrl }) }, key);
|
|
1022
935
|
}
|
|
1023
936
|
|
|
937
|
+
// src/server/tiptap-inline-renderer.tsx
|
|
938
|
+
import {
|
|
939
|
+
findInternalContentLinkTokens,
|
|
940
|
+
INTERNAL_CONTENT_LINK_CLASS,
|
|
941
|
+
INTERNAL_CONTENT_LINK_PENDING_ATTR,
|
|
942
|
+
INTERNAL_CONTENT_LINK_PENDING_CLASS
|
|
943
|
+
} from "@roottale/cms-core";
|
|
944
|
+
|
|
1024
945
|
// src/server/tiptap-types.ts
|
|
1025
946
|
function isRecord(value) {
|
|
1026
947
|
return typeof value === "object" && value !== null;
|
|
@@ -1044,7 +965,7 @@ function parseTiptapMark(value) {
|
|
|
1044
965
|
}
|
|
1045
966
|
|
|
1046
967
|
// src/server/tiptap-inline-renderer.tsx
|
|
1047
|
-
import { Fragment as Fragment3, jsx as
|
|
968
|
+
import { Fragment as Fragment3, jsx as jsx10 } from "react/jsx-runtime";
|
|
1048
969
|
function isSafeHref(value) {
|
|
1049
970
|
if (typeof value !== "string") return false;
|
|
1050
971
|
const href = value.trim();
|
|
@@ -1057,52 +978,86 @@ function isSafeHref(value) {
|
|
|
1057
978
|
function applyMark(mark, child) {
|
|
1058
979
|
switch (mark.type) {
|
|
1059
980
|
case "bold":
|
|
1060
|
-
return /* @__PURE__ */
|
|
981
|
+
return /* @__PURE__ */ jsx10("strong", { children: child });
|
|
1061
982
|
case "italic":
|
|
1062
|
-
return /* @__PURE__ */
|
|
983
|
+
return /* @__PURE__ */ jsx10("em", { children: child });
|
|
1063
984
|
case "underline":
|
|
1064
|
-
return /* @__PURE__ */
|
|
985
|
+
return /* @__PURE__ */ jsx10("u", { children: child });
|
|
1065
986
|
case "strike":
|
|
1066
|
-
return /* @__PURE__ */
|
|
987
|
+
return /* @__PURE__ */ jsx10("s", { children: child });
|
|
1067
988
|
case "code":
|
|
1068
|
-
return /* @__PURE__ */
|
|
989
|
+
return /* @__PURE__ */ jsx10("code", { children: child });
|
|
1069
990
|
case "highlight": {
|
|
1070
991
|
const color = mark.attrs?.color;
|
|
1071
992
|
const style = typeof color === "string" && color.length > 0 ? { background: color } : void 0;
|
|
1072
|
-
return /* @__PURE__ */
|
|
993
|
+
return /* @__PURE__ */ jsx10("mark", { style, children: child });
|
|
1073
994
|
}
|
|
1074
995
|
case "textStyle": {
|
|
1075
996
|
const color = mark.attrs?.color;
|
|
1076
997
|
if (typeof color !== "string" || color.length === 0) {
|
|
1077
|
-
return /* @__PURE__ */
|
|
998
|
+
return /* @__PURE__ */ jsx10(Fragment3, { children: child });
|
|
1078
999
|
}
|
|
1079
|
-
return /* @__PURE__ */
|
|
1000
|
+
return /* @__PURE__ */ jsx10("span", { style: { color }, children: child });
|
|
1080
1001
|
}
|
|
1081
1002
|
case "link": {
|
|
1082
1003
|
const rawHref = mark.attrs?.href;
|
|
1083
1004
|
const href = isSafeHref(rawHref) ? rawHref : "#";
|
|
1084
|
-
return /* @__PURE__ */
|
|
1005
|
+
return /* @__PURE__ */ jsx10("a", { href, rel: "noopener noreferrer", target: "_blank", children: child });
|
|
1085
1006
|
}
|
|
1086
1007
|
default:
|
|
1087
|
-
return /* @__PURE__ */
|
|
1008
|
+
return /* @__PURE__ */ jsx10("span", { children: child });
|
|
1088
1009
|
}
|
|
1089
1010
|
}
|
|
1090
|
-
function
|
|
1011
|
+
function isSafeInternalPath(path) {
|
|
1012
|
+
return typeof path === "string" && path.startsWith("/") && !path.startsWith("//");
|
|
1013
|
+
}
|
|
1014
|
+
function renderInternalLinkText(text, resolve) {
|
|
1015
|
+
const tokens = findInternalContentLinkTokens(text);
|
|
1016
|
+
if (tokens.length === 0) return text;
|
|
1017
|
+
const parts = [];
|
|
1018
|
+
let cursor = 0;
|
|
1019
|
+
tokens.forEach((token, index) => {
|
|
1020
|
+
if (token.index > cursor) parts.push(text.slice(cursor, token.index));
|
|
1021
|
+
const path = resolve(token.key);
|
|
1022
|
+
parts.push(
|
|
1023
|
+
isSafeInternalPath(path) ? /* @__PURE__ */ jsx10("a", { href: path, className: INTERNAL_CONTENT_LINK_CLASS, children: token.label }, index) : /* @__PURE__ */ jsx10(
|
|
1024
|
+
"span",
|
|
1025
|
+
{
|
|
1026
|
+
className: `${INTERNAL_CONTENT_LINK_CLASS} ${INTERNAL_CONTENT_LINK_PENDING_CLASS}`,
|
|
1027
|
+
...{ [INTERNAL_CONTENT_LINK_PENDING_ATTR]: token.key },
|
|
1028
|
+
children: token.label
|
|
1029
|
+
},
|
|
1030
|
+
index
|
|
1031
|
+
)
|
|
1032
|
+
);
|
|
1033
|
+
cursor = token.index + token.raw.length;
|
|
1034
|
+
});
|
|
1035
|
+
if (cursor < text.length) parts.push(text.slice(cursor));
|
|
1036
|
+
return /* @__PURE__ */ jsx10(Fragment3, { children: parts });
|
|
1037
|
+
}
|
|
1038
|
+
function renderTextNode(node, key, internalLinks = null) {
|
|
1091
1039
|
const text = node.text ?? "";
|
|
1092
1040
|
const marks = (node.marks ?? []).flatMap((value) => {
|
|
1093
1041
|
const mark = parseTiptapMark(value);
|
|
1094
1042
|
return mark ? [mark] : [];
|
|
1095
1043
|
});
|
|
1096
|
-
|
|
1097
|
-
|
|
1044
|
+
const canLink = internalLinks !== null && !marks.some((mark) => mark.type === "code" || mark.type === "link");
|
|
1045
|
+
const content = canLink && internalLinks ? renderInternalLinkText(text, internalLinks) : text;
|
|
1046
|
+
if (marks.length === 0) {
|
|
1047
|
+
return typeof content === "string" ? content : /* @__PURE__ */ jsx10("span", { children: content }, key);
|
|
1048
|
+
}
|
|
1049
|
+
let element = typeof content === "string" ? content : /* @__PURE__ */ jsx10(Fragment3, { children: content });
|
|
1098
1050
|
for (const mark of marks) {
|
|
1099
1051
|
element = applyMark(mark, element);
|
|
1100
1052
|
}
|
|
1101
|
-
return /* @__PURE__ */
|
|
1053
|
+
return /* @__PURE__ */ jsx10("span", { children: element }, key);
|
|
1102
1054
|
}
|
|
1103
1055
|
|
|
1104
1056
|
// src/server/tiptap-node-renderer.tsx
|
|
1105
|
-
import { jsx as
|
|
1057
|
+
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1058
|
+
function withInternalLinksInHtml(html, internalLinks) {
|
|
1059
|
+
return internalLinks ? renderInternalContentLinksInHtml(html, internalLinks).html : html;
|
|
1060
|
+
}
|
|
1106
1061
|
function boundContentText(node, fields) {
|
|
1107
1062
|
return bindingTextValue(
|
|
1108
1063
|
boundFieldValue(node.attrs?.bindings, "content", fields)
|
|
@@ -1135,7 +1090,7 @@ function renderTiptapNode(value, key, context) {
|
|
|
1135
1090
|
switch (node.type) {
|
|
1136
1091
|
case "paragraph": {
|
|
1137
1092
|
const bound = boundContentText(node, context.fields);
|
|
1138
|
-
return /* @__PURE__ */
|
|
1093
|
+
return /* @__PURE__ */ jsx11("p", { style: alignStyle(node), children: bound ?? children }, key);
|
|
1139
1094
|
}
|
|
1140
1095
|
case "heading": {
|
|
1141
1096
|
const rawLevel = Number(node.attrs?.level ?? 2);
|
|
@@ -1146,27 +1101,29 @@ function renderTiptapNode(value, key, context) {
|
|
|
1146
1101
|
const style = alignStyle(node);
|
|
1147
1102
|
const inner = boundContentText(node, context.fields) ?? children;
|
|
1148
1103
|
if (level === 2) {
|
|
1149
|
-
return /* @__PURE__ */
|
|
1104
|
+
return /* @__PURE__ */ jsx11("h2", { id, style, children: inner }, key);
|
|
1150
1105
|
}
|
|
1151
1106
|
if (level === 3) {
|
|
1152
|
-
return /* @__PURE__ */
|
|
1107
|
+
return /* @__PURE__ */ jsx11("h3", { id, style, children: inner }, key);
|
|
1153
1108
|
}
|
|
1154
|
-
return /* @__PURE__ */
|
|
1109
|
+
return /* @__PURE__ */ jsx11("h4", { id, style, children: inner }, key);
|
|
1155
1110
|
}
|
|
1156
1111
|
case "bulletList":
|
|
1157
|
-
return /* @__PURE__ */
|
|
1112
|
+
return /* @__PURE__ */ jsx11("ul", { children }, key);
|
|
1158
1113
|
case "orderedList":
|
|
1159
|
-
return /* @__PURE__ */
|
|
1114
|
+
return /* @__PURE__ */ jsx11("ol", { children }, key);
|
|
1160
1115
|
case "listItem":
|
|
1161
|
-
return /* @__PURE__ */
|
|
1116
|
+
return /* @__PURE__ */ jsx11("li", { children }, key);
|
|
1162
1117
|
case "blockquote":
|
|
1163
|
-
return /* @__PURE__ */
|
|
1118
|
+
return /* @__PURE__ */ jsx11("blockquote", { children }, key);
|
|
1164
1119
|
case "codeBlock":
|
|
1165
|
-
return /* @__PURE__ */
|
|
1120
|
+
return /* @__PURE__ */ jsx11("pre", { children: /* @__PURE__ */ jsx11("code", { children: node.content?.map(
|
|
1121
|
+
(child, index) => renderTiptapNode(child, index, { ...context, internalLinks: null })
|
|
1122
|
+
) }) }, key);
|
|
1166
1123
|
case "horizontalRule":
|
|
1167
|
-
return /* @__PURE__ */
|
|
1124
|
+
return /* @__PURE__ */ jsx11("hr", {}, key);
|
|
1168
1125
|
case "hardBreak":
|
|
1169
|
-
return /* @__PURE__ */
|
|
1126
|
+
return /* @__PURE__ */ jsx11("br", {}, key);
|
|
1170
1127
|
case "image": {
|
|
1171
1128
|
const boundSrc = bindingSrcValue(
|
|
1172
1129
|
boundFieldValue(node.attrs?.bindings, "src", context.fields)
|
|
@@ -1177,7 +1134,7 @@ function renderTiptapNode(value, key, context) {
|
|
|
1177
1134
|
const title = typeof node.attrs?.title === "string" ? node.attrs.title : void 0;
|
|
1178
1135
|
const width = typeof node.attrs?.width === "number" && node.attrs.width > 0 ? node.attrs.width : void 0;
|
|
1179
1136
|
const height = typeof node.attrs?.height === "number" && node.attrs.height > 0 ? node.attrs.height : void 0;
|
|
1180
|
-
return /* @__PURE__ */
|
|
1137
|
+
return /* @__PURE__ */ jsx11(
|
|
1181
1138
|
"img",
|
|
1182
1139
|
{
|
|
1183
1140
|
src,
|
|
@@ -1202,59 +1159,62 @@ function renderTiptapNode(value, key, context) {
|
|
|
1202
1159
|
scope,
|
|
1203
1160
|
assetUrls
|
|
1204
1161
|
);
|
|
1205
|
-
return /* @__PURE__ */
|
|
1206
|
-
scopedCss ? /* @__PURE__ */
|
|
1207
|
-
/* @__PURE__ */
|
|
1162
|
+
return /* @__PURE__ */ jsxs8(Fragment4, { children: [
|
|
1163
|
+
scopedCss ? /* @__PURE__ */ jsx11("style", { "data-rt-imported-style": scope, children: scopedCss }) : null,
|
|
1164
|
+
/* @__PURE__ */ jsx11(
|
|
1208
1165
|
"div",
|
|
1209
1166
|
{
|
|
1210
1167
|
className: "rt-imported-html",
|
|
1211
1168
|
"data-rt-import-scope": scopedCss ? scope : void 0,
|
|
1212
1169
|
style: { position: "relative", isolation: "isolate" },
|
|
1213
1170
|
dangerouslySetInnerHTML: {
|
|
1214
|
-
__html:
|
|
1171
|
+
__html: withInternalLinksInHtml(
|
|
1172
|
+
ensureImportedHtmlDocumentRoots(rawHtml, assetUrls),
|
|
1173
|
+
context.internalLinks ?? null
|
|
1174
|
+
)
|
|
1215
1175
|
}
|
|
1216
1176
|
}
|
|
1217
1177
|
)
|
|
1218
1178
|
] }, key);
|
|
1219
1179
|
}
|
|
1220
1180
|
case "columns":
|
|
1221
|
-
return /* @__PURE__ */
|
|
1181
|
+
return /* @__PURE__ */ jsx11("div", { className: "rt-columns", children }, key);
|
|
1222
1182
|
case "column":
|
|
1223
|
-
return /* @__PURE__ */
|
|
1183
|
+
return /* @__PURE__ */ jsx11("div", { className: "rt-column", children }, key);
|
|
1224
1184
|
case "table":
|
|
1225
|
-
return /* @__PURE__ */
|
|
1185
|
+
return /* @__PURE__ */ jsx11("div", { className: "rt-table-wrap", children: /* @__PURE__ */ jsx11("table", { className: "rt-table", children: /* @__PURE__ */ jsx11("tbody", { children }) }) }, key);
|
|
1226
1186
|
case "tableRow":
|
|
1227
|
-
return /* @__PURE__ */
|
|
1187
|
+
return /* @__PURE__ */ jsx11("tr", { children }, key);
|
|
1228
1188
|
case "tableHeader": {
|
|
1229
1189
|
const { colSpan, rowSpan } = tableCellSpans(node);
|
|
1230
|
-
return /* @__PURE__ */
|
|
1190
|
+
return /* @__PURE__ */ jsx11("th", { colSpan, rowSpan, children }, key);
|
|
1231
1191
|
}
|
|
1232
1192
|
case "tableCell": {
|
|
1233
1193
|
const { colSpan, rowSpan } = tableCellSpans(node);
|
|
1234
|
-
return /* @__PURE__ */
|
|
1194
|
+
return /* @__PURE__ */ jsx11("td", { colSpan, rowSpan, children }, key);
|
|
1235
1195
|
}
|
|
1236
1196
|
case "embed":
|
|
1237
1197
|
return renderEmbedNode(node, key);
|
|
1238
1198
|
case "faq":
|
|
1239
|
-
return /* @__PURE__ */
|
|
1199
|
+
return /* @__PURE__ */ jsx11("div", { "data-roottale-cms": "faq", className: "rt-cms-faq", children }, key);
|
|
1240
1200
|
case "faqItem":
|
|
1241
|
-
return /* @__PURE__ */
|
|
1201
|
+
return /* @__PURE__ */ jsx11("details", { className: "rt-cms-faq-item", children }, key);
|
|
1242
1202
|
case "faqQuestion":
|
|
1243
|
-
return /* @__PURE__ */
|
|
1203
|
+
return /* @__PURE__ */ jsx11("summary", { className: "rt-cms-faq-q", children }, key);
|
|
1244
1204
|
case "faqAnswer":
|
|
1245
|
-
return /* @__PURE__ */
|
|
1205
|
+
return /* @__PURE__ */ jsx11("div", { className: "rt-cms-faq-a", children }, key);
|
|
1246
1206
|
case "references": {
|
|
1247
1207
|
const rawTitle = node.attrs?.title;
|
|
1248
1208
|
const title = typeof rawTitle === "string" && rawTitle.trim() ? rawTitle.trim() : "\uACF5\uC2DD \uCD9C\uCC98";
|
|
1249
1209
|
const titleId = `rt-cms-references-${key}`;
|
|
1250
|
-
return /* @__PURE__ */
|
|
1210
|
+
return /* @__PURE__ */ jsxs8(
|
|
1251
1211
|
"section",
|
|
1252
1212
|
{
|
|
1253
1213
|
"data-roottale-cms": "references",
|
|
1254
1214
|
className: "rt-cms-references",
|
|
1255
1215
|
"aria-labelledby": titleId,
|
|
1256
1216
|
children: [
|
|
1257
|
-
/* @__PURE__ */
|
|
1217
|
+
/* @__PURE__ */ jsx11("h2", { id: titleId, className: "rt-cms-references__title", children: title }),
|
|
1258
1218
|
children
|
|
1259
1219
|
]
|
|
1260
1220
|
},
|
|
@@ -1276,14 +1236,14 @@ function renderTiptapNode(value, key, context) {
|
|
|
1276
1236
|
return renderSection(node, key, context.section);
|
|
1277
1237
|
}
|
|
1278
1238
|
case "text":
|
|
1279
|
-
return renderTextNode(node, key);
|
|
1239
|
+
return renderTextNode(node, key, context.internalLinks ?? null);
|
|
1280
1240
|
default:
|
|
1281
|
-
return children ? /* @__PURE__ */
|
|
1241
|
+
return children ? /* @__PURE__ */ jsx11("span", { children }, key) : null;
|
|
1282
1242
|
}
|
|
1283
1243
|
}
|
|
1284
1244
|
|
|
1285
1245
|
// src/server/render-tiptap.tsx
|
|
1286
|
-
import { Fragment as Fragment5, jsx as
|
|
1246
|
+
import { Fragment as Fragment5, jsx as jsx12 } from "react/jsx-runtime";
|
|
1287
1247
|
function isDevelopmentRuntime() {
|
|
1288
1248
|
const processValue = Reflect.get(globalThis, "process");
|
|
1289
1249
|
if (!isRecord(processValue)) return true;
|
|
@@ -1296,7 +1256,7 @@ function createSectionContextWithBlogListNetworkFetch(apiKey, baseUrl) {
|
|
|
1296
1256
|
return {
|
|
1297
1257
|
cms,
|
|
1298
1258
|
isDev: isDevelopmentRuntime,
|
|
1299
|
-
renderBlogList: (props) => /* @__PURE__ */
|
|
1259
|
+
renderBlogList: (props) => /* @__PURE__ */ jsx12(RootTaleBlogList, { ...props })
|
|
1300
1260
|
};
|
|
1301
1261
|
}
|
|
1302
1262
|
function contentNodes(doc) {
|
|
@@ -1306,7 +1266,8 @@ function RenderTiptap({
|
|
|
1306
1266
|
doc,
|
|
1307
1267
|
fields,
|
|
1308
1268
|
apiKey,
|
|
1309
|
-
baseUrl
|
|
1269
|
+
baseUrl,
|
|
1270
|
+
internalLinks
|
|
1310
1271
|
}) {
|
|
1311
1272
|
const content = contentNodes(doc);
|
|
1312
1273
|
const hasTableOfContents = content.some(
|
|
@@ -1316,15 +1277,16 @@ function RenderTiptap({
|
|
|
1316
1277
|
const renderDoc = hasTableOfContents && entries.length > 0 ? attachHeadingIds(doc, entries) : doc;
|
|
1317
1278
|
const context = {
|
|
1318
1279
|
fields: fields ?? null,
|
|
1319
|
-
section: createSectionContextWithBlogListNetworkFetch(apiKey, baseUrl)
|
|
1280
|
+
section: createSectionContextWithBlogListNetworkFetch(apiKey, baseUrl),
|
|
1281
|
+
internalLinks: internalLinks ? internalContentLinkResolverFrom(internalLinks) : null
|
|
1320
1282
|
};
|
|
1321
|
-
return /* @__PURE__ */
|
|
1283
|
+
return /* @__PURE__ */ jsx12(Fragment5, { children: contentNodes(renderDoc).map((value, index) => {
|
|
1322
1284
|
const node = parseTiptapNode(value);
|
|
1323
1285
|
if (node?.type !== "tableOfContents") {
|
|
1324
1286
|
return renderTiptapNode(value, index, context);
|
|
1325
1287
|
}
|
|
1326
1288
|
const title = node.attrs?.title;
|
|
1327
|
-
return /* @__PURE__ */
|
|
1289
|
+
return /* @__PURE__ */ jsx12(
|
|
1328
1290
|
RootTaleTableOfContents,
|
|
1329
1291
|
{
|
|
1330
1292
|
entries,
|
|
@@ -1335,8 +1297,152 @@ function RenderTiptap({
|
|
|
1335
1297
|
}) });
|
|
1336
1298
|
}
|
|
1337
1299
|
|
|
1300
|
+
// src/server/site-patterns.tsx
|
|
1301
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
1302
|
+
async function resolvePostPatternFailSoft(post, input, slot = POST_FOOTER_PATTERN_SLOT) {
|
|
1303
|
+
const key = post.patternSlots?.[slot] ?? null;
|
|
1304
|
+
if (!key) return null;
|
|
1305
|
+
try {
|
|
1306
|
+
const patterns = await fetchSitePatterns({
|
|
1307
|
+
apiKey: input.apiKey,
|
|
1308
|
+
baseUrl: input.baseUrl,
|
|
1309
|
+
tags: [SITE_PATTERNS_CACHE_TAG]
|
|
1310
|
+
});
|
|
1311
|
+
return selectSitePatternForSlot(post.patternSlots, patterns, slot);
|
|
1312
|
+
} catch {
|
|
1313
|
+
return null;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
function renderPostPattern(pattern, slot, input) {
|
|
1317
|
+
if (!pattern) return null;
|
|
1318
|
+
return /* @__PURE__ */ jsx13(
|
|
1319
|
+
"aside",
|
|
1320
|
+
{
|
|
1321
|
+
"data-roottale-cms": "post-pattern",
|
|
1322
|
+
"data-pattern-slot": slot,
|
|
1323
|
+
"data-pattern-key": pattern.key,
|
|
1324
|
+
className: "rt-cms-post-pattern",
|
|
1325
|
+
"aria-label": pattern.name,
|
|
1326
|
+
children: /* @__PURE__ */ jsx13(RenderTiptap, { doc: pattern.bodyJson, apiKey: input.apiKey, baseUrl: input.baseUrl })
|
|
1327
|
+
}
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1330
|
+
async function RootTalePostPattern(props) {
|
|
1331
|
+
const slot = props.slot ?? POST_FOOTER_PATTERN_SLOT;
|
|
1332
|
+
const pattern = await resolvePostPatternFailSoft(props.post, props, slot);
|
|
1333
|
+
return renderPostPattern(pattern, slot, props);
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
// src/server/blog-post-archive.tsx
|
|
1337
|
+
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1338
|
+
var FIELD_FACT_TYPES = /* @__PURE__ */ new Set([
|
|
1339
|
+
"text",
|
|
1340
|
+
"textarea",
|
|
1341
|
+
"email",
|
|
1342
|
+
"url",
|
|
1343
|
+
"select",
|
|
1344
|
+
"radio",
|
|
1345
|
+
"button_group",
|
|
1346
|
+
"number",
|
|
1347
|
+
"range"
|
|
1348
|
+
]);
|
|
1349
|
+
function archiveGroupTitle(group) {
|
|
1350
|
+
if (group.collectionKey === "conditions") {
|
|
1351
|
+
return `\uC774 \uC99D\uC0C1\uACFC \uAD00\uB828\uB41C \uC9C8\uD658 ${group.items.length}`;
|
|
1352
|
+
}
|
|
1353
|
+
if (group.collectionKey === "treatments") {
|
|
1354
|
+
return `\uAD00\uB828 \uCE58\uB8CC\uBC95 ${group.items.length}`;
|
|
1355
|
+
}
|
|
1356
|
+
if (group.collectionKey === "symptoms") {
|
|
1357
|
+
return `\uAD00\uB828 \uC99D\uC0C1 ${group.items.length}`;
|
|
1358
|
+
}
|
|
1359
|
+
return `${group.label} ${group.items.length}`;
|
|
1360
|
+
}
|
|
1361
|
+
function defaultArchiveBasePath(collectionKey) {
|
|
1362
|
+
if (collectionKey === "symptoms") return "/symptoms";
|
|
1363
|
+
if (collectionKey === "conditions") return "/conditions";
|
|
1364
|
+
if (collectionKey === "treatments") return "/treatments";
|
|
1365
|
+
return "/blog";
|
|
1366
|
+
}
|
|
1367
|
+
function archivePostHref(collectionKey, slug, collections) {
|
|
1368
|
+
const collection = collections?.find((item) => item.key === collectionKey);
|
|
1369
|
+
const basePath = collection?.basePath || defaultArchiveBasePath(collectionKey);
|
|
1370
|
+
const trimmedBase = basePath.replace(/\/+$/, "");
|
|
1371
|
+
if (collection?.detail === false) return trimmedBase;
|
|
1372
|
+
return `${trimmedBase}/${slug}`;
|
|
1373
|
+
}
|
|
1374
|
+
function renderArchiveRelated(groups, collections) {
|
|
1375
|
+
const visibleGroups = groups.filter((group) => group.items.length > 0);
|
|
1376
|
+
if (visibleGroups.length === 0) return null;
|
|
1377
|
+
return /* @__PURE__ */ jsx14("aside", { className: "rt-archive-related", "aria-label": "\uC5F0\uAD00 \uC815\uBCF4", children: visibleGroups.map((group) => /* @__PURE__ */ jsxs9("section", { className: "rt-archive-related__group", children: [
|
|
1378
|
+
/* @__PURE__ */ jsx14("h2", { className: "rt-archive-related__title", children: archiveGroupTitle(group) }),
|
|
1379
|
+
/* @__PURE__ */ jsx14("ul", { className: "rt-archive-related__list", children: group.items.map((item) => /* @__PURE__ */ jsx14("li", { className: "rt-archive-related__item", children: /* @__PURE__ */ jsxs9(
|
|
1380
|
+
"a",
|
|
1381
|
+
{
|
|
1382
|
+
className: "rt-archive-related__link",
|
|
1383
|
+
href: archivePostHref(
|
|
1384
|
+
item.collectionKey,
|
|
1385
|
+
item.slug,
|
|
1386
|
+
collections
|
|
1387
|
+
),
|
|
1388
|
+
children: [
|
|
1389
|
+
/* @__PURE__ */ jsx14("span", { className: "rt-archive-related__item-title", children: item.title }),
|
|
1390
|
+
item.excerpt ? /* @__PURE__ */ jsx14("span", { className: "rt-archive-related__item-excerpt", children: item.excerpt }) : null
|
|
1391
|
+
]
|
|
1392
|
+
}
|
|
1393
|
+
) }, item.id)) })
|
|
1394
|
+
] }, group.collectionKey)) });
|
|
1395
|
+
}
|
|
1396
|
+
function fieldFactValue(raw, meta) {
|
|
1397
|
+
if (raw === null || raw === void 0) return null;
|
|
1398
|
+
if (typeof raw === "number") return raw.toLocaleString("ko-KR");
|
|
1399
|
+
if (typeof raw !== "string" || raw.trim() === "") return null;
|
|
1400
|
+
if (!meta.choices) return raw;
|
|
1401
|
+
const choice = meta.choices.find((item) => item.value === raw);
|
|
1402
|
+
return choice ? choice.label : raw;
|
|
1403
|
+
}
|
|
1404
|
+
function renderFieldFacts(post) {
|
|
1405
|
+
if (!post.fields) return null;
|
|
1406
|
+
const rows = post.fieldsMeta.filter((meta) => FIELD_FACT_TYPES.has(meta.type)).flatMap((meta) => {
|
|
1407
|
+
const value = fieldFactValue(post.fields?.[meta.name], meta);
|
|
1408
|
+
return value === null ? [] : [{ meta, value }];
|
|
1409
|
+
});
|
|
1410
|
+
if (rows.length === 0) return null;
|
|
1411
|
+
return /* @__PURE__ */ jsx14("aside", { className: "rt-archive-facts", "aria-label": "\uC0C1\uC138 \uC815\uBCF4", children: /* @__PURE__ */ jsx14("dl", { className: "rt-archive-facts__list", children: rows.map(({ meta, value }) => /* @__PURE__ */ jsxs9("div", { className: "rt-archive-facts__row", children: [
|
|
1412
|
+
/* @__PURE__ */ jsx14("dt", { className: "rt-archive-facts__label", children: meta.label }),
|
|
1413
|
+
/* @__PURE__ */ jsx14(
|
|
1414
|
+
"dd",
|
|
1415
|
+
{
|
|
1416
|
+
className: meta.type === "textarea" ? "rt-archive-facts__value rt-archive-facts__value--multiline" : "rt-archive-facts__value",
|
|
1417
|
+
children: value
|
|
1418
|
+
}
|
|
1419
|
+
)
|
|
1420
|
+
] }, meta.name)) }) });
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1338
1423
|
// src/server/blog-post-body.tsx
|
|
1339
|
-
import {
|
|
1424
|
+
import {
|
|
1425
|
+
internalContentLinkResolverFrom as internalContentLinkResolverFrom2,
|
|
1426
|
+
renderInternalContentLinksInHtml as renderInternalContentLinksInHtml2
|
|
1427
|
+
} from "@roottale/cms-core";
|
|
1428
|
+
|
|
1429
|
+
// src/server/post-body-source.ts
|
|
1430
|
+
import { sanitizeHtml as sanitizeHtml2 } from "@roottale/cms-core";
|
|
1431
|
+
function hasVisibleContent(html) {
|
|
1432
|
+
if (/<(?:img|hr|br|iframe|video|audio)\b/i.test(html)) return true;
|
|
1433
|
+
return html.replace(/<[^>]*>/g, "").replace(/ /gi, " ").trim().length > 0;
|
|
1434
|
+
}
|
|
1435
|
+
function resolvePostBodySource(bodyHtml) {
|
|
1436
|
+
if (typeof bodyHtml !== "string") return { kind: "json" };
|
|
1437
|
+
const trimmed = bodyHtml.trim();
|
|
1438
|
+
if (trimmed.length === 0) return { kind: "json" };
|
|
1439
|
+
const sanitized = sanitizeHtml2(trimmed);
|
|
1440
|
+
if (!hasVisibleContent(sanitized)) return { kind: "json" };
|
|
1441
|
+
return { kind: "html", html: sanitized };
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
// src/server/blog-post-body.tsx
|
|
1445
|
+
import { Fragment as Fragment6, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1340
1446
|
function BlogPostBody(props) {
|
|
1341
1447
|
const {
|
|
1342
1448
|
doc,
|
|
@@ -1346,28 +1452,34 @@ function BlogPostBody(props) {
|
|
|
1346
1452
|
toc,
|
|
1347
1453
|
tocTitle,
|
|
1348
1454
|
tocPosition,
|
|
1349
|
-
bodyHtml
|
|
1455
|
+
bodyHtml,
|
|
1456
|
+
internalLinks = null
|
|
1350
1457
|
} = props;
|
|
1351
|
-
const tableOfContents = toc.length > 0 ? /* @__PURE__ */
|
|
1458
|
+
const tableOfContents = toc.length > 0 ? /* @__PURE__ */ jsx15(RootTaleTableOfContents, { entries: toc, title: tocTitle }) : null;
|
|
1352
1459
|
const source = resolvePostBodySource(bodyHtml);
|
|
1353
|
-
const
|
|
1460
|
+
const sourceHtml = source.kind === "html" && internalLinks ? renderInternalContentLinksInHtml2(
|
|
1461
|
+
source.html,
|
|
1462
|
+
internalContentLinkResolverFrom2(internalLinks)
|
|
1463
|
+
).html : source.kind === "html" ? source.html : "";
|
|
1464
|
+
const body = /* @__PURE__ */ jsx15("div", { "data-roottale-cms": "body", className: "rt-cms-body", children: source.kind === "html" ? (
|
|
1354
1465
|
// source.html 은 resolvePostBodySource 가 공용 sanitizeHtml 로 정제한 값이다
|
|
1355
1466
|
// (이 컴포넌트에서 한 번 더 정제하지 않는 이유 — sink 가 한 곳이어야 눈으로
|
|
1356
1467
|
// 추적 가능하고, 이중 정제는 안전을 더 만들지 않으면서 비용만 든다).
|
|
1357
|
-
/* @__PURE__ */
|
|
1358
|
-
) : /* @__PURE__ */
|
|
1468
|
+
/* @__PURE__ */ jsx15("div", { dangerouslySetInnerHTML: { __html: sourceHtml } })
|
|
1469
|
+
) : /* @__PURE__ */ jsx15(
|
|
1359
1470
|
RenderTiptap,
|
|
1360
1471
|
{
|
|
1361
1472
|
doc,
|
|
1362
1473
|
fields,
|
|
1363
1474
|
apiKey,
|
|
1364
|
-
baseUrl
|
|
1475
|
+
baseUrl,
|
|
1476
|
+
internalLinks
|
|
1365
1477
|
}
|
|
1366
1478
|
) });
|
|
1367
1479
|
if (tocPosition === "sidebar" && tableOfContents) {
|
|
1368
1480
|
return /* @__PURE__ */ jsxs10("div", { className: "rt-cms-layout rt-cms-layout--toc-side", children: [
|
|
1369
1481
|
body,
|
|
1370
|
-
/* @__PURE__ */
|
|
1482
|
+
/* @__PURE__ */ jsx15("aside", { className: "rt-cms-toc-side", children: tableOfContents })
|
|
1371
1483
|
] });
|
|
1372
1484
|
}
|
|
1373
1485
|
return /* @__PURE__ */ jsxs10(Fragment6, { children: [
|
|
@@ -1384,7 +1496,7 @@ import {
|
|
|
1384
1496
|
import {
|
|
1385
1497
|
resolvePostCollection as resolvePostCollection2
|
|
1386
1498
|
} from "@roottale/cms-core";
|
|
1387
|
-
import { Fragment as Fragment7, jsx as
|
|
1499
|
+
import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1388
1500
|
function absoluteUrl(siteUrl, path) {
|
|
1389
1501
|
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
1390
1502
|
return `${trimSlash(siteUrl)}${normalizedPath}`;
|
|
@@ -1420,10 +1532,10 @@ function renderBlogPostBreadcrumb(input) {
|
|
|
1420
1532
|
}))
|
|
1421
1533
|
) : null;
|
|
1422
1534
|
return /* @__PURE__ */ jsxs11(Fragment7, { children: [
|
|
1423
|
-
/* @__PURE__ */
|
|
1424
|
-
(crumb, index) => index === crumbs.length - 1 ? /* @__PURE__ */
|
|
1535
|
+
/* @__PURE__ */ jsx16("nav", { "aria-label": "\uBE0C\uB808\uB4DC\uD06C\uB7FC", className: "rt-cms-breadcrumb", children: /* @__PURE__ */ jsx16("ol", { children: crumbs.map(
|
|
1536
|
+
(crumb, index) => index === crumbs.length - 1 ? /* @__PURE__ */ jsx16("li", { "aria-current": "page", children: crumb.label }, index) : /* @__PURE__ */ jsx16("li", { children: /* @__PURE__ */ jsx16("a", { href: crumb.href, children: crumb.label }) }, index)
|
|
1425
1537
|
) }) }),
|
|
1426
|
-
jsonLd ? /* @__PURE__ */
|
|
1538
|
+
jsonLd ? /* @__PURE__ */ jsx16(
|
|
1427
1539
|
"script",
|
|
1428
1540
|
{
|
|
1429
1541
|
type: "application/ld+json",
|
|
@@ -1433,12 +1545,51 @@ function renderBlogPostBreadcrumb(input) {
|
|
|
1433
1545
|
] });
|
|
1434
1546
|
}
|
|
1435
1547
|
|
|
1548
|
+
// src/server/internal-links.ts
|
|
1549
|
+
import { internalContentPathIndex } from "@roottale/cms-core";
|
|
1550
|
+
import { fetchPosts as fetchPosts2 } from "@roottale/cms-client/server";
|
|
1551
|
+
async function fetchInternalContentPathIndex(options) {
|
|
1552
|
+
const entries = [];
|
|
1553
|
+
const maxPages = options.maxPages ?? 20;
|
|
1554
|
+
let cursor;
|
|
1555
|
+
for (let page = 0; page < maxPages; page += 1) {
|
|
1556
|
+
try {
|
|
1557
|
+
const result = await fetchPosts2({
|
|
1558
|
+
apiKey: options.apiKey,
|
|
1559
|
+
baseUrl: options.baseUrl,
|
|
1560
|
+
type: "post",
|
|
1561
|
+
limit: 100,
|
|
1562
|
+
cursor,
|
|
1563
|
+
...options.locale ? { locale: options.locale } : {},
|
|
1564
|
+
...options.revalidate !== void 0 ? { revalidate: options.revalidate } : {}
|
|
1565
|
+
});
|
|
1566
|
+
for (const post of result.items) {
|
|
1567
|
+
entries.push({ path: post.path, previousSlugs: post.previousSlugs });
|
|
1568
|
+
}
|
|
1569
|
+
if (!result.hasMore || !result.nextCursor) break;
|
|
1570
|
+
cursor = result.nextCursor;
|
|
1571
|
+
} catch {
|
|
1572
|
+
break;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
return internalContentPathIndex(entries);
|
|
1576
|
+
}
|
|
1577
|
+
function bodyHasInternalContentLinkTokens(doc, html) {
|
|
1578
|
+
if (html && html.includes("[[internal:")) return true;
|
|
1579
|
+
if (!doc) return false;
|
|
1580
|
+
try {
|
|
1581
|
+
return JSON.stringify(doc).includes("[[internal:");
|
|
1582
|
+
} catch {
|
|
1583
|
+
return false;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1436
1587
|
// src/server/blog-post-footer.tsx
|
|
1437
1588
|
import {
|
|
1438
1589
|
jsonLdJson as jsonLdJson2
|
|
1439
1590
|
} from "@roottale/cms-client/server";
|
|
1440
1591
|
import { extractFaqEntries, faqPageJsonLd } from "@roottale/cms-core";
|
|
1441
|
-
import { jsx as
|
|
1592
|
+
import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1442
1593
|
function renderPostCta(cta) {
|
|
1443
1594
|
if (!cta) return null;
|
|
1444
1595
|
const href = cta.buttonHref.trim();
|
|
@@ -1446,9 +1597,9 @@ function renderPostCta(cta) {
|
|
|
1446
1597
|
if (!isSafeHref2) return null;
|
|
1447
1598
|
const isExternal = /^https?:/i.test(href);
|
|
1448
1599
|
return /* @__PURE__ */ jsxs12("aside", { "data-roottale-cms": "post-cta", className: "rt-cms-post-cta", children: [
|
|
1449
|
-
cta.title ? /* @__PURE__ */
|
|
1450
|
-
cta.description ? /* @__PURE__ */
|
|
1451
|
-
/* @__PURE__ */
|
|
1600
|
+
cta.title ? /* @__PURE__ */ jsx17("p", { className: "rt-cms-post-cta__title", children: cta.title }) : null,
|
|
1601
|
+
cta.description ? /* @__PURE__ */ jsx17("p", { className: "rt-cms-post-cta__desc", children: cta.description }) : null,
|
|
1602
|
+
/* @__PURE__ */ jsx17(
|
|
1452
1603
|
"a",
|
|
1453
1604
|
{
|
|
1454
1605
|
className: "rt-cms-post-cta__btn",
|
|
@@ -1463,9 +1614,9 @@ function renderPostCta(cta) {
|
|
|
1463
1614
|
function renderRelatedPosts(posts, href, title) {
|
|
1464
1615
|
if (posts.length === 0) return null;
|
|
1465
1616
|
return /* @__PURE__ */ jsxs12("nav", { "data-roottale-cms": "related", className: "rt-cms-related", "aria-label": title, children: [
|
|
1466
|
-
/* @__PURE__ */
|
|
1467
|
-
/* @__PURE__ */
|
|
1468
|
-
post.featuredImageUrl ? /* @__PURE__ */
|
|
1617
|
+
/* @__PURE__ */ jsx17("h2", { className: "rt-cms-related-title", children: title }),
|
|
1618
|
+
/* @__PURE__ */ jsx17("ul", { className: "rt-cms-related-list", children: posts.map((post) => /* @__PURE__ */ jsx17("li", { className: "rt-cms-related-item", children: /* @__PURE__ */ jsxs12("a", { className: "rt-cms-related-link", href: href(post), children: [
|
|
1619
|
+
post.featuredImageUrl ? /* @__PURE__ */ jsx17(
|
|
1469
1620
|
"img",
|
|
1470
1621
|
{
|
|
1471
1622
|
className: "rt-cms-related-thumb",
|
|
@@ -1474,14 +1625,14 @@ function renderRelatedPosts(posts, href, title) {
|
|
|
1474
1625
|
loading: "lazy"
|
|
1475
1626
|
}
|
|
1476
1627
|
) : null,
|
|
1477
|
-
/* @__PURE__ */
|
|
1628
|
+
/* @__PURE__ */ jsx17("span", { className: "rt-cms-related-post-title", children: post.title })
|
|
1478
1629
|
] }) }, post.id)) })
|
|
1479
1630
|
] });
|
|
1480
1631
|
}
|
|
1481
1632
|
function renderFaqJsonLd(doc) {
|
|
1482
1633
|
const jsonLd = faqPageJsonLd(extractFaqEntries(doc));
|
|
1483
1634
|
if (!jsonLd) return null;
|
|
1484
|
-
return /* @__PURE__ */
|
|
1635
|
+
return /* @__PURE__ */ jsx17(
|
|
1485
1636
|
"script",
|
|
1486
1637
|
{
|
|
1487
1638
|
type: "application/ld+json",
|
|
@@ -1496,10 +1647,10 @@ function RootTaleFaqJsonLd({
|
|
|
1496
1647
|
}
|
|
1497
1648
|
|
|
1498
1649
|
// src/server/blog-post.tsx
|
|
1499
|
-
import { jsx as
|
|
1650
|
+
import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1500
1651
|
async function fetchRelatedPostsFailSoft(input) {
|
|
1501
1652
|
if (input.count <= 0) return [];
|
|
1502
|
-
const candidates = await
|
|
1653
|
+
const candidates = await fetchPosts3({
|
|
1503
1654
|
apiKey: input.apiKey,
|
|
1504
1655
|
baseUrl: input.baseUrl,
|
|
1505
1656
|
type: "post",
|
|
@@ -1571,6 +1722,16 @@ async function lookupPost(input) {
|
|
|
1571
1722
|
});
|
|
1572
1723
|
return post ? { kind: "post", post } : { kind: "missing" };
|
|
1573
1724
|
}
|
|
1725
|
+
async function resolveInternalLinksSource(source, input) {
|
|
1726
|
+
if (source === false) return null;
|
|
1727
|
+
if (source !== "auto" && source !== void 0) return source;
|
|
1728
|
+
if (!input.hasTokens) return null;
|
|
1729
|
+
return fetchInternalContentPathIndex({
|
|
1730
|
+
apiKey: input.apiKey,
|
|
1731
|
+
baseUrl: input.baseUrl,
|
|
1732
|
+
locale: input.locale
|
|
1733
|
+
});
|
|
1734
|
+
}
|
|
1574
1735
|
async function RootTaleBlogPost(props) {
|
|
1575
1736
|
const {
|
|
1576
1737
|
apiKey,
|
|
@@ -1589,7 +1750,8 @@ async function RootTaleBlogPost(props) {
|
|
|
1589
1750
|
showFieldFacts = false,
|
|
1590
1751
|
theme: themeProp,
|
|
1591
1752
|
authorHref = defaultAuthorHref,
|
|
1592
|
-
breadcrumb
|
|
1753
|
+
breadcrumb,
|
|
1754
|
+
internalLinks = "auto"
|
|
1593
1755
|
} = props;
|
|
1594
1756
|
const [lookup, themeStyle, blogSettings] = await Promise.all([
|
|
1595
1757
|
lookupPost({ apiKey, baseUrl, slugOrId, previewToken, locale }),
|
|
@@ -1597,10 +1759,10 @@ async function RootTaleBlogPost(props) {
|
|
|
1597
1759
|
fetchBlogSettingsFailSoft({ apiKey, baseUrl })
|
|
1598
1760
|
]);
|
|
1599
1761
|
if (lookup.kind === "preview-expired") {
|
|
1600
|
-
return previewExpiredElement ?? /* @__PURE__ */
|
|
1762
|
+
return previewExpiredElement ?? /* @__PURE__ */ jsx18("div", { "data-roottale-cms": "post-preview-expired", style: themeStyle ?? void 0, children: /* @__PURE__ */ jsx18("p", { className: "rt-cms-empty", children: t(locale, "previewExpired") }) });
|
|
1601
1763
|
}
|
|
1602
1764
|
if (lookup.kind === "missing") {
|
|
1603
|
-
return notFoundElement ?? /* @__PURE__ */
|
|
1765
|
+
return notFoundElement ?? /* @__PURE__ */ jsx18("div", { "data-roottale-cms": "post-missing", style: themeStyle ?? void 0, children: /* @__PURE__ */ jsx18("p", { className: "rt-cms-empty", children: t(locale, "postNotFound") }) });
|
|
1604
1766
|
}
|
|
1605
1767
|
const post = lookup.post;
|
|
1606
1768
|
const display = resolvePostDisplay2(post, blogSettings, {
|
|
@@ -1609,13 +1771,22 @@ async function RootTaleBlogPost(props) {
|
|
|
1609
1771
|
});
|
|
1610
1772
|
const toc = display.showTableOfContents ? extractToc(post.bodyJson) : [];
|
|
1611
1773
|
const renderDoc = toc.length > 0 ? attachHeadingIds(post.bodyJson, toc) : post.bodyJson;
|
|
1612
|
-
const related = await
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1774
|
+
const [related, internalLinkIndex, footerPattern] = await Promise.all([
|
|
1775
|
+
fetchRelatedPostsFailSoft({
|
|
1776
|
+
post,
|
|
1777
|
+
apiKey,
|
|
1778
|
+
baseUrl,
|
|
1779
|
+
count: relatedPostsCount,
|
|
1780
|
+
locale
|
|
1781
|
+
}),
|
|
1782
|
+
resolveInternalLinksSource(internalLinks, {
|
|
1783
|
+
apiKey,
|
|
1784
|
+
baseUrl,
|
|
1785
|
+
locale,
|
|
1786
|
+
hasTokens: bodyHasInternalContentLinkTokens(post.bodyJson, post.bodyHtml)
|
|
1787
|
+
}),
|
|
1788
|
+
resolvePostPatternFailSoft(post, { apiKey, baseUrl }, POST_FOOTER_PATTERN_SLOT2)
|
|
1789
|
+
]);
|
|
1619
1790
|
const relatedHref = props.postHref ?? (collections ? makeCollectionPostHref(collections) : defaultPostHref);
|
|
1620
1791
|
const authorName = post.authorName;
|
|
1621
1792
|
const authorSlug = post.authorSlug;
|
|
@@ -1649,7 +1820,7 @@ async function RootTaleBlogPost(props) {
|
|
|
1649
1820
|
options: breadcrumb
|
|
1650
1821
|
}),
|
|
1651
1822
|
/* @__PURE__ */ jsxs13("header", { className: "rt-cms-post-header", children: [
|
|
1652
|
-
categories.length > 0 ? /* @__PURE__ */
|
|
1823
|
+
categories.length > 0 ? /* @__PURE__ */ jsx18("nav", { className: "rt-cms-post-categories", "aria-label": "\uAE00 \uCE74\uD14C\uACE0\uB9AC", children: categories.map((category) => /* @__PURE__ */ jsx18(
|
|
1653
1824
|
"a",
|
|
1654
1825
|
{
|
|
1655
1826
|
className: "rt-cms-post-category",
|
|
@@ -1658,8 +1829,8 @@ async function RootTaleBlogPost(props) {
|
|
|
1658
1829
|
},
|
|
1659
1830
|
category.id
|
|
1660
1831
|
)) }) : null,
|
|
1661
|
-
showTitle ? /* @__PURE__ */
|
|
1662
|
-
post.excerpt ? /* @__PURE__ */
|
|
1832
|
+
showTitle ? /* @__PURE__ */ jsx18("h1", { className: "rt-cms-title", children: post.title }) : null,
|
|
1833
|
+
post.excerpt ? /* @__PURE__ */ jsx18("p", { className: "rt-cms-excerpt", children: post.excerpt }) : null,
|
|
1663
1834
|
publishedDate || modifiedDate || showTopAuthor || views !== null || reviewedDate ? /* @__PURE__ */ jsxs13("p", { className: "rt-cms-meta", children: [
|
|
1664
1835
|
publishedDate ? /* @__PURE__ */ jsxs13(
|
|
1665
1836
|
"time",
|
|
@@ -1688,7 +1859,7 @@ async function RootTaleBlogPost(props) {
|
|
|
1688
1859
|
showTopAuthor ? /* @__PURE__ */ jsxs13("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "author", children: [
|
|
1689
1860
|
"\uC791\uC131\uC790",
|
|
1690
1861
|
" ",
|
|
1691
|
-
authorSlug ? /* @__PURE__ */
|
|
1862
|
+
authorSlug ? /* @__PURE__ */ jsx18("a", { className: "rt-cms-author-link", href: authorHref(authorSlug), children: authorName }) : authorName
|
|
1692
1863
|
] }) : null,
|
|
1693
1864
|
editorialReview && reviewedDate ? /* @__PURE__ */ jsxs13("span", { className: "rt-cms-meta-item", "data-rt-cms-meta": "reviewed", children: [
|
|
1694
1865
|
"\uB0B4\uC6A9 \uAC80\uD1A0 ",
|
|
@@ -1704,7 +1875,7 @@ async function RootTaleBlogPost(props) {
|
|
|
1704
1875
|
] }),
|
|
1705
1876
|
renderArchiveRelated(post.archiveRelated, collections),
|
|
1706
1877
|
showFieldFacts ? renderFieldFacts(post) : null,
|
|
1707
|
-
/* @__PURE__ */
|
|
1878
|
+
/* @__PURE__ */ jsx18(
|
|
1708
1879
|
BlogPostBody,
|
|
1709
1880
|
{
|
|
1710
1881
|
doc: renderDoc,
|
|
@@ -1714,11 +1885,13 @@ async function RootTaleBlogPost(props) {
|
|
|
1714
1885
|
toc,
|
|
1715
1886
|
tocTitle: display.tocTitle,
|
|
1716
1887
|
tocPosition: display.tocPosition,
|
|
1717
|
-
bodyHtml: post.bodyHtml
|
|
1888
|
+
bodyHtml: post.bodyHtml,
|
|
1889
|
+
internalLinks: internalLinkIndex
|
|
1718
1890
|
}
|
|
1719
1891
|
),
|
|
1892
|
+
renderPostPattern(footerPattern, POST_FOOTER_PATTERN_SLOT2, { apiKey, baseUrl }),
|
|
1720
1893
|
showAuthorCard && authorName ? /* @__PURE__ */ jsxs13("section", { className: "rt-cms-author-card", "aria-label": "\uC791\uC131\uC790", children: [
|
|
1721
|
-
authorImageUrl ? /* @__PURE__ */
|
|
1894
|
+
authorImageUrl ? /* @__PURE__ */ jsx18(
|
|
1722
1895
|
"img",
|
|
1723
1896
|
{
|
|
1724
1897
|
className: "rt-cms-author-card__image",
|
|
@@ -1727,11 +1900,11 @@ async function RootTaleBlogPost(props) {
|
|
|
1727
1900
|
loading: "lazy",
|
|
1728
1901
|
style: authorImageStyle
|
|
1729
1902
|
}
|
|
1730
|
-
) : /* @__PURE__ */
|
|
1903
|
+
) : /* @__PURE__ */ jsx18("div", { className: "rt-cms-author-card__avatar", "aria-hidden": "true", children: authorInitials(authorName) }),
|
|
1731
1904
|
/* @__PURE__ */ jsxs13("div", { className: "rt-cms-author-card__body", children: [
|
|
1732
|
-
/* @__PURE__ */
|
|
1733
|
-
/* @__PURE__ */
|
|
1734
|
-
authorBio ? /* @__PURE__ */
|
|
1905
|
+
/* @__PURE__ */ jsx18("p", { className: "rt-cms-author-card__eyebrow", children: display.authorCardEyebrow }),
|
|
1906
|
+
/* @__PURE__ */ jsx18("p", { className: "rt-cms-author-card__name", children: authorSlug ? /* @__PURE__ */ jsx18("a", { className: "rt-cms-author-link", href: authorHref(authorSlug), children: authorName }) : authorName }),
|
|
1907
|
+
authorBio ? /* @__PURE__ */ jsx18("p", { className: "rt-cms-author-card__description", children: authorBio }) : null
|
|
1735
1908
|
] })
|
|
1736
1909
|
] }) : null,
|
|
1737
1910
|
renderRelatedPosts(related, relatedHref, relatedPostsTitle),
|
|
@@ -1743,7 +1916,7 @@ async function RootTaleBlogPost(props) {
|
|
|
1743
1916
|
}
|
|
1744
1917
|
|
|
1745
1918
|
// src/server/preview-notice.tsx
|
|
1746
|
-
import { jsx as
|
|
1919
|
+
import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1747
1920
|
function formatExpiry(locale, iso) {
|
|
1748
1921
|
const date = new Date(iso);
|
|
1749
1922
|
if (Number.isNaN(date.getTime())) return null;
|
|
@@ -1760,14 +1933,14 @@ function RootTalePreviewNotice({
|
|
|
1760
1933
|
}) {
|
|
1761
1934
|
const expiry = expiresAt ? formatExpiry(locale, expiresAt) : null;
|
|
1762
1935
|
return /* @__PURE__ */ jsxs14("div", { "data-roottale-cms": "preview-notice", className: "rt-cms-preview-notice", role: "status", children: [
|
|
1763
|
-
/* @__PURE__ */
|
|
1764
|
-
expiry ? /* @__PURE__ */
|
|
1936
|
+
/* @__PURE__ */ jsx19("span", { children: message ?? t(locale, "previewNotice") }),
|
|
1937
|
+
expiry ? /* @__PURE__ */ jsx19("span", { className: "rt-cms-preview-notice__expiry", children: expiry }) : null
|
|
1765
1938
|
] });
|
|
1766
1939
|
}
|
|
1767
1940
|
|
|
1768
1941
|
// src/server/page.tsx
|
|
1769
1942
|
import { fetchPost as fetchPost2 } from "@roottale/cms-client/server";
|
|
1770
|
-
import { jsx as
|
|
1943
|
+
import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1771
1944
|
async function RootTalePage(props) {
|
|
1772
1945
|
const {
|
|
1773
1946
|
apiKey,
|
|
@@ -1784,7 +1957,7 @@ async function RootTalePage(props) {
|
|
|
1784
1957
|
resolveThemeStyleFailSoft({ themeProp, apiKey, baseUrl })
|
|
1785
1958
|
]);
|
|
1786
1959
|
if (!post) {
|
|
1787
|
-
return notFoundElement ?? /* @__PURE__ */
|
|
1960
|
+
return notFoundElement ?? /* @__PURE__ */ jsx20("div", { "data-roottale-cms": "page-missing", style: themeStyle ?? void 0, children: /* @__PURE__ */ jsx20("p", { className: "rt-cms-empty", children: t(locale, "pageNotFound") }) });
|
|
1788
1961
|
}
|
|
1789
1962
|
const pageBodySource = resolvePostBodySource(post.bodyHtml);
|
|
1790
1963
|
const bodyJson = pageBodySource.kind === "json" && prepareBody ? await prepareBody(post.bodyJson) : post.bodyJson;
|
|
@@ -1795,8 +1968,8 @@ async function RootTalePage(props) {
|
|
|
1795
1968
|
className: "rt-cms-article",
|
|
1796
1969
|
style: themeStyle ?? void 0,
|
|
1797
1970
|
children: [
|
|
1798
|
-
showTitle ? /* @__PURE__ */
|
|
1799
|
-
/* @__PURE__ */
|
|
1971
|
+
showTitle ? /* @__PURE__ */ jsx20("header", { className: "rt-cms-post-header", children: /* @__PURE__ */ jsx20("h1", { className: "rt-cms-title", children: post.title }) }) : null,
|
|
1972
|
+
/* @__PURE__ */ jsx20("div", { "data-roottale-cms": "body", className: "rt-cms-body", children: pageBodySource.kind === "html" ? /* @__PURE__ */ jsx20("div", { dangerouslySetInnerHTML: { __html: pageBodySource.html } }) : /* @__PURE__ */ jsx20(
|
|
1800
1973
|
RenderTiptap,
|
|
1801
1974
|
{
|
|
1802
1975
|
doc: bodyJson,
|
|
@@ -1812,7 +1985,7 @@ async function RootTalePage(props) {
|
|
|
1812
1985
|
|
|
1813
1986
|
// src/server/exposure-slot.tsx
|
|
1814
1987
|
import { fetchExposure } from "@roottale/cms-client/server";
|
|
1815
|
-
import { jsx as
|
|
1988
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
1816
1989
|
async function RootTaleExposureSlot(props) {
|
|
1817
1990
|
const campaign = await fetchExposure({
|
|
1818
1991
|
apiKey: props.apiKey,
|
|
@@ -1823,7 +1996,7 @@ async function RootTaleExposureSlot(props) {
|
|
|
1823
1996
|
revalidate: props.revalidate
|
|
1824
1997
|
});
|
|
1825
1998
|
if (!campaign || !props.allowedVariants.includes(campaign.content.variant)) return null;
|
|
1826
|
-
return /* @__PURE__ */
|
|
1999
|
+
return /* @__PURE__ */ jsx21(RootTaleExposure, { campaign, allowedVariants: props.allowedVariants });
|
|
1827
2000
|
}
|
|
1828
2001
|
export {
|
|
1829
2002
|
RenderTiptap,
|
|
@@ -1836,14 +2009,17 @@ export {
|
|
|
1836
2009
|
RootTaleLeadForm,
|
|
1837
2010
|
RootTalePage,
|
|
1838
2011
|
RootTalePostCard,
|
|
2012
|
+
RootTalePostPattern,
|
|
1839
2013
|
RootTalePreviewNotice,
|
|
1840
2014
|
RootTaleTableOfContents,
|
|
1841
2015
|
RootTaleThemeProvider,
|
|
1842
2016
|
attachHeadingIds,
|
|
2017
|
+
bodyHasInternalContentLinkTokens,
|
|
1843
2018
|
collectCategories,
|
|
1844
2019
|
collectCategoriesWithCounts,
|
|
1845
2020
|
defaultCategoryHref,
|
|
1846
2021
|
extractToc,
|
|
2022
|
+
fetchInternalContentPathIndex,
|
|
1847
2023
|
filterPostsByCategory,
|
|
1848
2024
|
headingToId,
|
|
1849
2025
|
makeCategoryHref,
|