@roottale/cms-renderer-next 0.67.1 → 0.69.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 +24 -0
- package/dist/routes.d.ts +6 -1
- package/dist/routes.js +13 -2
- package/dist/routes.js.map +1 -1
- package/dist/server.d.ts +4 -30
- package/dist/server.js +2 -14
- package/dist/server.js.map +1 -1
- package/package.json +3 -3
package/dist/server.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { ReactElement, CSSProperties, ReactNode } from 'react';
|
|
|
2
2
|
import { CmsPostContent, RootTaleTheme, CmsPostType, CmsSitePatternPresentation } from '@roottale/cms-client/server';
|
|
3
3
|
export { RootTaleTheme } from '@roottale/cms-client/server';
|
|
4
4
|
import { Block, BindingResolver, RouteCollection, InternalContentLinkResolver } from '@roottale/cms-core';
|
|
5
|
+
export { PostBodySource, resolvePostBodySource } from '@roottale/cms-core';
|
|
5
6
|
export { a as BlogCategory, B as BlogCategoryWithCount, c as collectCategories, b as collectCategoriesWithCounts, d as defaultCategoryHref, f as filterPostsByCategory, m as makeCategoryHref } from './blog-filter-CS4GZws8.js';
|
|
6
7
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
7
8
|
|
|
@@ -266,6 +267,8 @@ type RootTaleBlogPostProps = {
|
|
|
266
267
|
theme?: RootTaleThemeInput;
|
|
267
268
|
locale?: string;
|
|
268
269
|
authorHref?: (slug: string) => string;
|
|
270
|
+
/** 제목 위 분류 링크. breadcrumb 표시와 무관하며, 생략하면 기존 breadcrumb/default 경로를 쓴다. */
|
|
271
|
+
categoryHref?: (slug: string) => string;
|
|
269
272
|
breadcrumb?: BlogPostBreadcrumbOptions;
|
|
270
273
|
/**
|
|
271
274
|
* 본문 예약 내부 링크(`[[internal:키|문구]]`) 해석. 기본 `"auto"` = 본문에 토큰이
|
|
@@ -379,33 +382,4 @@ declare function fetchInternalContentPathIndex(options: FetchInternalContentPath
|
|
|
379
382
|
/** 본문(JSON 또는 HTML)에 토큰이 있는지 — 색인을 만들 필요가 있는지 미리 본다. */
|
|
380
383
|
declare function bodyHasInternalContentLinkTokens(doc: Record<string, unknown> | null | undefined, html?: string | null): boolean;
|
|
381
384
|
|
|
382
|
-
|
|
383
|
-
* 본문 렌더 소스 결정 — HTML 정본 우선, 없으면 Block JSON 폴백.
|
|
384
|
-
*
|
|
385
|
-
* 배경: 외부에서 이관한 글은 **HTML 이 정본**이고 body_json 은 그 파생/대체본이다.
|
|
386
|
-
* 파생본은 표·중첩 목록 같은 구조에서 원문과 미묘하게 달라질 수 있으므로, 원문이
|
|
387
|
-
* 남아 있으면 그것을 보여주는 편이 정확하다. 반대로 에디터에서 편집된 글은
|
|
388
|
-
* body_html 이 비워지므로(저장 경로가 무효화) 자동으로 JSON 경로로 돌아온다.
|
|
389
|
-
*
|
|
390
|
-
* 안전성: HTML 은 신뢰하지 않는다. 최종 sink 는 공용 `sanitizeHtml` 하나뿐이며
|
|
391
|
-
* (cms-core), 렌더 직전 여기서 통과시킨다 — 저장 시점에 정제됐더라도 다시 한다
|
|
392
|
-
* (저장 경로가 여럿이고 sanitizer 규칙은 갱신되므로, 방어는 렌더 시점 기준).
|
|
393
|
-
*
|
|
394
|
-
* sanitize 결과가 실질적으로 비면(태그만 남고 내용이 없음) HTML 을 쓰지 않고
|
|
395
|
-
* JSON 으로 폴백한다 — 빈 화면보다 파생본이 낫다.
|
|
396
|
-
*/
|
|
397
|
-
type PostBodySource = {
|
|
398
|
-
readonly kind: "html";
|
|
399
|
-
readonly html: string;
|
|
400
|
-
} | {
|
|
401
|
-
readonly kind: "json";
|
|
402
|
-
};
|
|
403
|
-
/**
|
|
404
|
-
* 렌더에 쓸 본문 소스를 고른다.
|
|
405
|
-
*
|
|
406
|
-
* @param bodyHtml 원문 HTML 정본 (null/미포함 = 구 서버이거나 JSON 이 정본).
|
|
407
|
-
* @returns `kind: "html"` 이면 `html` 은 **이미 sanitize 된** 값이라 그대로 주입 가능.
|
|
408
|
-
*/
|
|
409
|
-
declare function resolvePostBodySource(bodyHtml: string | null | undefined): PostBodySource;
|
|
410
|
-
|
|
411
|
-
export { type BlogPostBreadcrumbOptions, type CtaButton, type CtaButtonType, type FetchInternalContentPathIndexOptions, type FloatingCtaPosition, type InternalLinksSource, type LeadFormVertical, type PostBodySource, RenderTiptap, RootTaleBlogCategories, type RootTaleBlogCategoriesProps, RootTaleBlogList, type RootTaleBlogListProps, RootTaleBlogPost, type RootTaleBlogPostProps, type RootTaleCssVars, RootTaleExposureSlot, type RootTaleExposureSlotProps, RootTaleFaqJsonLd, RootTaleFloatingCta, type RootTaleFloatingCtaProps, RootTaleLeadForm, type RootTaleLeadFormProps, RootTalePage, type RootTalePageProps, RootTalePostCard, type RootTalePostCardProps, RootTalePostPattern, type RootTalePostPatternProps, RootTalePreviewNotice, type RootTalePreviewNoticeProps, RootTaleTableOfContents, type RootTaleTableOfContentsProps, type RootTaleThemeInput, RootTaleThemeProvider, type TocEntry, attachHeadingIds, bodyHasInternalContentLinkTokens, extractToc, fetchInternalContentPathIndex, headingToId, renderBlock, renderBlocks, renderBoundBlocks, resolvePostBodySource, themeToCssVars };
|
|
385
|
+
export { type BlogPostBreadcrumbOptions, type CtaButton, type CtaButtonType, type FetchInternalContentPathIndexOptions, type FloatingCtaPosition, type InternalLinksSource, type LeadFormVertical, RenderTiptap, RootTaleBlogCategories, type RootTaleBlogCategoriesProps, RootTaleBlogList, type RootTaleBlogListProps, RootTaleBlogPost, type RootTaleBlogPostProps, type RootTaleCssVars, RootTaleExposureSlot, type RootTaleExposureSlotProps, RootTaleFaqJsonLd, RootTaleFloatingCta, type RootTaleFloatingCtaProps, RootTaleLeadForm, type RootTaleLeadFormProps, RootTalePage, type RootTalePageProps, RootTalePostCard, type RootTalePostCardProps, RootTalePostPattern, type RootTalePostPatternProps, RootTalePreviewNotice, type RootTalePreviewNoticeProps, RootTaleTableOfContents, type RootTaleTableOfContentsProps, type RootTaleThemeInput, RootTaleThemeProvider, type TocEntry, attachHeadingIds, bodyHasInternalContentLinkTokens, extractToc, fetchInternalContentPathIndex, headingToId, renderBlock, renderBlocks, renderBoundBlocks, themeToCssVars };
|
package/dist/server.js
CHANGED
|
@@ -1455,19 +1455,7 @@ import {
|
|
|
1455
1455
|
} from "@roottale/cms-core";
|
|
1456
1456
|
|
|
1457
1457
|
// src/server/post-body-source.ts
|
|
1458
|
-
import {
|
|
1459
|
-
function hasVisibleContent(html) {
|
|
1460
|
-
if (/<(?:img|hr|br|iframe|video|audio)\b/i.test(html)) return true;
|
|
1461
|
-
return html.replace(/<[^>]*>/g, "").replace(/ /gi, " ").trim().length > 0;
|
|
1462
|
-
}
|
|
1463
|
-
function resolvePostBodySource(bodyHtml) {
|
|
1464
|
-
if (typeof bodyHtml !== "string") return { kind: "json" };
|
|
1465
|
-
const trimmed = bodyHtml.trim();
|
|
1466
|
-
if (trimmed.length === 0) return { kind: "json" };
|
|
1467
|
-
const sanitized = sanitizeHtml2(trimmed);
|
|
1468
|
-
if (!hasVisibleContent(sanitized)) return { kind: "json" };
|
|
1469
|
-
return { kind: "html", html: sanitized };
|
|
1470
|
-
}
|
|
1458
|
+
import { resolvePostBodySource } from "@roottale/cms-core";
|
|
1471
1459
|
|
|
1472
1460
|
// src/server/blog-post-body.tsx
|
|
1473
1461
|
import { Fragment as Fragment6, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
@@ -1836,7 +1824,7 @@ async function RootTaleBlogPost(props) {
|
|
|
1836
1824
|
const publishedDate = display.showDate ? formatPostDate(post.publishedAt) : "";
|
|
1837
1825
|
const modifiedDate = display.showDate ? distinctModifiedDate(post.publishedAt, post.updatedAt) : null;
|
|
1838
1826
|
const showTopAuthor = display.showAuthor && Boolean(authorName);
|
|
1839
|
-
const categoryHref = breadcrumb?.categoryHref ?? defaultCategoryHref;
|
|
1827
|
+
const categoryHref = props.categoryHref ?? breadcrumb?.categoryHref ?? defaultCategoryHref;
|
|
1840
1828
|
const categories = breadcrumb?.showCategory === false ? [] : post.terms.filter((term) => term.taxonomy === "category");
|
|
1841
1829
|
const authorBio = post.authorBio;
|
|
1842
1830
|
const authorImageUrl = post.authorImageUrl;
|