@fullstackdatasolutions/articles 0.5.1 → 0.7.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/dist/server.cjs CHANGED
@@ -22,6 +22,18 @@ var __spreadValues = (a, b) => {
22
22
  return a;
23
23
  };
24
24
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
25
+ var __objRest = (source, exclude) => {
26
+ var target = {};
27
+ for (var prop in source)
28
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
29
+ target[prop] = source[prop];
30
+ if (source != null && __getOwnPropSymbols)
31
+ for (var prop of __getOwnPropSymbols(source)) {
32
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
33
+ target[prop] = source[prop];
34
+ }
35
+ return target;
36
+ };
25
37
  var __export = (target, all) => {
26
38
  for (var name in all)
27
39
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -97,7 +109,6 @@ var import_node_path = __toESM(require("path"), 1);
97
109
  var import_reading_time = __toESM(require("reading-time"), 1);
98
110
 
99
111
  // src/markdown.ts
100
- var import_rehype_autolink_headings = __toESM(require("rehype-autolink-headings"), 1);
101
112
  var import_rehype_prism_plus = __toESM(require("rehype-prism-plus"), 1);
102
113
  var import_rehype_sanitize = __toESM(require("rehype-sanitize"), 1);
103
114
  var import_rehype_slug = __toESM(require("rehype-slug"), 1);
@@ -238,11 +249,15 @@ var customRenderer = () => {
238
249
  case "p":
239
250
  props.className = "text-muted-foreground leading-relaxed mb-4";
240
251
  break;
241
- case "a":
252
+ case "a": {
242
253
  props.className = "text-primary hover:underline transition-colors duration-200";
243
- props.target = "_blank";
244
- props.rel = "noopener noreferrer";
254
+ const href = typeof props.href === "string" ? props.href : "";
255
+ if (!href.startsWith("#")) {
256
+ props.target = "_blank";
257
+ props.rel = "noopener noreferrer";
258
+ }
245
259
  break;
260
+ }
246
261
  case "ul":
247
262
  props.className = "list-disc list-inside mb-4 space-y-2 ml-4";
248
263
  break;
@@ -309,7 +324,7 @@ var rehypeProcessImages = (options = {}) => {
309
324
  function markdownToHtml(markdown, articleSlug) {
310
325
  return __async(this, null, function* () {
311
326
  try {
312
- let processor = (0, import_remark.remark)().use(import_remark_parse.default).use(import_remark_gfm.default).use(import_remark_github_blockquote_alert.default).use(import_remark_rehype.default).use(customRenderer).use(import_rehype_slug.default).use(import_rehype_autolink_headings.default, { behavior: "wrap" }).use(import_rehype_prism_plus.default).use(import_rehype_sanitize.default, {
327
+ let processor = (0, import_remark.remark)().use(import_remark_parse.default).use(import_remark_gfm.default).use(import_remark_github_blockquote_alert.default).use(import_remark_rehype.default).use(customRenderer).use(import_rehype_slug.default).use(import_rehype_prism_plus.default).use(import_rehype_sanitize.default, {
313
328
  attributes: {
314
329
  "*": ["className", "class", "id"],
315
330
  a: ["href", "target", "rel", "id"],
@@ -408,19 +423,29 @@ function findArticleFile(slug) {
408
423
  function getAvailableArticleSlugs() {
409
424
  try {
410
425
  if (!import_node_fs.default.existsSync(articlesDirectory)) return [];
411
- const items = import_node_fs.default.readdirSync(articlesDirectory, { withFileTypes: true });
412
- return items.filter((item) => item.isDirectory()).filter((dir) => {
413
- try {
414
- return findArticleFile(dir.name) !== null;
415
- } catch (e) {
416
- return false;
417
- }
418
- }).map((dir) => dir.name);
426
+ return walkArticleDir(articlesDirectory, "");
419
427
  } catch (error) {
420
428
  console.error("Error reading articles directory:", error);
421
429
  return [];
422
430
  }
423
431
  }
432
+ function walkArticleDir(dir, baseSlug) {
433
+ const slugs = [];
434
+ try {
435
+ const items = import_node_fs.default.readdirSync(dir, { withFileTypes: true });
436
+ for (const item of items) {
437
+ if (!item.isDirectory()) continue;
438
+ const slug = baseSlug ? `${baseSlug}/${item.name}` : item.name;
439
+ if (findArticleFile(slug) !== null) {
440
+ slugs.push(slug);
441
+ } else {
442
+ slugs.push(...walkArticleDir(import_node_path.default.join(dir, item.name), slug));
443
+ }
444
+ }
445
+ } catch (e) {
446
+ }
447
+ return slugs;
448
+ }
424
449
  function parseDateField(rawDate) {
425
450
  if (!rawDate) return void 0;
426
451
  try {
@@ -775,28 +800,66 @@ function getArticleSitemapEntries(baseUrlOrConfig) {
775
800
  });
776
801
  }
777
802
 
778
- // src/ArticleContent.tsx
779
- var import_rsc = require("next-mdx-remote/rsc");
803
+ // src/renderMdx.tsx
804
+ var import_react2 = __toESM(require("react"), 1);
805
+ var devRuntime = __toESM(require("react/jsx-dev-runtime"), 1);
806
+ var runtime = __toESM(require("react/jsx-runtime"), 1);
807
+ var import_mdx = require("@mdx-js/mdx");
808
+ var import_rehype_prism_plus2 = __toESM(require("rehype-prism-plus"), 1);
809
+ var import_rehype_slug2 = __toESM(require("rehype-slug"), 1);
810
+ var import_remark_gfm2 = __toESM(require("remark-gfm"), 1);
811
+ var import_remark_github_blockquote_alert2 = __toESM(require("remark-github-blockquote-alert"), 1);
780
812
  var import_jsx_runtime = require("react/jsx-runtime");
781
- function ArticleContent({ article, className }) {
782
- if (article.contentType === "mdx" && article.mdxSource) {
783
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_rsc.MDXRemote, { source: article.mdxSource }) });
784
- }
785
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, dangerouslySetInnerHTML: { __html: article.htmlContent || "" } });
813
+ function makeImgComponent(basePath) {
814
+ return function MdxImage(_a) {
815
+ var _b = _a, { src, alt } = _b, props = __objRest(_b, ["src", "alt"]);
816
+ const resolvedSrc = src && !src.startsWith("http") && !src.startsWith("/") ? `${basePath}/${src}` : src;
817
+ return import_react2.default.createElement("img", __spreadValues({ src: resolvedSrc, alt }, props));
818
+ };
819
+ }
820
+ function renderMdxSource(source, basePath) {
821
+ return __async(this, null, function* () {
822
+ const isDevelopment = process.env.NODE_ENV === "development";
823
+ const mdxModule = yield (0, import_mdx.evaluate)(source, __spreadProps(__spreadValues({}, isDevelopment ? devRuntime : runtime), {
824
+ development: isDevelopment,
825
+ remarkPlugins: [import_remark_gfm2.default, import_remark_github_blockquote_alert2.default],
826
+ rehypePlugins: [
827
+ customRenderer,
828
+ import_rehype_slug2.default,
829
+ // @ts-ignore
830
+ import_rehype_prism_plus2.default
831
+ ]
832
+ }));
833
+ const Content = mdxModule.default;
834
+ const components = basePath ? { img: makeImgComponent(basePath) } : void 0;
835
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Content, { components });
836
+ });
786
837
  }
787
838
 
788
- // src/ArticleTOC.tsx
839
+ // src/ArticleContent.tsx
789
840
  var import_jsx_runtime2 = require("react/jsx-runtime");
841
+ function ArticleContent(_0) {
842
+ return __async(this, arguments, function* ({ article, className }) {
843
+ if (article.contentType === "mdx" && article.mdxSource) {
844
+ const content = yield renderMdxSource(article.mdxSource, `/articles/${article.slug}`);
845
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className, children: content });
846
+ }
847
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className, dangerouslySetInnerHTML: { __html: article.htmlContent || "" } });
848
+ });
849
+ }
850
+
851
+ // src/ArticleTOC.tsx
852
+ var import_jsx_runtime3 = require("react/jsx-runtime");
790
853
  function ArticleTOC({ toc, className }) {
791
854
  if (!toc.length) return null;
792
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
855
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
793
856
  "nav",
794
857
  {
795
858
  "aria-label": "Table of contents",
796
859
  className: `mb-8 rounded-lg border border-border bg-muted/40 px-6 py-4 ${className != null ? className : ""}`,
797
860
  children: [
798
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "On this page" }),
799
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: "space-y-1 text-sm", children: toc.map((item) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { style: { paddingLeft: `${Math.max(0, item.depth - 2) * 1}rem` }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
861
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "On this page" }),
862
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "space-y-1 text-sm", children: toc.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { style: { paddingLeft: `${Math.max(0, item.depth - 2) * 1}rem` }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
800
863
  "a",
801
864
  {
802
865
  href: `#${item.id}`,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server.ts","../src/server-articles.ts","../src/markdown.ts","../src/seoUtils.ts","../src/ArticleContent.tsx","../src/ArticleTOC.tsx"],"sourcesContent":["// Server-only exports — uses fs/path; never import this in a client bundle\nexport {\n getAllArticles,\n getArticleMetadata,\n getAvailableArticleSlugs,\n getAdjacentArticles,\n searchArticles,\n getAllCategories,\n getArticlesByCategory,\n categoryToSlug,\n sanitizeImagePath,\n} from './server-articles'\n\nexport {\n generateArticleStaticParams,\n generateCategoryStaticParams,\n generateArticlesIndexMetadata,\n generateArticleMetadata,\n generateCategoryMetadata,\n getArticleSitemapEntries,\n} from './seoUtils'\n\nexport { markdownToHtml, extractToc } from './markdown'\nexport { ArticleContent } from './ArticleContent'\nexport { ArticleTOC } from './ArticleTOC'\n\nexport type { Article, CategoryInfo, TocItem } from './articleTypes'\nexport type { ArticlesConfig } from './articlesConfig'\n","import { cache } from 'react'\nimport matter from 'gray-matter'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport readingTime from 'reading-time'\nimport { markdownToHtml, extractToc } from './markdown'\nimport type { Article, CategoryInfo, FaqItem, HowToStep } from './articleTypes'\n\nconst articlesDirectory = path.join(process.cwd(), 'public/articles')\n\nfunction getReadingTime(content: string): string {\n return readingTime(content).text\n}\n\nfunction findArticleImage(slug: string): string | null {\n try {\n const articleDir = path.join(articlesDirectory, slug)\n const entries: (fs.Dirent | string)[] = fs.readdirSync(articleDir, { withFileTypes: true })\n const names = entries\n .map((entry) => {\n if (typeof entry === 'string') return entry\n if (entry && typeof entry.name === 'string') return entry.name\n return null\n })\n .filter((name): name is string => Boolean(name))\n const imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.webp']\n const imageFileName = names.find((name) =>\n imageExtensions.some((ext) => name.toLowerCase().endsWith(ext))\n )\n return imageFileName ? sanitizeImagePath(imageFileName, slug) : null\n } catch {\n return null\n }\n}\n\nfunction sanitizeImagePath(rawPath: string, articleSlug: string): string | null {\n if (!rawPath || typeof rawPath !== 'string') return null\n const cleanPath = rawPath.replaceAll(/[\\x00-\\x1f\\x7f-\\x9f]/g, '')\n if (cleanPath.startsWith('http://') || cleanPath.startsWith('https://')) return cleanPath\n if (cleanPath.includes('..') || cleanPath.includes('\\\\') || cleanPath.startsWith('/')) {\n console.warn(`Potentially unsafe image path detected: ${cleanPath}`)\n return null\n }\n if (!/^[a-zA-Z0-9._/-]+$/.test(cleanPath)) {\n console.warn(`Invalid characters in image path: ${cleanPath}`)\n return null\n }\n if (cleanPath.includes('/')) {\n const normalizedPath = path.normalize(cleanPath)\n if (normalizedPath.startsWith('..') || normalizedPath.includes('../')) {\n console.warn(`Path traversal attempt detected: ${cleanPath}`)\n return null\n }\n return `/articles/${articleSlug}/${cleanPath}`\n }\n return `/articles/${articleSlug}/${cleanPath}`\n}\n\nfunction findArticleFile(slug: string): { filePath: string; contentType: 'md' | 'mdx' } | null {\n const mdPath = path.join(articlesDirectory, slug, 'article.md')\n const mdxPath = path.join(articlesDirectory, slug, 'article.mdx')\n if (fs.existsSync(mdPath)) return { filePath: mdPath, contentType: 'md' }\n if (fs.existsSync(mdxPath)) return { filePath: mdxPath, contentType: 'mdx' }\n return null\n}\n\nexport function getAvailableArticleSlugs(): string[] {\n try {\n if (!fs.existsSync(articlesDirectory)) return []\n const items = fs.readdirSync(articlesDirectory, { withFileTypes: true })\n return items\n .filter((item) => item.isDirectory())\n .filter((dir) => {\n try {\n return findArticleFile(dir.name) !== null\n } catch {\n return false\n }\n })\n .map((dir) => dir.name)\n } catch (error) {\n console.error('Error reading articles directory:', error)\n return []\n }\n}\n\nfunction parseDateField(rawDate: unknown): string | undefined {\n if (!rawDate) return undefined\n try {\n const parsed = new Date(rawDate as string)\n if (!Number.isNaN(parsed.getTime())) return parsed.toISOString().split('T')[0]\n } catch {\n // ignore invalid dates\n }\n return undefined\n}\n\nfunction resolveFeaturedImage(rawImage: unknown, slug: string): string {\n const img = typeof rawImage === 'string' ? rawImage : ''\n if (img && !img.startsWith('http')) return sanitizeImagePath(img, slug) || '/placeholder-logo.png'\n if (!img) return findArticleImage(slug) || '/placeholder-logo.png'\n return img\n}\n\nfunction parseFaqItems(raw: unknown): FaqItem[] | undefined {\n if (!Array.isArray(raw)) return undefined\n const items = raw.filter(\n (item): item is FaqItem =>\n typeof item === 'object' &&\n item !== null &&\n typeof (item as FaqItem).question === 'string' &&\n typeof (item as FaqItem).answer === 'string'\n )\n return items.length ? items : undefined\n}\n\nfunction parseHowToSteps(raw: unknown): HowToStep[] | undefined {\n if (!Array.isArray(raw)) return undefined\n const steps = raw.filter(\n (item): item is HowToStep =>\n typeof item === 'object' &&\n item !== null &&\n typeof (item as HowToStep).name === 'string' &&\n typeof (item as HowToStep).text === 'string'\n )\n return steps.length ? steps : undefined\n}\n\nasync function getArticleSummary(slug: string): Promise<Article | null> {\n try {\n const found = findArticleFile(slug)\n if (!found) return null\n const fileContent = fs.readFileSync(found.filePath, 'utf8')\n const { data, content: markdownContent } = matter(fileContent)\n const readTime = getReadingTime(markdownContent)\n const allTags: string[] = Array.isArray(data.tags)\n ? data.tags.filter((t: unknown) => typeof t === 'string' && String(t).trim())\n : []\n const categories: string[] =\n allTags.length > 0 ? allTags.map((t: string) => t.replaceAll('-', ' ').trim()) : ['Campaigns']\n return {\n slug,\n title: data.title || slug.replaceAll('-', ' '),\n excerpt: data.excerpt || '',\n date: parseDateField(data.date),\n lastmod: parseDateField(data.lastmod),\n author: data.author || 'Andrew Blase',\n category: categories[0],\n categories,\n readTime,\n featuredImage: resolveFeaturedImage(data.featuredImage, slug),\n tags: data.tags || [],\n contentType: found.contentType,\n draft: data.draft === true,\n faq: parseFaqItems(data.faq),\n howTo: parseHowToSteps(data.howTo),\n canonicalUrl: typeof data.canonicalUrl === 'string' ? data.canonicalUrl : undefined,\n articleType: typeof data.articleType === 'string' ? data.articleType : undefined,\n series: typeof data.series === 'string' ? data.series : undefined,\n }\n } catch (error) {\n console.error(`Error loading article ${slug}:`, error)\n return null\n }\n}\n\nexport const getArticleMetadata = cache(async (slug: string): Promise<Article | null> => {\n try {\n const summary = await getArticleSummary(slug)\n if (!summary) return null\n const found = findArticleFile(slug)\n if (!found) return null\n const fileContent = fs.readFileSync(found.filePath, 'utf8')\n const { content: markdownContent } = matter(fileContent)\n const toc = await extractToc(markdownContent)\n let htmlContent: string | undefined\n let mdxSource: string | undefined\n if (found.contentType === 'mdx') {\n mdxSource = markdownContent\n } else {\n htmlContent = await markdownToHtml(markdownContent, slug)\n }\n return { ...summary, content: markdownContent, htmlContent, mdxSource, toc }\n } catch (error) {\n console.error(`Error loading article ${slug}:`, error)\n return null\n }\n})\n\nexport const getAllArticles = cache(async (): Promise<Article[]> => {\n const slugs = getAvailableArticleSlugs()\n const articles = await Promise.all(slugs.map((slug) => getArticleSummary(slug)))\n const currentDate = new Date().toISOString().split('T')[0]\n return articles\n .filter((article): article is Article => article !== null)\n .filter((article) => !article.date || article.date <= currentDate)\n .filter((article) => !(article.draft && process.env.NODE_ENV === 'production'))\n .sort((a, b) => {\n if (!a.date && !b.date) return 0\n if (!a.date) return 1\n if (!b.date) return -1\n return new Date(b.date).getTime() - new Date(a.date).getTime()\n })\n})\n\nexport async function getAdjacentArticles(\n currentSlug: string\n): Promise<{ previous: Article | null; next: Article | null }> {\n const allArticles = await getAllArticles()\n const currentIndex = allArticles.findIndex((article) => article.slug === currentSlug)\n if (currentIndex === -1) return { previous: null, next: null }\n const previous = currentIndex < allArticles.length - 1 ? allArticles[currentIndex + 1] : null\n const next = currentIndex > 0 ? allArticles[currentIndex - 1] : null\n return { previous, next }\n}\n\nexport async function searchArticles(query: string): Promise<Article[]> {\n if (!query?.trim()) return getAllArticles()\n const articles = await getAllArticles()\n const searchTerm = query.toLowerCase().trim()\n return articles.filter((article) => {\n const matchesTitle = article.title.toLowerCase().includes(searchTerm)\n const matchesExcerpt = article.excerpt.toLowerCase().includes(searchTerm)\n const matchesAuthor = article.author.toLowerCase().includes(searchTerm)\n const matchesCategory = article.categories.some((cat) => cat.toLowerCase().includes(searchTerm))\n const matchesTags = article.tags?.some((tag) => tag.toLowerCase().includes(searchTerm))\n return (\n matchesTitle || matchesExcerpt || matchesAuthor || matchesCategory || Boolean(matchesTags)\n )\n })\n}\n\nexport function categoryToSlug(category: string): string {\n return category\n .toLowerCase()\n .replaceAll(/\\s+/g, '-')\n .replaceAll(/[^a-z0-9-]/g, '')\n}\n\nexport async function getAllCategories(): Promise<CategoryInfo[]> {\n const articles = await getAllArticles()\n const categoryMap = new Map<string, { count: number; featuredImage: string }>()\n for (const article of articles) {\n for (const cat of article.categories) {\n if (!categoryMap.has(cat)) {\n categoryMap.set(cat, { count: 0, featuredImage: article.featuredImage })\n }\n categoryMap.get(cat)!.count++\n }\n }\n return Array.from(categoryMap.entries())\n .map(([name, { count, featuredImage }]) => ({\n name,\n slug: categoryToSlug(name),\n count,\n featuredImage,\n }))\n .sort((a, b) => b.count - a.count)\n}\n\nexport async function getArticlesByCategory(categorySlug: string): Promise<Article[]> {\n const articles = await getAllArticles()\n return articles.filter((article) =>\n article.categories.some((cat) => categoryToSlug(cat) === categorySlug)\n )\n}\n\nexport { sanitizeImagePath }\n","import type { Element, Root, ElementContent } from 'hast'\nimport rehypeAutolinkHeadings from 'rehype-autolink-headings'\nimport rehypePrism from 'rehype-prism-plus'\nimport rehypeSanitize from 'rehype-sanitize'\nimport rehypeSlug from 'rehype-slug'\nimport rehypeStringify from 'rehype-stringify'\nimport { remark } from 'remark'\nimport remarkGfm from 'remark-gfm'\nimport remarkGithubBlockquoteAlert from 'remark-github-blockquote-alert'\nimport remarkParse from 'remark-parse'\nimport remarkRehype from 'remark-rehype'\nimport { Plugin } from 'unified'\nimport { visit } from 'unist-util-visit'\nimport type { TocItem } from './articleTypes'\n\n// Import the sanitizeImagePath function\nfunction sanitizeImagePath(rawPath: string, articleSlug: string): string | null {\n if (!rawPath || typeof rawPath !== 'string') {\n return null\n }\n\n // Remove any null bytes or control characters\n const cleanPath = rawPath.replaceAll(/[\\x00-\\x1f\\x7f-\\x9f]/g, '')\n\n // Check for absolute URLs (http/https)\n if (cleanPath.startsWith('http://') || cleanPath.startsWith('https://')) {\n // For external URLs, just return as-is (they're safe)\n return cleanPath\n }\n\n // For relative paths, ensure they don't contain dangerous patterns\n if (cleanPath.includes('..') || cleanPath.includes('\\\\') || cleanPath.startsWith('/')) {\n console.warn(`Potentially unsafe image path detected: ${cleanPath}`)\n return null\n }\n\n // Only allow alphanumeric characters, hyphens, underscores, dots, and forward slashes\n if (!/^[a-zA-Z0-9._/-]+$/.test(cleanPath)) {\n console.warn(`Invalid characters in image path: ${cleanPath}`)\n return null\n }\n\n // Construct safe path within articles directory\n if (cleanPath.includes('/')) {\n // Relative path, ensure it's within the article directory\n const normalizedPath = cleanPath.replaceAll('\\\\', '/')\n if (normalizedPath.startsWith('..') || normalizedPath.includes('../')) {\n console.warn(`Path traversal attempt detected: ${cleanPath}`)\n return null\n }\n return `/articles/${articleSlug}/${normalizedPath}`\n } else {\n // Just a filename, construct full path\n return `/articles/${articleSlug}/${cleanPath}`\n }\n}\n\nfunction styleFootnoteLinks(nodes: ElementContent[]): void {\n nodes.forEach((n) => {\n if (n.type !== 'element') return\n const el = n as Element\n if (el.tagName === 'a') {\n const isBackRef =\n el.properties?.['dataFootnoteBackref'] !== undefined ||\n (el.children[0]?.type === 'text' && el.children[0].value === '↩')\n if (isBackRef) {\n el.properties.className = 'text-primary hover:underline ml-1'\n if (typeof el.properties.href === 'string') {\n el.properties.href = el.properties.href.replaceAll('#user-content-fnref-', '#ref-')\n }\n } else {\n el.properties.className = 'text-primary hover:underline break-all'\n }\n }\n if (el.children) styleFootnoteLinks(el.children)\n })\n}\n\nfunction processFootnoteRef(node: Element): void {\n if (\n node.tagName !== 'sup' ||\n node.children?.[0]?.type !== 'element' ||\n (node.children[0] as Element).tagName !== 'a'\n )\n return\n\n const link = node.children[0] as Element\n const href = link.properties?.href\n if (typeof href !== 'string' || !href.startsWith('#user-content-fn-')) return\n\n delete link.properties.target\n delete link.properties.rel\n link.properties.className = 'text-primary hover:underline'\n link.properties.href = href.replaceAll('#user-content-fn-', '#footnote-')\n\n if (typeof node.properties?.id === 'string') {\n link.properties.id = node.properties.id.replaceAll('user-content-fnref-', 'ref-')\n delete node.properties.id\n }\n\n if (link.children?.[0]?.type === 'text') {\n link.children[0].value = `[${link.children[0].value}]`\n }\n}\n\nfunction processFootnotesSection(node: Element): void {\n const cls = node.properties?.className\n const isFootnotes =\n node.tagName === 'section' &&\n (Array.isArray(cls) ? cls.includes('footnotes') : cls === 'footnotes')\n if (!isFootnotes) return\n\n const olCandidate = node.children.find(\n (child) => child.type === 'element' && (child as Element).tagName === 'ol'\n )\n if (olCandidate?.type !== 'element') return\n\n const ol = olCandidate as Element\n ol.properties.className = 'list-decimal ml-6 space-y-2 text-sm text-muted-foreground'\n\n ol.children.forEach((li) => {\n if (li.type !== 'element' || li.tagName !== 'li') return\n const liEl = li as Element\n\n if (liEl.properties) {\n liEl.properties.className = 'pl-2'\n if (typeof liEl.properties.id === 'string') {\n liEl.properties.id = liEl.properties.id.replaceAll('user-content-fn-', 'footnote-')\n }\n }\n\n const pIndex = liEl.children.findIndex(\n (child) => child.type === 'element' && (child as Element).tagName === 'p'\n )\n if (pIndex !== -1) {\n const p = liEl.children[pIndex] as Element\n liEl.children.splice(pIndex, 1, ...p.children)\n }\n\n styleFootnoteLinks(liEl.children)\n })\n\n const hr: Element = {\n type: 'element',\n tagName: 'hr',\n properties: { className: 'my-8 border-border' },\n children: [],\n }\n const h3: Element = {\n type: 'element',\n tagName: 'h3',\n properties: { className: 'text-lg font-semibold mb-4' },\n children: [{ type: 'text', value: 'References' }],\n }\n\n node.children = [hr, h3, ol]\n if (node.properties) node.properties.className = undefined\n}\n\nconst customRenderer: Plugin<[], Root> = () => {\n return (tree: Root) => {\n // First pass: Apply general styles\n visit(tree, 'element', (node: Element) => {\n if (node.tagName) {\n const props = node.properties || {}\n\n switch (node.tagName) {\n case 'h1':\n props.className = 'text-3xl font-bold text-foreground mt-8 mb-4 scroll-mt-20'\n break\n case 'h2':\n props.className = 'text-2xl font-semibold text-foreground mt-6 mb-3 scroll-mt-20'\n break\n case 'h3':\n props.className = 'text-xl font-semibold text-foreground mt-4 mb-2 scroll-mt-20'\n break\n case 'h4':\n props.className = 'text-lg font-semibold text-foreground mt-3 mb-2 scroll-mt-20'\n break\n case 'p':\n props.className = 'text-muted-foreground leading-relaxed mb-4'\n break\n case 'a':\n props.className = 'text-primary hover:underline transition-colors duration-200'\n props.target = '_blank'\n props.rel = 'noopener noreferrer'\n break\n case 'ul':\n props.className = 'list-disc list-inside mb-4 space-y-2 ml-4'\n break\n case 'ol':\n props.className = 'list-decimal list-inside mb-4 space-y-2 ml-4'\n break\n case 'li':\n props.className = 'mb-1'\n break\n case 'blockquote':\n props.className = 'border-l-4 border-primary pl-4 italic my-4 text-muted-foreground'\n break\n case 'code':\n props.className =\n (props.className ? props.className + ' ' : '') +\n 'bg-muted px-1 py-0.5 rounded text-sm font-mono'\n break\n case 'pre':\n props.className = 'bg-muted rounded-lg p-4 overflow-x-auto my-4'\n break\n case 'img':\n props.className = 'rounded-lg my-6 w-full max-w-2xl mx-auto'\n break\n case 'table':\n props.className = 'border-collapse border border-border my-4 w-full'\n break\n case 'th':\n props.className = 'border border-border px-2 py-1 bg-muted font-semibold'\n break\n case 'td':\n props.className = 'border border-border px-2 py-1'\n break\n case 'hr':\n props.className = 'my-8 border-border'\n break\n default:\n break\n }\n\n node.properties = props\n }\n })\n\n // Second pass: Fix footnotes and references\n visit(tree, 'element', (node: Element) => {\n processFootnoteRef(node)\n processFootnotesSection(node)\n })\n }\n}\n\n// Custom rehype plugin to process image URLs\nconst rehypeProcessImages: Plugin<[{ articleSlug?: string }], Root> = (options = {}) => {\n return (tree: Root) => {\n visit(tree, 'element', (node: Element) => {\n if (node.tagName === 'img' && node.properties) {\n const src = node.properties.src\n if (src && typeof src === 'string' && options.articleSlug) {\n // Sanitize the image path\n const sanitizedSrc = sanitizeImagePath(src, options.articleSlug)\n if (sanitizedSrc) {\n node.properties.src = sanitizedSrc\n } else {\n // If sanitization fails, use a placeholder\n console.warn(`Using placeholder for unsafe image path: ${src}`)\n node.properties.src = '/placeholder-logo.png'\n }\n }\n }\n })\n }\n}\n\nexport async function markdownToHtml(markdown: string, articleSlug?: string) {\n try {\n // Start building the remark processor\n let processor = remark()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkGithubBlockquoteAlert)\n .use(remarkRehype)\n .use(customRenderer)\n .use(rehypeSlug)\n .use(rehypeAutolinkHeadings, { behavior: 'wrap' })\n // @ts-ignore\n .use(rehypePrism)\n .use(rehypeSanitize, {\n attributes: {\n '*': ['className', 'class', 'id'],\n a: ['href', 'target', 'rel', 'id'],\n img: ['src', 'alt'],\n },\n })\n\n // Add image processing plugin if articleSlug is provided\n if (articleSlug) {\n processor = processor.use(rehypeProcessImages, { articleSlug })\n }\n\n const result = await processor.use(rehypeStringify).process(markdown)\n\n return result.toString()\n } catch (error) {\n console.error('Markdown conversion error:', error)\n // Return the original markdown as fallback\n return markdown\n }\n}\n\nfunction nodeTextValue(c: ElementContent): string {\n return c.type === 'text' ? (c as { value: string }).value : ''\n}\n\nfunction extractHeadingItem(node: Element): TocItem | null {\n const match = /^h([1-6])$/.exec(node.tagName)\n if (!match) return null\n const id = typeof node.properties?.id === 'string' ? node.properties.id : ''\n const text = node.children.map(nodeTextValue).join('')\n if (!id || !text) return null\n return { id, depth: Number.parseInt(match[1], 10), text }\n}\n\nexport async function extractToc(markdown: string): Promise<TocItem[]> {\n const headings: TocItem[] = []\n const collectHeadings: Plugin<[], Root> = () => (tree: Root) => {\n visit(tree, 'element', (node: Element) => {\n const item = extractHeadingItem(node)\n if (item) headings.push(item)\n })\n }\n await remark()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkRehype)\n .use(rehypeSlug)\n .use(collectHeadings)\n .use(rehypeStringify)\n .process(markdown)\n return headings\n}\n","import type { Metadata, MetadataRoute } from 'next'\nimport {\n getArticleMetadata,\n getAllArticles,\n getAllCategories,\n getArticlesByCategory,\n getAvailableArticleSlugs,\n} from './server-articles'\nimport { ArticlesConfig } from './articlesConfig'\n\nexport function generateArticleStaticParams(): { slug: string }[] {\n return getAvailableArticleSlugs().map((slug) => ({ slug }))\n}\n\nexport async function generateCategoryStaticParams(): Promise<{ category: string }[]> {\n const categories = await getAllCategories()\n return categories.map((cat) => ({ category: cat.slug }))\n}\n\nfunction resolveImageUrl(featuredImage: string, siteUrl: string): string {\n const base = siteUrl.replace(/\\/$/, '')\n if (featuredImage.startsWith('http://') || featuredImage.startsWith('https://')) {\n return featuredImage\n }\n return `${base}/${featuredImage.replace(/^\\/+/, '')}`\n}\n\nexport async function generateArticleMetadata(\n slug: string,\n config: ArticlesConfig\n): Promise<Metadata> {\n const article = await getArticleMetadata(slug)\n\n if (!article) {\n return {\n title: 'Article Not Found',\n description: 'The requested article could not be found.',\n }\n }\n\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const articleUrl = `${siteUrl}/articles/${slug}`\n const canonicalUrl = article.canonicalUrl ?? articleUrl\n const imageUrl = article.featuredImage\n ? resolveImageUrl(article.featuredImage, siteUrl)\n : `${siteUrl}/placeholder-logo.png`\n const description = article.excerpt ?? `Read ${article.title} on ${config.siteName}.`\n\n return {\n title: `${article.title} | ${config.siteName}`,\n description,\n keywords: [...(article.tags ?? []).map((tag) => tag.toLowerCase())].join(', '),\n openGraph: {\n title: article.title,\n description,\n url: articleUrl,\n siteName: config.siteName,\n images: [{ url: imageUrl, width: 1200, height: 630, alt: article.title }],\n locale: 'en_US',\n type: 'article',\n ...(article.date && { publishedTime: article.date }),\n ...(article.lastmod && { modifiedTime: new Date(article.lastmod).toISOString() }),\n authors: [article.author],\n tags: article.tags ?? [],\n },\n twitter: {\n card: 'summary_large_image',\n title: article.title,\n description,\n images: [imageUrl],\n },\n alternates: {\n canonical: canonicalUrl,\n },\n robots: {\n index: true,\n follow: true,\n googleBot: {\n index: true,\n follow: true,\n 'max-video-preview': -1,\n 'max-image-preview': 'large',\n 'max-snippet': -1,\n },\n },\n other: {\n 'article:author': article.author,\n ...(article.date && {\n 'article:published_time': new Date(article.date).toISOString(),\n }),\n ...(article.lastmod && {\n 'article:modified_time': new Date(article.lastmod).toISOString(),\n }),\n 'article:section': article.category,\n 'article:tag': article.tags?.join(',') ?? '',\n 'linkedin:owner': process.env.NEXT_PUBLIC_LINKEDIN_COMPANY_ID ?? '',\n },\n }\n}\n\nexport function generateArticlesIndexMetadata(config: ArticlesConfig): Metadata {\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const indexUrl = `${siteUrl}/articles`\n const title = `Articles | ${config.siteName}`\n const description =\n config.hero?.description ?? `Expert analysis and insights from ${config.siteName}.`\n return {\n title,\n description,\n openGraph: {\n title,\n description,\n url: indexUrl,\n siteName: config.siteName,\n type: 'website',\n locale: 'en_US',\n },\n twitter: {\n card: 'summary_large_image',\n title,\n description,\n },\n alternates: {\n canonical: indexUrl,\n types: {\n 'application/rss+xml': `${siteUrl}/articles/feed.xml`,\n },\n },\n robots: {\n index: true,\n follow: true,\n googleBot: {\n index: true,\n follow: true,\n 'max-video-preview': -1,\n 'max-image-preview': 'large',\n 'max-snippet': -1,\n },\n },\n }\n}\n\nexport async function generateCategoryMetadata(\n categorySlug: string,\n config: ArticlesConfig\n): Promise<Metadata> {\n const articles = await getArticlesByCategory(categorySlug)\n\n if (articles.length === 0) return { title: 'Category Not Found' }\n\n const categoryName = articles[0].category\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const categoryUrl = `${siteUrl}/articles/category/${categorySlug}`\n const raw = config.categoryDescriptions?.[categorySlug]\n const fallback = `Browse ${articles.length} article${articles.length === 1 ? '' : 's'} in the ${categoryName} category.`\n const description = typeof raw === 'string' ? raw : (raw?.short ?? fallback)\n\n const title = `${categoryName} Articles | ${config.siteName}`\n return {\n title,\n description,\n openGraph: {\n title: `${categoryName} Articles`,\n description,\n url: categoryUrl,\n siteName: config.siteName,\n images: [{ url: articles[0].featuredImage }],\n type: 'website',\n locale: 'en_US',\n },\n twitter: {\n card: 'summary_large_image',\n title: `${categoryName} Articles`,\n description,\n },\n alternates: {\n canonical: categoryUrl,\n },\n robots: {\n index: true,\n follow: true,\n googleBot: {\n index: true,\n follow: true,\n 'max-video-preview': -1,\n 'max-image-preview': 'large',\n 'max-snippet': -1,\n },\n },\n }\n}\n\nexport async function getArticleSitemapEntries(\n baseUrlOrConfig: string | ArticlesConfig\n): Promise<MetadataRoute.Sitemap> {\n const baseUrl = (\n typeof baseUrlOrConfig === 'string' ? baseUrlOrConfig : baseUrlOrConfig.siteUrl\n ).replace(/\\/$/, '')\n\n try {\n const [articles, categories] = await Promise.all([getAllArticles(), getAllCategories()])\n\n const articleEntries: MetadataRoute.Sitemap = articles.map((article) => {\n const dateStr = article.lastmod ?? article.date\n const lastModified = dateStr ? new Date(dateStr) : undefined\n return {\n url: `${baseUrl}/articles/${article.slug}`,\n lastModified,\n changeFrequency: 'weekly' as const,\n priority: 0.8,\n }\n })\n\n const categoryEntries: MetadataRoute.Sitemap = categories.map((cat) => ({\n url: `${baseUrl}/articles/category/${cat.slug}`,\n lastModified: new Date(),\n changeFrequency: 'weekly' as const,\n priority: 0.7,\n }))\n\n return [...articleEntries, ...categoryEntries]\n } catch {\n return []\n }\n}\n","import { MDXRemote } from 'next-mdx-remote/rsc'\nimport type { Article } from './articleTypes'\n\ntype ArticleContentProps = Readonly<{ article: Article; className?: string }>\n\nexport function ArticleContent({ article, className }: ArticleContentProps) {\n if (article.contentType === 'mdx' && article.mdxSource) {\n return (\n <div className={className}>\n <MDXRemote source={article.mdxSource} />\n </div>\n )\n }\n return (\n <div className={className} dangerouslySetInnerHTML={{ __html: article.htmlContent || '' }} />\n )\n}\n","import type { TocItem } from './articleTypes'\n\ntype ArticleTOCProps = Readonly<{ toc: TocItem[]; className?: string }>\n\nexport function ArticleTOC({ toc, className }: ArticleTOCProps) {\n if (!toc.length) return null\n return (\n <nav\n aria-label=\"Table of contents\"\n className={`mb-8 rounded-lg border border-border bg-muted/40 px-6 py-4 ${className ?? ''}`}\n >\n <p className=\"mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground\">\n On this page\n </p>\n <ul className=\"space-y-1 text-sm\">\n {toc.map((item) => (\n <li key={item.id} style={{ paddingLeft: `${Math.max(0, item.depth - 2) * 1}rem` }}>\n <a\n href={`#${item.id}`}\n className=\"text-muted-foreground hover:text-foreground transition-colors\"\n >\n {item.text}\n </a>\n </li>\n ))}\n </ul>\n </nav>\n )\n}\n"],"mappings":"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","names":["sanitizeImagePath","remarkParse","remarkGfm","remarkGithubBlockquoteAlert","remarkRehype","rehypeSlug","rehypeAutolinkHeadings","rehypePrism","rehypeSanitize","rehypeStringify","path","readingTime","fs","sanitizeImagePath","matter","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/server.ts","../src/server-articles.ts","../src/markdown.ts","../src/seoUtils.ts","../src/renderMdx.tsx","../src/ArticleContent.tsx","../src/ArticleTOC.tsx"],"sourcesContent":["// Server-only exports — uses fs/path; never import this in a client bundle\nexport {\n getAllArticles,\n getArticleMetadata,\n getAvailableArticleSlugs,\n getAdjacentArticles,\n searchArticles,\n getAllCategories,\n getArticlesByCategory,\n categoryToSlug,\n sanitizeImagePath,\n} from './server-articles'\n\nexport {\n generateArticleStaticParams,\n generateCategoryStaticParams,\n generateArticlesIndexMetadata,\n generateArticleMetadata,\n generateCategoryMetadata,\n getArticleSitemapEntries,\n} from './seoUtils'\n\nexport { markdownToHtml, extractToc } from './markdown'\nexport { ArticleContent } from './ArticleContent'\nexport { ArticleTOC } from './ArticleTOC'\n\nexport type { Article, CategoryInfo, TocItem } from './articleTypes'\nexport type { ArticlesConfig } from './articlesConfig'\n","import { cache } from 'react'\nimport matter from 'gray-matter'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport readingTime from 'reading-time'\nimport { markdownToHtml, extractToc } from './markdown'\nimport type { Article, CategoryInfo, FaqItem, HowToStep } from './articleTypes'\n\nconst articlesDirectory = path.join(process.cwd(), 'public/articles')\n\nfunction getReadingTime(content: string): string {\n return readingTime(content).text\n}\n\nfunction findArticleImage(slug: string): string | null {\n try {\n const articleDir = path.join(articlesDirectory, slug)\n const entries: (fs.Dirent | string)[] = fs.readdirSync(articleDir, { withFileTypes: true })\n const names = entries\n .map((entry) => {\n if (typeof entry === 'string') return entry\n if (entry && typeof entry.name === 'string') return entry.name\n return null\n })\n .filter((name): name is string => Boolean(name))\n const imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.webp']\n const imageFileName = names.find((name) =>\n imageExtensions.some((ext) => name.toLowerCase().endsWith(ext))\n )\n return imageFileName ? sanitizeImagePath(imageFileName, slug) : null\n } catch {\n return null\n }\n}\n\nfunction sanitizeImagePath(rawPath: string, articleSlug: string): string | null {\n if (!rawPath || typeof rawPath !== 'string') return null\n const cleanPath = rawPath.replaceAll(/[\\x00-\\x1f\\x7f-\\x9f]/g, '')\n if (cleanPath.startsWith('http://') || cleanPath.startsWith('https://')) return cleanPath\n if (cleanPath.includes('..') || cleanPath.includes('\\\\') || cleanPath.startsWith('/')) {\n console.warn(`Potentially unsafe image path detected: ${cleanPath}`)\n return null\n }\n if (!/^[a-zA-Z0-9._/-]+$/.test(cleanPath)) {\n console.warn(`Invalid characters in image path: ${cleanPath}`)\n return null\n }\n if (cleanPath.includes('/')) {\n const normalizedPath = path.normalize(cleanPath)\n if (normalizedPath.startsWith('..') || normalizedPath.includes('../')) {\n console.warn(`Path traversal attempt detected: ${cleanPath}`)\n return null\n }\n return `/articles/${articleSlug}/${cleanPath}`\n }\n return `/articles/${articleSlug}/${cleanPath}`\n}\n\nfunction findArticleFile(slug: string): { filePath: string; contentType: 'md' | 'mdx' } | null {\n const mdPath = path.join(articlesDirectory, slug, 'article.md')\n const mdxPath = path.join(articlesDirectory, slug, 'article.mdx')\n if (fs.existsSync(mdPath)) return { filePath: mdPath, contentType: 'md' }\n if (fs.existsSync(mdxPath)) return { filePath: mdxPath, contentType: 'mdx' }\n return null\n}\n\nexport function getAvailableArticleSlugs(): string[] {\n try {\n if (!fs.existsSync(articlesDirectory)) return []\n return walkArticleDir(articlesDirectory, '')\n } catch (error) {\n console.error('Error reading articles directory:', error)\n return []\n }\n}\n\nfunction walkArticleDir(dir: string, baseSlug: string): string[] {\n const slugs: string[] = []\n try {\n const items = fs.readdirSync(dir, { withFileTypes: true })\n for (const item of items) {\n if (!item.isDirectory()) continue\n const slug = baseSlug ? `${baseSlug}/${item.name}` : item.name\n if (findArticleFile(slug) !== null) {\n slugs.push(slug)\n } else {\n slugs.push(...walkArticleDir(path.join(dir, item.name), slug))\n }\n }\n } catch {\n // ignore unreadable directories\n }\n return slugs\n}\n\nfunction parseDateField(rawDate: unknown): string | undefined {\n if (!rawDate) return undefined\n try {\n const parsed = new Date(rawDate as string)\n if (!Number.isNaN(parsed.getTime())) return parsed.toISOString().split('T')[0]\n } catch {\n // ignore invalid dates\n }\n return undefined\n}\n\nfunction resolveFeaturedImage(rawImage: unknown, slug: string): string {\n const img = typeof rawImage === 'string' ? rawImage : ''\n if (img && !img.startsWith('http')) return sanitizeImagePath(img, slug) || '/placeholder-logo.png'\n if (!img) return findArticleImage(slug) || '/placeholder-logo.png'\n return img\n}\n\nfunction parseFaqItems(raw: unknown): FaqItem[] | undefined {\n if (!Array.isArray(raw)) return undefined\n const items = raw.filter(\n (item): item is FaqItem =>\n typeof item === 'object' &&\n item !== null &&\n typeof (item as FaqItem).question === 'string' &&\n typeof (item as FaqItem).answer === 'string'\n )\n return items.length ? items : undefined\n}\n\nfunction parseHowToSteps(raw: unknown): HowToStep[] | undefined {\n if (!Array.isArray(raw)) return undefined\n const steps = raw.filter(\n (item): item is HowToStep =>\n typeof item === 'object' &&\n item !== null &&\n typeof (item as HowToStep).name === 'string' &&\n typeof (item as HowToStep).text === 'string'\n )\n return steps.length ? steps : undefined\n}\n\nasync function getArticleSummary(slug: string): Promise<Article | null> {\n try {\n const found = findArticleFile(slug)\n if (!found) return null\n const fileContent = fs.readFileSync(found.filePath, 'utf8')\n const { data, content: markdownContent } = matter(fileContent)\n const readTime = getReadingTime(markdownContent)\n const allTags: string[] = Array.isArray(data.tags)\n ? data.tags.filter((t: unknown) => typeof t === 'string' && String(t).trim())\n : []\n const categories: string[] =\n allTags.length > 0 ? allTags.map((t: string) => t.replaceAll('-', ' ').trim()) : ['Campaigns']\n return {\n slug,\n title: data.title || slug.replaceAll('-', ' '),\n excerpt: data.excerpt || '',\n date: parseDateField(data.date),\n lastmod: parseDateField(data.lastmod),\n author: data.author || 'Andrew Blase',\n category: categories[0],\n categories,\n readTime,\n featuredImage: resolveFeaturedImage(data.featuredImage, slug),\n tags: data.tags || [],\n contentType: found.contentType,\n draft: data.draft === true,\n faq: parseFaqItems(data.faq),\n howTo: parseHowToSteps(data.howTo),\n canonicalUrl: typeof data.canonicalUrl === 'string' ? data.canonicalUrl : undefined,\n articleType: typeof data.articleType === 'string' ? data.articleType : undefined,\n series: typeof data.series === 'string' ? data.series : undefined,\n }\n } catch (error) {\n console.error(`Error loading article ${slug}:`, error)\n return null\n }\n}\n\nexport const getArticleMetadata = cache(async (slug: string): Promise<Article | null> => {\n try {\n const summary = await getArticleSummary(slug)\n if (!summary) return null\n const found = findArticleFile(slug)\n if (!found) return null\n const fileContent = fs.readFileSync(found.filePath, 'utf8')\n const { content: markdownContent } = matter(fileContent)\n const toc = await extractToc(markdownContent)\n let htmlContent: string | undefined\n let mdxSource: string | undefined\n if (found.contentType === 'mdx') {\n mdxSource = markdownContent\n } else {\n htmlContent = await markdownToHtml(markdownContent, slug)\n }\n return { ...summary, content: markdownContent, htmlContent, mdxSource, toc }\n } catch (error) {\n console.error(`Error loading article ${slug}:`, error)\n return null\n }\n})\n\nexport const getAllArticles = cache(async (): Promise<Article[]> => {\n const slugs = getAvailableArticleSlugs()\n const articles = await Promise.all(slugs.map((slug) => getArticleSummary(slug)))\n const currentDate = new Date().toISOString().split('T')[0]\n return articles\n .filter((article): article is Article => article !== null)\n .filter((article) => !article.date || article.date <= currentDate)\n .filter((article) => !(article.draft && process.env.NODE_ENV === 'production'))\n .sort((a, b) => {\n if (!a.date && !b.date) return 0\n if (!a.date) return 1\n if (!b.date) return -1\n return new Date(b.date).getTime() - new Date(a.date).getTime()\n })\n})\n\nexport async function getAdjacentArticles(\n currentSlug: string\n): Promise<{ previous: Article | null; next: Article | null }> {\n const allArticles = await getAllArticles()\n const currentIndex = allArticles.findIndex((article) => article.slug === currentSlug)\n if (currentIndex === -1) return { previous: null, next: null }\n const previous = currentIndex < allArticles.length - 1 ? allArticles[currentIndex + 1] : null\n const next = currentIndex > 0 ? allArticles[currentIndex - 1] : null\n return { previous, next }\n}\n\nexport async function searchArticles(query: string): Promise<Article[]> {\n if (!query?.trim()) return getAllArticles()\n const articles = await getAllArticles()\n const searchTerm = query.toLowerCase().trim()\n return articles.filter((article) => {\n const matchesTitle = article.title.toLowerCase().includes(searchTerm)\n const matchesExcerpt = article.excerpt.toLowerCase().includes(searchTerm)\n const matchesAuthor = article.author.toLowerCase().includes(searchTerm)\n const matchesCategory = article.categories.some((cat) => cat.toLowerCase().includes(searchTerm))\n const matchesTags = article.tags?.some((tag) => tag.toLowerCase().includes(searchTerm))\n return (\n matchesTitle || matchesExcerpt || matchesAuthor || matchesCategory || Boolean(matchesTags)\n )\n })\n}\n\nexport function categoryToSlug(category: string): string {\n return category\n .toLowerCase()\n .replaceAll(/\\s+/g, '-')\n .replaceAll(/[^a-z0-9-]/g, '')\n}\n\nexport async function getAllCategories(): Promise<CategoryInfo[]> {\n const articles = await getAllArticles()\n const categoryMap = new Map<string, { count: number; featuredImage: string }>()\n for (const article of articles) {\n for (const cat of article.categories) {\n if (!categoryMap.has(cat)) {\n categoryMap.set(cat, { count: 0, featuredImage: article.featuredImage })\n }\n categoryMap.get(cat)!.count++\n }\n }\n return Array.from(categoryMap.entries())\n .map(([name, { count, featuredImage }]) => ({\n name,\n slug: categoryToSlug(name),\n count,\n featuredImage,\n }))\n .sort((a, b) => b.count - a.count)\n}\n\nexport async function getArticlesByCategory(categorySlug: string): Promise<Article[]> {\n const articles = await getAllArticles()\n return articles.filter((article) =>\n article.categories.some((cat) => categoryToSlug(cat) === categorySlug)\n )\n}\n\nexport { sanitizeImagePath }\n","import type { Element, Root, ElementContent } from 'hast'\nimport rehypePrism from 'rehype-prism-plus'\nimport rehypeSanitize from 'rehype-sanitize'\nimport rehypeSlug from 'rehype-slug'\nimport rehypeStringify from 'rehype-stringify'\nimport { remark } from 'remark'\nimport remarkGfm from 'remark-gfm'\nimport remarkGithubBlockquoteAlert from 'remark-github-blockquote-alert'\nimport remarkParse from 'remark-parse'\nimport remarkRehype from 'remark-rehype'\nimport { Plugin } from 'unified'\nimport { visit } from 'unist-util-visit'\nimport type { TocItem } from './articleTypes'\n\n// Import the sanitizeImagePath function\nfunction sanitizeImagePath(rawPath: string, articleSlug: string): string | null {\n if (!rawPath || typeof rawPath !== 'string') {\n return null\n }\n\n // Remove any null bytes or control characters\n const cleanPath = rawPath.replaceAll(/[\\x00-\\x1f\\x7f-\\x9f]/g, '')\n\n // Check for absolute URLs (http/https)\n if (cleanPath.startsWith('http://') || cleanPath.startsWith('https://')) {\n // For external URLs, just return as-is (they're safe)\n return cleanPath\n }\n\n // For relative paths, ensure they don't contain dangerous patterns\n if (cleanPath.includes('..') || cleanPath.includes('\\\\') || cleanPath.startsWith('/')) {\n console.warn(`Potentially unsafe image path detected: ${cleanPath}`)\n return null\n }\n\n // Only allow alphanumeric characters, hyphens, underscores, dots, and forward slashes\n if (!/^[a-zA-Z0-9._/-]+$/.test(cleanPath)) {\n console.warn(`Invalid characters in image path: ${cleanPath}`)\n return null\n }\n\n // Construct safe path within articles directory\n if (cleanPath.includes('/')) {\n // Relative path, ensure it's within the article directory\n const normalizedPath = cleanPath.replaceAll('\\\\', '/')\n if (normalizedPath.startsWith('..') || normalizedPath.includes('../')) {\n console.warn(`Path traversal attempt detected: ${cleanPath}`)\n return null\n }\n return `/articles/${articleSlug}/${normalizedPath}`\n } else {\n // Just a filename, construct full path\n return `/articles/${articleSlug}/${cleanPath}`\n }\n}\n\nfunction styleFootnoteLinks(nodes: ElementContent[]): void {\n nodes.forEach((n) => {\n if (n.type !== 'element') return\n const el = n as Element\n if (el.tagName === 'a') {\n const isBackRef =\n el.properties?.['dataFootnoteBackref'] !== undefined ||\n (el.children[0]?.type === 'text' && el.children[0].value === '↩')\n if (isBackRef) {\n el.properties.className = 'text-primary hover:underline ml-1'\n if (typeof el.properties.href === 'string') {\n el.properties.href = el.properties.href.replaceAll('#user-content-fnref-', '#ref-')\n }\n } else {\n el.properties.className = 'text-primary hover:underline break-all'\n }\n }\n if (el.children) styleFootnoteLinks(el.children)\n })\n}\n\nfunction processFootnoteRef(node: Element): void {\n if (\n node.tagName !== 'sup' ||\n node.children?.[0]?.type !== 'element' ||\n (node.children[0] as Element).tagName !== 'a'\n )\n return\n\n const link = node.children[0] as Element\n const href = link.properties?.href\n if (typeof href !== 'string' || !href.startsWith('#user-content-fn-')) return\n\n delete link.properties.target\n delete link.properties.rel\n link.properties.className = 'text-primary hover:underline'\n link.properties.href = href.replaceAll('#user-content-fn-', '#footnote-')\n\n if (typeof node.properties?.id === 'string') {\n link.properties.id = node.properties.id.replaceAll('user-content-fnref-', 'ref-')\n delete node.properties.id\n }\n\n if (link.children?.[0]?.type === 'text') {\n link.children[0].value = `[${link.children[0].value}]`\n }\n}\n\nfunction processFootnotesSection(node: Element): void {\n const cls = node.properties?.className\n const isFootnotes =\n node.tagName === 'section' &&\n (Array.isArray(cls) ? cls.includes('footnotes') : cls === 'footnotes')\n if (!isFootnotes) return\n\n const olCandidate = node.children.find(\n (child) => child.type === 'element' && (child as Element).tagName === 'ol'\n )\n if (olCandidate?.type !== 'element') return\n\n const ol = olCandidate as Element\n ol.properties.className = 'list-decimal ml-6 space-y-2 text-sm text-muted-foreground'\n\n ol.children.forEach((li) => {\n if (li.type !== 'element' || li.tagName !== 'li') return\n const liEl = li as Element\n\n if (liEl.properties) {\n liEl.properties.className = 'pl-2'\n if (typeof liEl.properties.id === 'string') {\n liEl.properties.id = liEl.properties.id.replaceAll('user-content-fn-', 'footnote-')\n }\n }\n\n const pIndex = liEl.children.findIndex(\n (child) => child.type === 'element' && (child as Element).tagName === 'p'\n )\n if (pIndex !== -1) {\n const p = liEl.children[pIndex] as Element\n liEl.children.splice(pIndex, 1, ...p.children)\n }\n\n styleFootnoteLinks(liEl.children)\n })\n\n const hr: Element = {\n type: 'element',\n tagName: 'hr',\n properties: { className: 'my-8 border-border' },\n children: [],\n }\n const h3: Element = {\n type: 'element',\n tagName: 'h3',\n properties: { className: 'text-lg font-semibold mb-4' },\n children: [{ type: 'text', value: 'References' }],\n }\n\n node.children = [hr, h3, ol]\n if (node.properties) node.properties.className = undefined\n}\n\nexport const customRenderer: Plugin<[], Root> = () => {\n return (tree: Root) => {\n // First pass: Apply general styles\n visit(tree, 'element', (node: Element) => {\n if (node.tagName) {\n const props = node.properties || {}\n\n switch (node.tagName) {\n case 'h1':\n props.className = 'text-3xl font-bold text-foreground mt-8 mb-4 scroll-mt-20'\n break\n case 'h2':\n props.className = 'text-2xl font-semibold text-foreground mt-6 mb-3 scroll-mt-20'\n break\n case 'h3':\n props.className = 'text-xl font-semibold text-foreground mt-4 mb-2 scroll-mt-20'\n break\n case 'h4':\n props.className = 'text-lg font-semibold text-foreground mt-3 mb-2 scroll-mt-20'\n break\n case 'p':\n props.className = 'text-muted-foreground leading-relaxed mb-4'\n break\n case 'a': {\n props.className = 'text-primary hover:underline transition-colors duration-200'\n const href = typeof props.href === 'string' ? props.href : ''\n if (!href.startsWith('#')) {\n props.target = '_blank'\n props.rel = 'noopener noreferrer'\n }\n break\n }\n case 'ul':\n props.className = 'list-disc list-inside mb-4 space-y-2 ml-4'\n break\n case 'ol':\n props.className = 'list-decimal list-inside mb-4 space-y-2 ml-4'\n break\n case 'li':\n props.className = 'mb-1'\n break\n case 'blockquote':\n props.className = 'border-l-4 border-primary pl-4 italic my-4 text-muted-foreground'\n break\n case 'code':\n props.className =\n (props.className ? props.className + ' ' : '') +\n 'bg-muted px-1 py-0.5 rounded text-sm font-mono'\n break\n case 'pre':\n props.className = 'bg-muted rounded-lg p-4 overflow-x-auto my-4'\n break\n case 'img':\n props.className = 'rounded-lg my-6 w-full max-w-2xl mx-auto'\n break\n case 'table':\n props.className = 'border-collapse border border-border my-4 w-full'\n break\n case 'th':\n props.className = 'border border-border px-2 py-1 bg-muted font-semibold'\n break\n case 'td':\n props.className = 'border border-border px-2 py-1'\n break\n case 'hr':\n props.className = 'my-8 border-border'\n break\n default:\n break\n }\n\n node.properties = props\n }\n })\n\n // Second pass: Fix footnotes and references\n visit(tree, 'element', (node: Element) => {\n processFootnoteRef(node)\n processFootnotesSection(node)\n })\n }\n}\n\n// Custom rehype plugin to process image URLs\nconst rehypeProcessImages: Plugin<[{ articleSlug?: string }], Root> = (options = {}) => {\n return (tree: Root) => {\n visit(tree, 'element', (node: Element) => {\n if (node.tagName === 'img' && node.properties) {\n const src = node.properties.src\n if (src && typeof src === 'string' && options.articleSlug) {\n // Sanitize the image path\n const sanitizedSrc = sanitizeImagePath(src, options.articleSlug)\n if (sanitizedSrc) {\n node.properties.src = sanitizedSrc\n } else {\n // If sanitization fails, use a placeholder\n console.warn(`Using placeholder for unsafe image path: ${src}`)\n node.properties.src = '/placeholder-logo.png'\n }\n }\n }\n })\n }\n}\n\nexport async function markdownToHtml(markdown: string, articleSlug?: string) {\n try {\n // Start building the remark processor\n let processor = remark()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkGithubBlockquoteAlert)\n .use(remarkRehype)\n .use(customRenderer)\n .use(rehypeSlug)\n // @ts-ignore\n .use(rehypePrism)\n .use(rehypeSanitize, {\n attributes: {\n '*': ['className', 'class', 'id'],\n a: ['href', 'target', 'rel', 'id'],\n img: ['src', 'alt'],\n },\n })\n\n // Add image processing plugin if articleSlug is provided\n if (articleSlug) {\n processor = processor.use(rehypeProcessImages, { articleSlug })\n }\n\n const result = await processor.use(rehypeStringify).process(markdown)\n\n return result.toString()\n } catch (error) {\n console.error('Markdown conversion error:', error)\n // Return the original markdown as fallback\n return markdown\n }\n}\n\nfunction nodeTextValue(c: ElementContent): string {\n return c.type === 'text' ? (c as { value: string }).value : ''\n}\n\nfunction extractHeadingItem(node: Element): TocItem | null {\n const match = /^h([1-6])$/.exec(node.tagName)\n if (!match) return null\n const id = typeof node.properties?.id === 'string' ? node.properties.id : ''\n const text = node.children.map(nodeTextValue).join('')\n if (!id || !text) return null\n return { id, depth: Number.parseInt(match[1], 10), text }\n}\n\nexport async function extractToc(markdown: string): Promise<TocItem[]> {\n const headings: TocItem[] = []\n const collectHeadings: Plugin<[], Root> = () => (tree: Root) => {\n visit(tree, 'element', (node: Element) => {\n const item = extractHeadingItem(node)\n if (item) headings.push(item)\n })\n }\n await remark()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkRehype)\n .use(rehypeSlug)\n .use(collectHeadings)\n .use(rehypeStringify)\n .process(markdown)\n return headings\n}\n","import type { Metadata, MetadataRoute } from 'next'\nimport {\n getArticleMetadata,\n getAllArticles,\n getAllCategories,\n getArticlesByCategory,\n getAvailableArticleSlugs,\n} from './server-articles'\nimport { ArticlesConfig } from './articlesConfig'\n\nexport function generateArticleStaticParams(): { slug: string }[] {\n return getAvailableArticleSlugs().map((slug) => ({ slug }))\n}\n\nexport async function generateCategoryStaticParams(): Promise<{ category: string }[]> {\n const categories = await getAllCategories()\n return categories.map((cat) => ({ category: cat.slug }))\n}\n\nfunction resolveImageUrl(featuredImage: string, siteUrl: string): string {\n const base = siteUrl.replace(/\\/$/, '')\n if (featuredImage.startsWith('http://') || featuredImage.startsWith('https://')) {\n return featuredImage\n }\n return `${base}/${featuredImage.replace(/^\\/+/, '')}`\n}\n\nexport async function generateArticleMetadata(\n slug: string,\n config: ArticlesConfig\n): Promise<Metadata> {\n const article = await getArticleMetadata(slug)\n\n if (!article) {\n return {\n title: 'Article Not Found',\n description: 'The requested article could not be found.',\n }\n }\n\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const articleUrl = `${siteUrl}/articles/${slug}`\n const canonicalUrl = article.canonicalUrl ?? articleUrl\n const imageUrl = article.featuredImage\n ? resolveImageUrl(article.featuredImage, siteUrl)\n : `${siteUrl}/placeholder-logo.png`\n const description = article.excerpt ?? `Read ${article.title} on ${config.siteName}.`\n\n return {\n title: `${article.title} | ${config.siteName}`,\n description,\n keywords: [...(article.tags ?? []).map((tag) => tag.toLowerCase())].join(', '),\n openGraph: {\n title: article.title,\n description,\n url: articleUrl,\n siteName: config.siteName,\n images: [{ url: imageUrl, width: 1200, height: 630, alt: article.title }],\n locale: 'en_US',\n type: 'article',\n ...(article.date && { publishedTime: article.date }),\n ...(article.lastmod && { modifiedTime: new Date(article.lastmod).toISOString() }),\n authors: [article.author],\n tags: article.tags ?? [],\n },\n twitter: {\n card: 'summary_large_image',\n title: article.title,\n description,\n images: [imageUrl],\n },\n alternates: {\n canonical: canonicalUrl,\n },\n robots: {\n index: true,\n follow: true,\n googleBot: {\n index: true,\n follow: true,\n 'max-video-preview': -1,\n 'max-image-preview': 'large',\n 'max-snippet': -1,\n },\n },\n other: {\n 'article:author': article.author,\n ...(article.date && {\n 'article:published_time': new Date(article.date).toISOString(),\n }),\n ...(article.lastmod && {\n 'article:modified_time': new Date(article.lastmod).toISOString(),\n }),\n 'article:section': article.category,\n 'article:tag': article.tags?.join(',') ?? '',\n 'linkedin:owner': process.env.NEXT_PUBLIC_LINKEDIN_COMPANY_ID ?? '',\n },\n }\n}\n\nexport function generateArticlesIndexMetadata(config: ArticlesConfig): Metadata {\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const indexUrl = `${siteUrl}/articles`\n const title = `Articles | ${config.siteName}`\n const description =\n config.hero?.description ?? `Expert analysis and insights from ${config.siteName}.`\n return {\n title,\n description,\n openGraph: {\n title,\n description,\n url: indexUrl,\n siteName: config.siteName,\n type: 'website',\n locale: 'en_US',\n },\n twitter: {\n card: 'summary_large_image',\n title,\n description,\n },\n alternates: {\n canonical: indexUrl,\n types: {\n 'application/rss+xml': `${siteUrl}/articles/feed.xml`,\n },\n },\n robots: {\n index: true,\n follow: true,\n googleBot: {\n index: true,\n follow: true,\n 'max-video-preview': -1,\n 'max-image-preview': 'large',\n 'max-snippet': -1,\n },\n },\n }\n}\n\nexport async function generateCategoryMetadata(\n categorySlug: string,\n config: ArticlesConfig\n): Promise<Metadata> {\n const articles = await getArticlesByCategory(categorySlug)\n\n if (articles.length === 0) return { title: 'Category Not Found' }\n\n const categoryName = articles[0].category\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const categoryUrl = `${siteUrl}/articles/category/${categorySlug}`\n const raw = config.categoryDescriptions?.[categorySlug]\n const fallback = `Browse ${articles.length} article${articles.length === 1 ? '' : 's'} in the ${categoryName} category.`\n const description = typeof raw === 'string' ? raw : (raw?.short ?? fallback)\n\n const title = `${categoryName} Articles | ${config.siteName}`\n return {\n title,\n description,\n openGraph: {\n title: `${categoryName} Articles`,\n description,\n url: categoryUrl,\n siteName: config.siteName,\n images: [{ url: articles[0].featuredImage }],\n type: 'website',\n locale: 'en_US',\n },\n twitter: {\n card: 'summary_large_image',\n title: `${categoryName} Articles`,\n description,\n },\n alternates: {\n canonical: categoryUrl,\n },\n robots: {\n index: true,\n follow: true,\n googleBot: {\n index: true,\n follow: true,\n 'max-video-preview': -1,\n 'max-image-preview': 'large',\n 'max-snippet': -1,\n },\n },\n }\n}\n\nexport async function getArticleSitemapEntries(\n baseUrlOrConfig: string | ArticlesConfig\n): Promise<MetadataRoute.Sitemap> {\n const baseUrl = (\n typeof baseUrlOrConfig === 'string' ? baseUrlOrConfig : baseUrlOrConfig.siteUrl\n ).replace(/\\/$/, '')\n\n try {\n const [articles, categories] = await Promise.all([getAllArticles(), getAllCategories()])\n\n const articleEntries: MetadataRoute.Sitemap = articles.map((article) => {\n const dateStr = article.lastmod ?? article.date\n const lastModified = dateStr ? new Date(dateStr) : undefined\n return {\n url: `${baseUrl}/articles/${article.slug}`,\n lastModified,\n changeFrequency: 'weekly' as const,\n priority: 0.8,\n }\n })\n\n const categoryEntries: MetadataRoute.Sitemap = categories.map((cat) => ({\n url: `${baseUrl}/articles/category/${cat.slug}`,\n lastModified: new Date(),\n changeFrequency: 'weekly' as const,\n priority: 0.7,\n }))\n\n return [...articleEntries, ...categoryEntries]\n } catch {\n return []\n }\n}\n","// MDX files must use JSX prop syntax:\n// Correct: <span style={{ color: '#3b82f6' }}>Text</span>\n// Incorrect: <span style=\"color: #3b82f6\">Text</span>\n// Correct: className=\"...\"\n// Incorrect: class=\"...\"\nimport React from 'react'\nimport type { ComponentType, ImgHTMLAttributes } from 'react'\nimport * as devRuntime from 'react/jsx-dev-runtime'\nimport * as runtime from 'react/jsx-runtime'\nimport { evaluate } from '@mdx-js/mdx'\nimport rehypePrism from 'rehype-prism-plus'\nimport rehypeSlug from 'rehype-slug'\nimport remarkGfm from 'remark-gfm'\nimport remarkGithubBlockquoteAlert from 'remark-github-blockquote-alert'\nimport { customRenderer } from './markdown'\n\ntype MdxContent = ComponentType<{\n components?: Record<string, ComponentType<unknown>>\n}>\n\nfunction makeImgComponent(basePath: string) {\n return function MdxImage({ src, alt, ...props }: ImgHTMLAttributes<HTMLImageElement>) {\n const resolvedSrc =\n src && !src.startsWith('http') && !src.startsWith('/') ? `${basePath}/${src}` : src\n return React.createElement('img', { src: resolvedSrc, alt, ...props })\n }\n}\n\nexport async function renderMdxSource(source: string, basePath?: string) {\n const isDevelopment = process.env.NODE_ENV === 'development'\n\n const mdxModule = await evaluate(source, {\n ...(isDevelopment ? devRuntime : runtime),\n development: isDevelopment,\n remarkPlugins: [remarkGfm, remarkGithubBlockquoteAlert],\n rehypePlugins: [\n customRenderer,\n rehypeSlug,\n // @ts-ignore\n rehypePrism,\n ],\n })\n\n const Content = mdxModule.default as MdxContent\n const components = basePath ? { img: makeImgComponent(basePath) } : undefined\n return <Content components={components as Record<string, ComponentType<unknown>>} />\n}\n","import { renderMdxSource } from './renderMdx'\nimport type { Article } from './articleTypes'\n\ntype ArticleContentProps = Readonly<{ article: Article; className?: string }>\n\nexport async function ArticleContent({ article, className }: ArticleContentProps) {\n if (article.contentType === 'mdx' && article.mdxSource) {\n const content = await renderMdxSource(article.mdxSource, `/articles/${article.slug}`)\n return <div className={className}>{content}</div>\n }\n return (\n <div className={className} dangerouslySetInnerHTML={{ __html: article.htmlContent || '' }} />\n )\n}\n","import type { TocItem } from './articleTypes'\n\ntype ArticleTOCProps = Readonly<{ toc: TocItem[]; className?: string }>\n\nexport function ArticleTOC({ toc, className }: ArticleTOCProps) {\n if (!toc.length) return null\n return (\n <nav\n aria-label=\"Table of contents\"\n className={`mb-8 rounded-lg border border-border bg-muted/40 px-6 py-4 ${className ?? ''}`}\n >\n <p className=\"mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground\">\n On this page\n </p>\n <ul className=\"space-y-1 text-sm\">\n {toc.map((item) => (\n <li key={item.id} style={{ paddingLeft: `${Math.max(0, item.depth - 2) * 1}rem` }}>\n <a\n href={`#${item.id}`}\n className=\"text-muted-foreground hover:text-foreground transition-colors\"\n >\n {item.text}\n </a>\n </li>\n ))}\n </ul>\n </nav>\n )\n}\n"],"mappings":"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","names":["sanitizeImagePath","remarkParse","remarkGfm","remarkGithubBlockquoteAlert","remarkRehype","rehypeSlug","rehypePrism","rehypeSanitize","rehypeStringify","path","readingTime","fs","sanitizeImagePath","matter","import_react","import_rehype_prism_plus","import_rehype_slug","import_remark_gfm","import_remark_github_blockquote_alert","React","remarkGfm","remarkGithubBlockquoteAlert","rehypeSlug","rehypePrism","import_jsx_runtime","import_jsx_runtime"]}
package/dist/server.d.cts CHANGED
@@ -147,6 +147,8 @@ interface ArticlesConfig {
147
147
  description?: string;
148
148
  /** Set to false to hide the table of contents on article detail pages. Default: true. */
149
149
  showToc?: boolean;
150
+ /** Set to false to hide the "Back to Articles" navigation link on article detail pages. Default: true. */
151
+ showBackToArticles?: boolean;
150
152
  }
151
153
 
152
154
  declare function generateArticleStaticParams(): {
@@ -167,7 +169,7 @@ type ArticleContentProps = Readonly<{
167
169
  article: Article;
168
170
  className?: string;
169
171
  }>;
170
- declare function ArticleContent({ article, className }: ArticleContentProps): react_jsx_runtime.JSX.Element;
172
+ declare function ArticleContent({ article, className }: ArticleContentProps): Promise<react_jsx_runtime.JSX.Element>;
171
173
 
172
174
  type ArticleTOCProps = Readonly<{
173
175
  toc: TocItem[];
package/dist/server.d.ts CHANGED
@@ -147,6 +147,8 @@ interface ArticlesConfig {
147
147
  description?: string;
148
148
  /** Set to false to hide the table of contents on article detail pages. Default: true. */
149
149
  showToc?: boolean;
150
+ /** Set to false to hide the "Back to Articles" navigation link on article detail pages. Default: true. */
151
+ showBackToArticles?: boolean;
150
152
  }
151
153
 
152
154
  declare function generateArticleStaticParams(): {
@@ -167,7 +169,7 @@ type ArticleContentProps = Readonly<{
167
169
  article: Article;
168
170
  className?: string;
169
171
  }>;
170
- declare function ArticleContent({ article, className }: ArticleContentProps): react_jsx_runtime.JSX.Element;
172
+ declare function ArticleContent({ article, className }: ArticleContentProps): Promise<react_jsx_runtime.JSX.Element>;
171
173
 
172
174
  type ArticleTOCProps = Readonly<{
173
175
  toc: TocItem[];
package/dist/server.js CHANGED
@@ -17,6 +17,18 @@ var __spreadValues = (a, b) => {
17
17
  return a;
18
18
  };
19
19
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __objRest = (source, exclude) => {
21
+ var target = {};
22
+ for (var prop in source)
23
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
24
+ target[prop] = source[prop];
25
+ if (source != null && __getOwnPropSymbols)
26
+ for (var prop of __getOwnPropSymbols(source)) {
27
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
28
+ target[prop] = source[prop];
29
+ }
30
+ return target;
31
+ };
20
32
  var __async = (__this, __arguments, generator) => {
21
33
  return new Promise((resolve, reject) => {
22
34
  var fulfilled = (value) => {
@@ -46,7 +58,6 @@ import path from "node:path";
46
58
  import readingTime from "reading-time";
47
59
 
48
60
  // src/markdown.ts
49
- import rehypeAutolinkHeadings from "rehype-autolink-headings";
50
61
  import rehypePrism from "rehype-prism-plus";
51
62
  import rehypeSanitize from "rehype-sanitize";
52
63
  import rehypeSlug from "rehype-slug";
@@ -187,11 +198,15 @@ var customRenderer = () => {
187
198
  case "p":
188
199
  props.className = "text-muted-foreground leading-relaxed mb-4";
189
200
  break;
190
- case "a":
201
+ case "a": {
191
202
  props.className = "text-primary hover:underline transition-colors duration-200";
192
- props.target = "_blank";
193
- props.rel = "noopener noreferrer";
203
+ const href = typeof props.href === "string" ? props.href : "";
204
+ if (!href.startsWith("#")) {
205
+ props.target = "_blank";
206
+ props.rel = "noopener noreferrer";
207
+ }
194
208
  break;
209
+ }
195
210
  case "ul":
196
211
  props.className = "list-disc list-inside mb-4 space-y-2 ml-4";
197
212
  break;
@@ -258,7 +273,7 @@ var rehypeProcessImages = (options = {}) => {
258
273
  function markdownToHtml(markdown, articleSlug) {
259
274
  return __async(this, null, function* () {
260
275
  try {
261
- let processor = remark().use(remarkParse).use(remarkGfm).use(remarkGithubBlockquoteAlert).use(remarkRehype).use(customRenderer).use(rehypeSlug).use(rehypeAutolinkHeadings, { behavior: "wrap" }).use(rehypePrism).use(rehypeSanitize, {
276
+ let processor = remark().use(remarkParse).use(remarkGfm).use(remarkGithubBlockquoteAlert).use(remarkRehype).use(customRenderer).use(rehypeSlug).use(rehypePrism).use(rehypeSanitize, {
262
277
  attributes: {
263
278
  "*": ["className", "class", "id"],
264
279
  a: ["href", "target", "rel", "id"],
@@ -357,19 +372,29 @@ function findArticleFile(slug) {
357
372
  function getAvailableArticleSlugs() {
358
373
  try {
359
374
  if (!fs.existsSync(articlesDirectory)) return [];
360
- const items = fs.readdirSync(articlesDirectory, { withFileTypes: true });
361
- return items.filter((item) => item.isDirectory()).filter((dir) => {
362
- try {
363
- return findArticleFile(dir.name) !== null;
364
- } catch (e) {
365
- return false;
366
- }
367
- }).map((dir) => dir.name);
375
+ return walkArticleDir(articlesDirectory, "");
368
376
  } catch (error) {
369
377
  console.error("Error reading articles directory:", error);
370
378
  return [];
371
379
  }
372
380
  }
381
+ function walkArticleDir(dir, baseSlug) {
382
+ const slugs = [];
383
+ try {
384
+ const items = fs.readdirSync(dir, { withFileTypes: true });
385
+ for (const item of items) {
386
+ if (!item.isDirectory()) continue;
387
+ const slug = baseSlug ? `${baseSlug}/${item.name}` : item.name;
388
+ if (findArticleFile(slug) !== null) {
389
+ slugs.push(slug);
390
+ } else {
391
+ slugs.push(...walkArticleDir(path.join(dir, item.name), slug));
392
+ }
393
+ }
394
+ } catch (e) {
395
+ }
396
+ return slugs;
397
+ }
373
398
  function parseDateField(rawDate) {
374
399
  if (!rawDate) return void 0;
375
400
  try {
@@ -724,18 +749,56 @@ function getArticleSitemapEntries(baseUrlOrConfig) {
724
749
  });
725
750
  }
726
751
 
727
- // src/ArticleContent.tsx
728
- import { MDXRemote } from "next-mdx-remote/rsc";
752
+ // src/renderMdx.tsx
753
+ import React from "react";
754
+ import * as devRuntime from "react/jsx-dev-runtime";
755
+ import * as runtime from "react/jsx-runtime";
756
+ import { evaluate } from "@mdx-js/mdx";
757
+ import rehypePrism2 from "rehype-prism-plus";
758
+ import rehypeSlug2 from "rehype-slug";
759
+ import remarkGfm2 from "remark-gfm";
760
+ import remarkGithubBlockquoteAlert2 from "remark-github-blockquote-alert";
729
761
  import { jsx } from "react/jsx-runtime";
730
- function ArticleContent({ article, className }) {
731
- if (article.contentType === "mdx" && article.mdxSource) {
732
- return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx(MDXRemote, { source: article.mdxSource }) });
733
- }
734
- return /* @__PURE__ */ jsx("div", { className, dangerouslySetInnerHTML: { __html: article.htmlContent || "" } });
762
+ function makeImgComponent(basePath) {
763
+ return function MdxImage(_a) {
764
+ var _b = _a, { src, alt } = _b, props = __objRest(_b, ["src", "alt"]);
765
+ const resolvedSrc = src && !src.startsWith("http") && !src.startsWith("/") ? `${basePath}/${src}` : src;
766
+ return React.createElement("img", __spreadValues({ src: resolvedSrc, alt }, props));
767
+ };
768
+ }
769
+ function renderMdxSource(source, basePath) {
770
+ return __async(this, null, function* () {
771
+ const isDevelopment = process.env.NODE_ENV === "development";
772
+ const mdxModule = yield evaluate(source, __spreadProps(__spreadValues({}, isDevelopment ? devRuntime : runtime), {
773
+ development: isDevelopment,
774
+ remarkPlugins: [remarkGfm2, remarkGithubBlockquoteAlert2],
775
+ rehypePlugins: [
776
+ customRenderer,
777
+ rehypeSlug2,
778
+ // @ts-ignore
779
+ rehypePrism2
780
+ ]
781
+ }));
782
+ const Content = mdxModule.default;
783
+ const components = basePath ? { img: makeImgComponent(basePath) } : void 0;
784
+ return /* @__PURE__ */ jsx(Content, { components });
785
+ });
786
+ }
787
+
788
+ // src/ArticleContent.tsx
789
+ import { jsx as jsx2 } from "react/jsx-runtime";
790
+ function ArticleContent(_0) {
791
+ return __async(this, arguments, function* ({ article, className }) {
792
+ if (article.contentType === "mdx" && article.mdxSource) {
793
+ const content = yield renderMdxSource(article.mdxSource, `/articles/${article.slug}`);
794
+ return /* @__PURE__ */ jsx2("div", { className, children: content });
795
+ }
796
+ return /* @__PURE__ */ jsx2("div", { className, dangerouslySetInnerHTML: { __html: article.htmlContent || "" } });
797
+ });
735
798
  }
736
799
 
737
800
  // src/ArticleTOC.tsx
738
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
801
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
739
802
  function ArticleTOC({ toc, className }) {
740
803
  if (!toc.length) return null;
741
804
  return /* @__PURE__ */ jsxs(
@@ -744,8 +807,8 @@ function ArticleTOC({ toc, className }) {
744
807
  "aria-label": "Table of contents",
745
808
  className: `mb-8 rounded-lg border border-border bg-muted/40 px-6 py-4 ${className != null ? className : ""}`,
746
809
  children: [
747
- /* @__PURE__ */ jsx2("p", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "On this page" }),
748
- /* @__PURE__ */ jsx2("ul", { className: "space-y-1 text-sm", children: toc.map((item) => /* @__PURE__ */ jsx2("li", { style: { paddingLeft: `${Math.max(0, item.depth - 2) * 1}rem` }, children: /* @__PURE__ */ jsx2(
810
+ /* @__PURE__ */ jsx3("p", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "On this page" }),
811
+ /* @__PURE__ */ jsx3("ul", { className: "space-y-1 text-sm", children: toc.map((item) => /* @__PURE__ */ jsx3("li", { style: { paddingLeft: `${Math.max(0, item.depth - 2) * 1}rem` }, children: /* @__PURE__ */ jsx3(
749
812
  "a",
750
813
  {
751
814
  href: `#${item.id}`,