@asteroidcms/core-utils 0.1.6 → 0.1.8
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/README.md +1 -1
- package/dist/client.cjs +522 -45
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +347 -2
- package/dist/client.d.ts +347 -2
- package/dist/client.js +518 -48
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +379 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +186 -1
- package/dist/index.d.ts +186 -1
- package/dist/index.js +368 -1
- package/dist/index.js.map +1 -1
- package/dist/next.cjs +211 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +114 -0
- package/dist/next.d.ts +114 -0
- package/dist/next.js +201 -0
- package/dist/next.js.map +1 -0
- package/package.json +13 -1
package/dist/client.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { createContext,
|
|
2
|
+
import { createContext, memo, useMemo, useRef, useState, useLayoutEffect, Fragment, createElement, useContext, useEffect } from 'react';
|
|
3
3
|
import { ApolloProvider, useQuery, useMutation } from '@apollo/client/react';
|
|
4
4
|
import { gql, HttpLink, ApolloClient, InMemoryCache, ApolloLink, CombinedGraphQLErrors, CombinedProtocolErrors } from '@apollo/client';
|
|
5
5
|
import { SetContextLink } from '@apollo/client/link/context';
|
|
6
6
|
import { ErrorLink } from '@apollo/client/link/error';
|
|
7
|
-
import { jsx,
|
|
7
|
+
import { jsxs, jsx, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
8
8
|
import { createPortal } from 'react-dom';
|
|
9
9
|
import hljs from 'highlight.js/lib/common';
|
|
10
10
|
|
|
@@ -1044,37 +1044,38 @@ function isIconEnabled(el) {
|
|
|
1044
1044
|
if (v === null) return false;
|
|
1045
1045
|
return v !== "false" && v !== "0";
|
|
1046
1046
|
}
|
|
1047
|
-
function enhanceCallouts(root) {
|
|
1047
|
+
function enhanceCallouts(root, calloutIcons) {
|
|
1048
1048
|
const callouts = root.querySelectorAll("aside[data-callout]");
|
|
1049
1049
|
callouts.forEach((el) => {
|
|
1050
|
-
if (el.dataset.rtCalloutEnhanced === "1") return;
|
|
1051
|
-
el.dataset.rtCalloutEnhanced = "1";
|
|
1052
|
-
if (!isIconEnabled(el)) return;
|
|
1053
1050
|
if (el.querySelector(":scope > .rt-callout-icon")) return;
|
|
1051
|
+
if (!isIconEnabled(el)) return;
|
|
1054
1052
|
const variant = calloutVariantOf(el);
|
|
1055
1053
|
const icon = document.createElement("span");
|
|
1056
1054
|
icon.className = "rt-callout-icon";
|
|
1057
1055
|
icon.dataset.variant = variant;
|
|
1058
1056
|
icon.setAttribute("aria-hidden", "true");
|
|
1057
|
+
if (!calloutIcons || !(variant in calloutIcons)) {
|
|
1058
|
+
icon.innerHTML = CALLOUT_ICON_SVG[variant] ?? CALLOUT_ICON_SVG.default;
|
|
1059
|
+
}
|
|
1059
1060
|
el.prepend(icon);
|
|
1060
1061
|
});
|
|
1062
|
+
if (!calloutIcons) return [];
|
|
1061
1063
|
const chips = [];
|
|
1062
1064
|
root.querySelectorAll(
|
|
1063
1065
|
"aside[data-callout] > .rt-callout-icon"
|
|
1064
1066
|
).forEach((chip) => {
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
}
|
|
1067
|
+
const variant = chip.dataset.variant || (chip.parentElement?.getAttribute("data-variant") ?? "default");
|
|
1068
|
+
if (variant in calloutIcons) {
|
|
1069
|
+
chips.push({ el: chip, variant });
|
|
1070
|
+
}
|
|
1069
1071
|
});
|
|
1070
1072
|
return chips;
|
|
1071
1073
|
}
|
|
1072
1074
|
function enhanceBlockquotes(root) {
|
|
1073
1075
|
const quotes = root.querySelectorAll("blockquote");
|
|
1074
1076
|
quotes.forEach((bq) => {
|
|
1075
|
-
if (bq.
|
|
1077
|
+
if (bq.querySelector(":scope > .rt-quote-open")) return;
|
|
1076
1078
|
if (bq.closest('figure[data-variant="pullquote"]')) return;
|
|
1077
|
-
bq.dataset.rtQuoted = "1";
|
|
1078
1079
|
const { first, last } = findQuoteBody(bq);
|
|
1079
1080
|
if (!first || !last) return;
|
|
1080
1081
|
const open = document.createElement("span");
|
|
@@ -1113,11 +1114,10 @@ function highlightCodeBlock(pre) {
|
|
|
1113
1114
|
if (!lang) return;
|
|
1114
1115
|
const code = pre.querySelector("code");
|
|
1115
1116
|
if (!code) return;
|
|
1116
|
-
if (code.
|
|
1117
|
+
if (code.classList.contains("hljs")) return;
|
|
1117
1118
|
const source = code.textContent ?? "";
|
|
1118
1119
|
code.innerHTML = highlightSource(source, lang);
|
|
1119
1120
|
code.classList.add("hljs");
|
|
1120
|
-
code.dataset.rtHighlighted = "1";
|
|
1121
1121
|
}
|
|
1122
1122
|
var DIFF_SEPARATOR_RE = /\n?@@---@@\n?/;
|
|
1123
1123
|
function diffLines(a, b) {
|
|
@@ -1240,8 +1240,7 @@ function buildCodeBlockLabel(pre) {
|
|
|
1240
1240
|
function enhanceCodeBlocks(root) {
|
|
1241
1241
|
const blocks = root.querySelectorAll("pre");
|
|
1242
1242
|
blocks.forEach((pre) => {
|
|
1243
|
-
if (pre.
|
|
1244
|
-
pre.dataset.rtEnhanced = "1";
|
|
1243
|
+
if (pre.classList.contains("rt-codeblock")) return;
|
|
1245
1244
|
pre.classList.add("rt-codeblock");
|
|
1246
1245
|
const variant = pre.dataset.variant;
|
|
1247
1246
|
if (variant === "diff") {
|
|
@@ -1585,7 +1584,7 @@ function BuiltinCalloutIcon({ variant }) {
|
|
|
1585
1584
|
const html = CALLOUT_ICON_SVG[variant] ?? CALLOUT_ICON_SVG.default;
|
|
1586
1585
|
return /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: html } });
|
|
1587
1586
|
}
|
|
1588
|
-
|
|
1587
|
+
var RichTextContent = memo(function RichTextContent2({
|
|
1589
1588
|
html,
|
|
1590
1589
|
classMap,
|
|
1591
1590
|
as = "div",
|
|
@@ -1603,49 +1602,58 @@ function RichTextContent({
|
|
|
1603
1602
|
[html, merged]
|
|
1604
1603
|
);
|
|
1605
1604
|
const ref = useRef(null);
|
|
1606
|
-
const
|
|
1607
|
-
|
|
1605
|
+
const prevSafe = useRef("");
|
|
1606
|
+
const chipsRef = useRef([]);
|
|
1607
|
+
const onReadyRef = useRef(onReady);
|
|
1608
|
+
onReadyRef.current = onReady;
|
|
1609
|
+
const contentRefStable = useRef(contentRef);
|
|
1610
|
+
contentRefStable.current = contentRef;
|
|
1611
|
+
const calloutIconsRef = useRef(calloutIcons);
|
|
1612
|
+
calloutIconsRef.current = calloutIcons;
|
|
1613
|
+
const [renderKey, setRenderKey] = useState(0);
|
|
1614
|
+
useLayoutEffect(() => {
|
|
1608
1615
|
ensureCodeBlockStyles();
|
|
1609
1616
|
const root = ref.current;
|
|
1610
1617
|
if (!root) return;
|
|
1611
|
-
if (
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
raf = requestAnimationFrame(() => {
|
|
1625
|
-
raf = 0;
|
|
1626
|
-
apply();
|
|
1627
|
-
});
|
|
1628
|
-
});
|
|
1629
|
-
apply();
|
|
1630
|
-
return () => {
|
|
1631
|
-
mo.disconnect();
|
|
1632
|
-
if (raf) cancelAnimationFrame(raf);
|
|
1633
|
-
};
|
|
1634
|
-
}, [safe, onReady, contentRef]);
|
|
1618
|
+
if (contentRefStable.current) contentRefStable.current.current = root;
|
|
1619
|
+
if (prevSafe.current === safe) return;
|
|
1620
|
+
prevSafe.current = safe;
|
|
1621
|
+
root.innerHTML = safe;
|
|
1622
|
+
enhanceCodeBlocks(root);
|
|
1623
|
+
enhanceBlockquotes(root);
|
|
1624
|
+
const nextChips = enhanceCallouts(root, calloutIconsRef.current);
|
|
1625
|
+
if (!calloutChipsEqual(chipsRef.current, nextChips)) {
|
|
1626
|
+
chipsRef.current = nextChips;
|
|
1627
|
+
if (nextChips.length > 0) setRenderKey((n) => n + 1);
|
|
1628
|
+
}
|
|
1629
|
+
onReadyRef.current?.(root);
|
|
1630
|
+
}, [safe]);
|
|
1635
1631
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1636
1632
|
createElement(as, {
|
|
1637
1633
|
ref,
|
|
1638
|
-
className
|
|
1639
|
-
dangerouslySetInnerHTML: { __html: safe }
|
|
1634
|
+
className
|
|
1640
1635
|
}),
|
|
1641
|
-
|
|
1636
|
+
chipsRef.current.map(
|
|
1642
1637
|
(chip, i) => createPortal(
|
|
1643
|
-
|
|
1638
|
+
calloutIconsRef.current && chip.variant in calloutIconsRef.current ? calloutIconsRef.current[chip.variant] : /* @__PURE__ */ jsx(BuiltinCalloutIcon, { variant: chip.variant }),
|
|
1644
1639
|
chip.el,
|
|
1645
1640
|
`${chip.variant}:${i}`
|
|
1646
1641
|
)
|
|
1647
1642
|
)
|
|
1648
1643
|
] });
|
|
1644
|
+
}, richTextPropsEqual);
|
|
1645
|
+
function richTextPropsEqual(prev, next) {
|
|
1646
|
+
if (prev.html !== next.html) return false;
|
|
1647
|
+
if (prev.classMap !== next.classMap) return false;
|
|
1648
|
+
if (prev.as !== next.as) return false;
|
|
1649
|
+
if (prev.className !== next.className) return false;
|
|
1650
|
+
const prevKeys = prev.calloutIcons ? Object.keys(prev.calloutIcons) : [];
|
|
1651
|
+
const nextKeys = next.calloutIcons ? Object.keys(next.calloutIcons) : [];
|
|
1652
|
+
if (prevKeys.length !== nextKeys.length) return false;
|
|
1653
|
+
for (const k of nextKeys) {
|
|
1654
|
+
if (!prevKeys.includes(k)) return false;
|
|
1655
|
+
}
|
|
1656
|
+
return true;
|
|
1649
1657
|
}
|
|
1650
1658
|
|
|
1651
1659
|
// src/utils/extractHeadings.ts
|
|
@@ -1720,7 +1728,469 @@ function extractHeadingsFromElement(root, options = {}) {
|
|
|
1720
1728
|
});
|
|
1721
1729
|
return out;
|
|
1722
1730
|
}
|
|
1731
|
+
function setMeta(attr, key, content) {
|
|
1732
|
+
let element = document.head.querySelector(
|
|
1733
|
+
`meta[${attr}="${key}"]`
|
|
1734
|
+
);
|
|
1735
|
+
if (!element) {
|
|
1736
|
+
if (!content) return;
|
|
1737
|
+
element = document.createElement("meta");
|
|
1738
|
+
element.setAttribute(attr, key);
|
|
1739
|
+
document.head.appendChild(element);
|
|
1740
|
+
} else if (!content) {
|
|
1741
|
+
element.remove();
|
|
1742
|
+
return;
|
|
1743
|
+
}
|
|
1744
|
+
element.setAttribute("content", content);
|
|
1745
|
+
}
|
|
1746
|
+
function setCanonical(url) {
|
|
1747
|
+
let canonical = document.head.querySelector(
|
|
1748
|
+
'link[rel="canonical"]'
|
|
1749
|
+
);
|
|
1750
|
+
if (!canonical) {
|
|
1751
|
+
canonical = document.createElement("link");
|
|
1752
|
+
canonical.rel = "canonical";
|
|
1753
|
+
document.head.appendChild(canonical);
|
|
1754
|
+
}
|
|
1755
|
+
canonical.href = url;
|
|
1756
|
+
}
|
|
1757
|
+
function JsonLd({ data }) {
|
|
1758
|
+
if (!data) return null;
|
|
1759
|
+
const html = JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
1760
|
+
return /* @__PURE__ */ jsx(
|
|
1761
|
+
"script",
|
|
1762
|
+
{
|
|
1763
|
+
type: "application/ld+json",
|
|
1764
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
1765
|
+
}
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1768
|
+
function Seo({
|
|
1769
|
+
title,
|
|
1770
|
+
description,
|
|
1771
|
+
url,
|
|
1772
|
+
siteName,
|
|
1773
|
+
keywords,
|
|
1774
|
+
twitter,
|
|
1775
|
+
image,
|
|
1776
|
+
noindex
|
|
1777
|
+
}) {
|
|
1778
|
+
useEffect(() => {
|
|
1779
|
+
document.title = title;
|
|
1780
|
+
setMeta("name", "description", description);
|
|
1781
|
+
setCanonical(url);
|
|
1782
|
+
setMeta("property", "og:title", title);
|
|
1783
|
+
setMeta("property", "og:description", description);
|
|
1784
|
+
setMeta("property", "og:url", url);
|
|
1785
|
+
setMeta("property", "og:site_name", siteName || "");
|
|
1786
|
+
setMeta("name", "keywords", keywords || "");
|
|
1787
|
+
setMeta("name", "robots", noindex ? "noindex" : "index");
|
|
1788
|
+
setMeta("name", "twitter:card", image ? "summary_large_image" : "summary");
|
|
1789
|
+
setMeta("name", "twitter:title", title);
|
|
1790
|
+
setMeta("name", "twitter:description", description);
|
|
1791
|
+
setMeta("name", "twitter:site", twitter || "");
|
|
1792
|
+
setMeta("property", "og:image", image || "");
|
|
1793
|
+
setMeta("name", "twitter:image", image || "");
|
|
1794
|
+
}, [title, description, url, siteName, keywords, twitter, image, noindex]);
|
|
1795
|
+
return null;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
// src/seo/seo.builders.ts
|
|
1799
|
+
function applyTitleTemplate(config, title) {
|
|
1800
|
+
return config.titleTemplate ? config.titleTemplate(title) : `${title} | ${config.siteName}`;
|
|
1801
|
+
}
|
|
1802
|
+
function buildOgImageUrl(config, params) {
|
|
1803
|
+
if (config.getOgImageUrl) {
|
|
1804
|
+
return config.getOgImageUrl(params);
|
|
1805
|
+
}
|
|
1806
|
+
const palette = config.ogImage?.palette;
|
|
1807
|
+
if (!palette) return void 0;
|
|
1808
|
+
const apiPath = config.ogImage?.apiPath ?? "/api/og";
|
|
1809
|
+
const base = config.baseUrl.replace(/\/$/, "");
|
|
1810
|
+
const searchParams = new URLSearchParams({
|
|
1811
|
+
title: params.title,
|
|
1812
|
+
type: params.type ?? "article",
|
|
1813
|
+
siteName: config.siteName,
|
|
1814
|
+
bg: palette.background,
|
|
1815
|
+
fg: palette.foreground,
|
|
1816
|
+
accent: palette.accent
|
|
1817
|
+
});
|
|
1818
|
+
if (params.subtitle?.trim()) searchParams.set("subtitle", params.subtitle.trim());
|
|
1819
|
+
if (params.eyebrow?.trim()) searchParams.set("eyebrow", params.eyebrow.trim());
|
|
1820
|
+
if (palette.accentMuted) searchParams.set("accentMuted", palette.accentMuted);
|
|
1821
|
+
if (palette.mutedText) searchParams.set("muted", palette.mutedText);
|
|
1822
|
+
return `${base}${apiPath}?${searchParams.toString()}`;
|
|
1823
|
+
}
|
|
1824
|
+
function resolveArticleImage(post, config) {
|
|
1825
|
+
const featuredImage = config.cmsUrl ? cmsImage(post.featured_image, { cmsUrl: config.cmsUrl }) : "";
|
|
1826
|
+
if (featuredImage) return featuredImage;
|
|
1827
|
+
const description = post.meta_description?.trim() || post.description?.trim() || config.defaultDescription;
|
|
1828
|
+
return buildOgImageUrl(config, {
|
|
1829
|
+
title: post.title,
|
|
1830
|
+
subtitle: description,
|
|
1831
|
+
eyebrow: config.contentLabel ?? "Article",
|
|
1832
|
+
type: "article"
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
function buildArticleSeoValues(post, config, slug, options) {
|
|
1836
|
+
const articlePath = config.articlePath ?? "/blog";
|
|
1837
|
+
const url = `${config.baseUrl.replace(/\/$/, "")}${articlePath}/${slug}`;
|
|
1838
|
+
const description = post.meta_description?.trim() || post.description?.trim() || config.defaultDescription || `Read the latest from ${config.siteName}.`;
|
|
1839
|
+
return {
|
|
1840
|
+
title: applyTitleTemplate(config, post.title),
|
|
1841
|
+
siteName: config.siteName,
|
|
1842
|
+
twitter: config.twitter ?? "",
|
|
1843
|
+
description,
|
|
1844
|
+
url,
|
|
1845
|
+
keywords: config.defaultKeywords ?? post.title,
|
|
1846
|
+
image: resolveArticleImage(post, config),
|
|
1847
|
+
noindex: options?.noindex ?? config.noindex,
|
|
1848
|
+
manifestUrl: config.manifestUrl
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
function buildArticleListingSeoValues(config, options) {
|
|
1852
|
+
const articlePath = config.articlePath ?? "/blog";
|
|
1853
|
+
const base = config.baseUrl.replace(/\/$/, "");
|
|
1854
|
+
const label = config.contentLabel ?? "Articles";
|
|
1855
|
+
const categoryName = options?.categoryName?.trim();
|
|
1856
|
+
const categorySlug = options?.categorySlug?.trim();
|
|
1857
|
+
const titleText = categoryName ? `${categoryName} ${label}` : label;
|
|
1858
|
+
const description = categoryName ? `Explore ${categoryName} ${label.toLowerCase()}, guides, and the latest updates from ${config.siteName}.` : config.defaultDescription || `Browse ${label.toLowerCase()}, insights, and the latest updates from ${config.siteName}.`;
|
|
1859
|
+
const url = categorySlug ? `${base}${articlePath}/category/${categorySlug}` : `${base}${articlePath}`;
|
|
1860
|
+
return {
|
|
1861
|
+
title: applyTitleTemplate(config, titleText),
|
|
1862
|
+
siteName: config.siteName,
|
|
1863
|
+
twitter: config.twitter ?? "",
|
|
1864
|
+
description,
|
|
1865
|
+
url,
|
|
1866
|
+
keywords: config.defaultKeywords ?? (categoryName ? `${categoryName}, ${config.siteName}` : `${config.siteName} ${label.toLowerCase()}`),
|
|
1867
|
+
image: buildOgImageUrl(config, {
|
|
1868
|
+
title: titleText,
|
|
1869
|
+
subtitle: description,
|
|
1870
|
+
eyebrow: categoryName ? "Category" : label,
|
|
1871
|
+
type: "listing"
|
|
1872
|
+
}),
|
|
1873
|
+
noindex: options?.noindex ?? config.noindex,
|
|
1874
|
+
manifestUrl: config.manifestUrl
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
function seoValuesToClientProps(values) {
|
|
1878
|
+
return {
|
|
1879
|
+
title: values.title,
|
|
1880
|
+
description: values.description,
|
|
1881
|
+
url: values.url,
|
|
1882
|
+
siteName: values.siteName,
|
|
1883
|
+
keywords: values.keywords,
|
|
1884
|
+
twitter: values.twitter,
|
|
1885
|
+
image: values.image,
|
|
1886
|
+
noindex: values.noindex
|
|
1887
|
+
};
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
// src/seo/jsonld.ts
|
|
1891
|
+
function buildArticleJsonLd(props) {
|
|
1892
|
+
return {
|
|
1893
|
+
"@context": "https://schema.org",
|
|
1894
|
+
"@type": props.articleType ?? "Article",
|
|
1895
|
+
headline: props.title,
|
|
1896
|
+
description: props.description,
|
|
1897
|
+
url: props.url,
|
|
1898
|
+
...props.image ? { image: props.image } : {},
|
|
1899
|
+
...props.publishedTime ? { datePublished: props.publishedTime } : {},
|
|
1900
|
+
...props.category ? { articleSection: props.category } : {},
|
|
1901
|
+
...props.tags && props.tags.length > 0 ? { keywords: props.tags.join(", ") } : {},
|
|
1902
|
+
author: { "@type": "Person", name: props.authorName || props.siteName },
|
|
1903
|
+
publisher: { "@id": `${props.siteUrl}/#organization` },
|
|
1904
|
+
isPartOf: { "@id": `${props.siteUrl}/#website` },
|
|
1905
|
+
inLanguage: "en-US"
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1908
|
+
function buildCollectionJsonLd(props) {
|
|
1909
|
+
return {
|
|
1910
|
+
"@context": "https://schema.org",
|
|
1911
|
+
"@type": "CollectionPage",
|
|
1912
|
+
name: props.name,
|
|
1913
|
+
description: props.description,
|
|
1914
|
+
url: props.url,
|
|
1915
|
+
isPartOf: { "@id": `${props.siteUrl}/#website` },
|
|
1916
|
+
publisher: { "@id": `${props.siteUrl}/#organization` },
|
|
1917
|
+
inLanguage: "en-US"
|
|
1918
|
+
};
|
|
1919
|
+
}
|
|
1920
|
+
function AsteroidArticlePage(props) {
|
|
1921
|
+
const {
|
|
1922
|
+
slug,
|
|
1923
|
+
useArticle,
|
|
1924
|
+
seo,
|
|
1925
|
+
articleType,
|
|
1926
|
+
backLink,
|
|
1927
|
+
renderRoot,
|
|
1928
|
+
renderSkeleton,
|
|
1929
|
+
renderError,
|
|
1930
|
+
renderHeader,
|
|
1931
|
+
renderMeta,
|
|
1932
|
+
renderDescription,
|
|
1933
|
+
renderFeaturedImage,
|
|
1934
|
+
renderToc,
|
|
1935
|
+
renderContent,
|
|
1936
|
+
renderPreArticle,
|
|
1937
|
+
renderMidArticle,
|
|
1938
|
+
renderPostArticle,
|
|
1939
|
+
renderTags,
|
|
1940
|
+
renderAuthorDetails,
|
|
1941
|
+
renderRelatedPosts,
|
|
1942
|
+
renderCTA,
|
|
1943
|
+
renderJsonLd,
|
|
1944
|
+
noindex,
|
|
1945
|
+
children
|
|
1946
|
+
} = props;
|
|
1947
|
+
const { data: article, loading, error } = useArticle(slug);
|
|
1948
|
+
const cmsConfig = useContext(AsteroidCMSContext);
|
|
1949
|
+
const seoConfig = seo && !seo.cmsUrl && cmsConfig?.cmsUrl ? { ...seo, cmsUrl: cmsConfig.cmsUrl } : seo;
|
|
1950
|
+
if (children) {
|
|
1951
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: children({ data: article, loading, error }) });
|
|
1952
|
+
}
|
|
1953
|
+
if (loading) {
|
|
1954
|
+
const body2 = renderSkeleton?.() ?? null;
|
|
1955
|
+
return renderRoot ? renderRoot({ children: body2 }) : /* @__PURE__ */ jsx(Fragment$1, { children: body2 });
|
|
1956
|
+
}
|
|
1957
|
+
if (!article || error) {
|
|
1958
|
+
const body2 = renderError?.({ error, reason: error ? "error" : "not-found" }) ?? null;
|
|
1959
|
+
return renderRoot ? renderRoot({ children: body2 }) : /* @__PURE__ */ jsx(Fragment$1, { children: body2 });
|
|
1960
|
+
}
|
|
1961
|
+
const seoValues = seoConfig && article ? buildArticleSeoValues(article, seoConfig, slug, { noindex }) : null;
|
|
1962
|
+
const body = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1963
|
+
seoValues ? /* @__PURE__ */ jsx(Seo, { ...seoValuesToClientProps(seoValues) }) : null,
|
|
1964
|
+
seoConfig && article ? renderJsonLd?.({ post: article }) ?? /* @__PURE__ */ jsx(
|
|
1965
|
+
JsonLd,
|
|
1966
|
+
{
|
|
1967
|
+
data: buildArticleJsonLd({
|
|
1968
|
+
title: article.title,
|
|
1969
|
+
description: article.description || seoConfig.defaultDescription || "",
|
|
1970
|
+
url: `${(seoConfig.baseUrl || "").replace(/\/$/, "")}${seoConfig.articlePath ?? "/blog"}/${slug}`,
|
|
1971
|
+
siteName: seoConfig.siteName,
|
|
1972
|
+
siteUrl: (seoConfig.baseUrl || "").replace(/\/$/, ""),
|
|
1973
|
+
articleType,
|
|
1974
|
+
image: seoValues?.image,
|
|
1975
|
+
authorName: article.author?.name,
|
|
1976
|
+
publishedTime: article.published_date || void 0,
|
|
1977
|
+
tags: article.tags?.split(",").map((t) => t.trim()).filter(Boolean),
|
|
1978
|
+
category: article.category?.name
|
|
1979
|
+
})
|
|
1980
|
+
}
|
|
1981
|
+
) : null,
|
|
1982
|
+
backLink,
|
|
1983
|
+
renderPreArticle?.({ post: article }),
|
|
1984
|
+
renderHeader?.({ post: article }),
|
|
1985
|
+
renderMeta?.({ post: article }),
|
|
1986
|
+
renderDescription?.({ post: article }),
|
|
1987
|
+
renderFeaturedImage?.({ post: article }),
|
|
1988
|
+
renderToc?.({ post: article }),
|
|
1989
|
+
renderContent?.({ post: article }),
|
|
1990
|
+
renderMidArticle?.({ post: article }),
|
|
1991
|
+
renderTags?.({ post: article }),
|
|
1992
|
+
renderAuthorDetails?.({ post: article }),
|
|
1993
|
+
renderRelatedPosts?.({ post: article }),
|
|
1994
|
+
renderCTA?.({ post: article }),
|
|
1995
|
+
renderPostArticle?.({ post: article })
|
|
1996
|
+
] });
|
|
1997
|
+
return renderRoot ? renderRoot({ children: body }) : /* @__PURE__ */ jsx(Fragment$1, { children: body });
|
|
1998
|
+
}
|
|
1999
|
+
function useDebouncedValue(value, delay) {
|
|
2000
|
+
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
2001
|
+
useEffect(() => {
|
|
2002
|
+
const timer = setTimeout(() => setDebouncedValue(value), delay);
|
|
2003
|
+
return () => clearTimeout(timer);
|
|
2004
|
+
}, [value, delay]);
|
|
2005
|
+
return debouncedValue;
|
|
2006
|
+
}
|
|
2007
|
+
function defaultGetCategoryName(post) {
|
|
2008
|
+
return post.category?.name?.trim() || void 0;
|
|
2009
|
+
}
|
|
2010
|
+
function defaultGroupPostsByCategory(posts) {
|
|
2011
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2012
|
+
for (const post of posts) {
|
|
2013
|
+
const categoryName = defaultGetCategoryName(post) || "Other";
|
|
2014
|
+
const categorySlug = post.category?.slug || "other";
|
|
2015
|
+
const existing = groups.get(categorySlug);
|
|
2016
|
+
if (existing) {
|
|
2017
|
+
existing.posts.push(post);
|
|
2018
|
+
} else {
|
|
2019
|
+
groups.set(categorySlug, { categoryName, categorySlug, posts: [post] });
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
return Array.from(groups.values());
|
|
2023
|
+
}
|
|
2024
|
+
function applyPostFilters(posts, {
|
|
2025
|
+
categorySlug,
|
|
2026
|
+
articleSlug
|
|
2027
|
+
}) {
|
|
2028
|
+
let filtered = posts;
|
|
2029
|
+
if (categorySlug) {
|
|
2030
|
+
filtered = filtered.filter((post) => post.category?.slug === categorySlug);
|
|
2031
|
+
}
|
|
2032
|
+
if (articleSlug) {
|
|
2033
|
+
filtered = filtered.filter((post) => post.slug === articleSlug);
|
|
2034
|
+
}
|
|
2035
|
+
return filtered;
|
|
2036
|
+
}
|
|
2037
|
+
function splitFeaturedAndRest(posts) {
|
|
2038
|
+
const featured = posts.find((post) => post.featured) ?? null;
|
|
2039
|
+
const rest = featured ? posts.filter((post) => post.slug !== featured.slug) : [...posts];
|
|
2040
|
+
return { featured, rest };
|
|
2041
|
+
}
|
|
2042
|
+
function useAsteroidArticlesState({
|
|
2043
|
+
usePosts,
|
|
2044
|
+
categorySlug,
|
|
2045
|
+
articleSlug,
|
|
2046
|
+
searchDebounceMs = 800,
|
|
2047
|
+
groupPostsByCategory = defaultGroupPostsByCategory
|
|
2048
|
+
}) {
|
|
2049
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
2050
|
+
const debouncedSearchQuery = useDebouncedValue(searchQuery, searchDebounceMs);
|
|
2051
|
+
const { posts: rawPosts, loading, error } = usePosts(debouncedSearchQuery);
|
|
2052
|
+
const posts = useMemo(
|
|
2053
|
+
() => applyPostFilters(rawPosts, { categorySlug, articleSlug }),
|
|
2054
|
+
[rawPosts, categorySlug, articleSlug]
|
|
2055
|
+
);
|
|
2056
|
+
const { featured, rest } = useMemo(
|
|
2057
|
+
() => splitFeaturedAndRest(posts),
|
|
2058
|
+
[posts]
|
|
2059
|
+
);
|
|
2060
|
+
const isSearching = debouncedSearchQuery.trim().length > 0;
|
|
2061
|
+
const categoryGroups = useMemo(() => {
|
|
2062
|
+
if (isSearching) {
|
|
2063
|
+
if (posts.length === 0) return [];
|
|
2064
|
+
return [
|
|
2065
|
+
{
|
|
2066
|
+
categoryName: `Search results for "${debouncedSearchQuery.trim()}"`,
|
|
2067
|
+
categorySlug: "search-results",
|
|
2068
|
+
posts
|
|
2069
|
+
}
|
|
2070
|
+
];
|
|
2071
|
+
}
|
|
2072
|
+
return groupPostsByCategory(rest);
|
|
2073
|
+
}, [isSearching, posts, debouncedSearchQuery, rest, groupPostsByCategory]);
|
|
2074
|
+
const hasError = Boolean(error);
|
|
2075
|
+
const isEmpty = !featured && rest.length === 0;
|
|
2076
|
+
return {
|
|
2077
|
+
posts,
|
|
2078
|
+
featured,
|
|
2079
|
+
rest,
|
|
2080
|
+
categoryGroups,
|
|
2081
|
+
loading,
|
|
2082
|
+
error,
|
|
2083
|
+
hasError,
|
|
2084
|
+
isEmpty,
|
|
2085
|
+
isSearching,
|
|
2086
|
+
searchQuery,
|
|
2087
|
+
debouncedSearchQuery,
|
|
2088
|
+
setSearchQuery
|
|
2089
|
+
};
|
|
2090
|
+
}
|
|
2091
|
+
function AsteroidArticlesListing(props) {
|
|
2092
|
+
const {
|
|
2093
|
+
eyebrow,
|
|
2094
|
+
title,
|
|
2095
|
+
description,
|
|
2096
|
+
seo,
|
|
2097
|
+
categorySlug,
|
|
2098
|
+
renderRoot,
|
|
2099
|
+
renderHeader,
|
|
2100
|
+
renderSearch,
|
|
2101
|
+
renderFeaturedCard,
|
|
2102
|
+
renderPostCard,
|
|
2103
|
+
renderCategoryHeading,
|
|
2104
|
+
renderPostGrid,
|
|
2105
|
+
renderCategoryGroup,
|
|
2106
|
+
renderSkeleton,
|
|
2107
|
+
renderEmpty,
|
|
2108
|
+
renderContent,
|
|
2109
|
+
renderJsonLd,
|
|
2110
|
+
noindex,
|
|
2111
|
+
children
|
|
2112
|
+
} = props;
|
|
2113
|
+
const state = useAsteroidArticlesState(props);
|
|
2114
|
+
const categoryName = categorySlug ? state.posts[0]?.category?.name?.trim() : void 0;
|
|
2115
|
+
if (children) {
|
|
2116
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: children(state) });
|
|
2117
|
+
}
|
|
2118
|
+
const seoNode = seo ? /* @__PURE__ */ jsx(
|
|
2119
|
+
Seo,
|
|
2120
|
+
{
|
|
2121
|
+
...seoValuesToClientProps(
|
|
2122
|
+
buildArticleListingSeoValues(seo, {
|
|
2123
|
+
categoryName,
|
|
2124
|
+
categorySlug,
|
|
2125
|
+
noindex
|
|
2126
|
+
})
|
|
2127
|
+
)
|
|
2128
|
+
}
|
|
2129
|
+
) : null;
|
|
2130
|
+
const jsonLdNode = seo ? renderJsonLd?.(state) ?? /* @__PURE__ */ jsx(
|
|
2131
|
+
JsonLd,
|
|
2132
|
+
{
|
|
2133
|
+
data: buildCollectionJsonLd({
|
|
2134
|
+
name: categoryName || `${seo.siteName} ${seo.contentLabel ?? "Articles"}`,
|
|
2135
|
+
description: seo.defaultDescription || "",
|
|
2136
|
+
url: `${(seo.baseUrl || "").replace(/\/$/, "")}${seo.articlePath ?? "/blog"}${categorySlug ? `/category/${categorySlug}` : ""}`,
|
|
2137
|
+
siteUrl: (seo.baseUrl || "").replace(/\/$/, "")
|
|
2138
|
+
})
|
|
2139
|
+
}
|
|
2140
|
+
) : null;
|
|
2141
|
+
const handleSearchSubmit = (event) => {
|
|
2142
|
+
event.preventDefault();
|
|
2143
|
+
};
|
|
2144
|
+
const searchNode = renderSearch ? renderSearch({
|
|
2145
|
+
value: state.searchQuery,
|
|
2146
|
+
onChange: state.setSearchQuery,
|
|
2147
|
+
onSubmit: handleSearchSubmit
|
|
2148
|
+
}) : null;
|
|
2149
|
+
const headerNode = renderHeader ? renderHeader({ eyebrow, title, description, search: searchNode }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2150
|
+
eyebrow,
|
|
2151
|
+
title,
|
|
2152
|
+
description,
|
|
2153
|
+
searchNode
|
|
2154
|
+
] });
|
|
2155
|
+
const featuredNode = state.featured && !state.isSearching && renderFeaturedCard ? renderFeaturedCard({ post: state.featured }) : null;
|
|
2156
|
+
const noSearchResultsNode = state.isSearching && !state.loading && state.posts.length === 0 ? renderEmpty?.({
|
|
2157
|
+
reason: "no-results",
|
|
2158
|
+
searchQuery: state.debouncedSearchQuery.trim()
|
|
2159
|
+
}) ?? null : null;
|
|
2160
|
+
const groupsNode = noSearchResultsNode ? null : state.categoryGroups.map((group) => {
|
|
2161
|
+
const postCards = group.posts.map((post, index) => /* @__PURE__ */ jsx(Fragment, { children: renderPostCard({ post, index, group }) }, post.slug ?? index));
|
|
2162
|
+
const gridNode = renderPostGrid ? renderPostGrid({ posts: group.posts, group, children: postCards }) : /* @__PURE__ */ jsx(Fragment$1, { children: postCards });
|
|
2163
|
+
const headingNode = renderCategoryHeading ? renderCategoryHeading({ group }) : group.categoryName;
|
|
2164
|
+
const defaultContent = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2165
|
+
headingNode,
|
|
2166
|
+
gridNode
|
|
2167
|
+
] });
|
|
2168
|
+
return renderCategoryGroup ? renderCategoryGroup({ group, defaultContent }) : defaultContent;
|
|
2169
|
+
});
|
|
2170
|
+
const contentNode = !state.loading && !state.hasError && (state.featured || state.rest.length > 0) ? renderContent ? renderContent({
|
|
2171
|
+
featured: featuredNode,
|
|
2172
|
+
groups: groupsNode,
|
|
2173
|
+
noSearchResults: noSearchResultsNode
|
|
2174
|
+
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2175
|
+
featuredNode,
|
|
2176
|
+
noSearchResultsNode,
|
|
2177
|
+
groupsNode
|
|
2178
|
+
] }) : null;
|
|
2179
|
+
const body = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2180
|
+
seoNode,
|
|
2181
|
+
jsonLdNode,
|
|
2182
|
+
headerNode,
|
|
2183
|
+
state.loading ? renderSkeleton?.() : null,
|
|
2184
|
+
!state.loading && (state.hasError || state.isEmpty) ? renderEmpty?.({
|
|
2185
|
+
reason: state.hasError ? "error" : "no-posts",
|
|
2186
|
+
searchQuery: state.debouncedSearchQuery.trim(),
|
|
2187
|
+
error: state.error
|
|
2188
|
+
}) : null,
|
|
2189
|
+
contentNode
|
|
2190
|
+
] });
|
|
2191
|
+
return renderRoot ? renderRoot({ children: body }) : /* @__PURE__ */ jsx(Fragment$1, { children: body });
|
|
2192
|
+
}
|
|
1723
2193
|
|
|
1724
|
-
export { AsteroidCMSProvider, RichTextContent, extractHeadingsFromElement, extractHeadingsFromHtml, slugify, useAsteroidCMSConfig, useCmsContent, useCmsImage, useCmsMutate };
|
|
2194
|
+
export { AsteroidArticlePage, AsteroidArticlesListing, AsteroidCMSProvider, JsonLd, RichTextContent, Seo, defaultGetCategoryName, defaultGroupPostsByCategory, extractHeadingsFromElement, extractHeadingsFromHtml, slugify, useAsteroidArticlesState, useAsteroidCMSConfig, useCmsContent, useCmsImage, useCmsMutate };
|
|
1725
2195
|
//# sourceMappingURL=client.js.map
|
|
1726
2196
|
//# sourceMappingURL=client.js.map
|