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