From 9f05a2fdbbdc27ed004da74f2e4af069cc4307fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Rebours?= Date: Thu, 22 Feb 2024 14:37:09 +0100 Subject: [PATCH] render excerpts of MDX differently --- package-lock.json | 6 +++ package.json | 1 + src/pages/[lang]/blog/index.astro | 5 ++- src/pages/[lang]/rss.xml.ts | 29 +-------------- src/utils/mdxToHtml.ts | 62 +++++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 30 deletions(-) create mode 100644 src/utils/mdxToHtml.ts diff --git a/package-lock.json b/package-lock.json index 33f2fdc..8dce7a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "astro-pintora": "^0.0.2", "buffer": "^6.0.3", "mdast-util-mdx-jsx": "^3.0.0", + "rehype-truncate": "^1.2.2", "remark": "^15.0.1", "remark-directive": "^3.0.0", "remark-html": "^16.0.1", @@ -10507,6 +10508,11 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/rehype-truncate": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/rehype-truncate/-/rehype-truncate-1.2.2.tgz", + "integrity": "sha512-zj2FxxC3rm8bg6loesMdS/+BuGpp89mu+12VsJrBzN5kKgOxKwtawscQXElyt2BnxU2pG3hkoyb3Euc3Q7F38A==" + }, "node_modules/remark": { "version": "15.0.1", "resolved": "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz", diff --git a/package.json b/package.json index e0fa5bb..e5863de 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "astro-pintora": "^0.0.2", "buffer": "^6.0.3", "mdast-util-mdx-jsx": "^3.0.0", + "rehype-truncate": "^1.2.2", "remark": "^15.0.1", "remark-directive": "^3.0.0", "remark-html": "^16.0.1", diff --git a/src/pages/[lang]/blog/index.astro b/src/pages/[lang]/blog/index.astro index 30f8465..8a0e5a3 100644 --- a/src/pages/[lang]/blog/index.astro +++ b/src/pages/[lang]/blog/index.astro @@ -5,6 +5,7 @@ import Layout from '../../../layouts/Layout.astro' import { getCollection } from 'astro:content' import { slugify } from '../../../utils' import { languages } from '../../../i18n' +import { convertMDXToHTML } from '../../../utils/mdxToHtml' export function getStaticPaths (): Array<{ params: { lang: string } }> { return Object.keys(languages).map(lang => ({ params: { lang } })) @@ -18,7 +19,7 @@ const localizePath = useTranslatedPath(lang) const blogPosts = (await getCollection('blog', ({ id }) => id.split('/')[0] === lang)) .sort((a, b) => b.data.date.getTime() - a.data.date.getTime()) -const renderedBlogPosts = await Promise.all(blogPosts.map(async post => ({ ...post, Component: (await post.render()).Content }))) +const renderedBlogPosts = await Promise.all(blogPosts.map(async post => ({ ...post, html: await convertMDXToHTML(post.body, 100) }))) const formatDate = useFormatDate(lang) --- @@ -45,7 +46,7 @@ const formatDate = useFormatDate(lang)

{post.data.title}

-
+
{formatDate(post.data.date)}
diff --git a/src/pages/[lang]/rss.xml.ts b/src/pages/[lang]/rss.xml.ts index 0a1285f..e13ce46 100644 --- a/src/pages/[lang]/rss.xml.ts +++ b/src/pages/[lang]/rss.xml.ts @@ -6,34 +6,7 @@ import { getCollection } from 'astro:content' import { slugify } from '~/utils' import { getAbsoluteLocaleUrl } from 'astro:i18n' import sanitizeHtml from 'sanitize-html' -import { unified } from 'unified' -import remarkParse from 'remark-parse' -import remarkMDX from 'remark-mdx' -import remarkHtml from 'remark-html' -import type { Root, Node } from 'mdast' - -const removeImportsAndExports = () => { - return (tree: Root) => { - tree.children = tree.children.filter((node: Node) => { - return node.type !== 'mdxjsEsm' && // import tags - node.type !== 'mdxJsxFlowElement' // custom components (Image, Pintora) - }) - } -} -const convertMDXToHTML = async (mdxContent: string): Promise => { - try { - const file = await unified() - .use(remarkParse) // Parse the Markdown syntax - .use(remarkMDX) // Parse the MDX syntax - .use(removeImportsAndExports) - .use(remarkHtml) // Convert to HTML - .process(mdxContent) // Process the MDX content - return String(file) - } catch (error) { - console.error('Error converting MDX to HTML:', error) - throw error - } -} +import { convertMDXToHTML } from '~/utils/mdxToHtml.ts' export const GET: APIRoute = async (context) => { const lang = context.params.lang diff --git a/src/utils/mdxToHtml.ts b/src/utils/mdxToHtml.ts new file mode 100644 index 0000000..fdfffec --- /dev/null +++ b/src/utils/mdxToHtml.ts @@ -0,0 +1,62 @@ +import type { Node, Root, Text, Parent, RootContent } from 'mdast' +import { unified } from 'unified' +import remarkParse from 'remark-parse' +import remarkMDX from 'remark-mdx' +import remarkHtml from 'remark-html' + +function assertsType (val: unknown): asserts val is T {} + +const truncateMDXContent = (limit: number): ((x: Root) => void) => { + if (limit === -1) return () => {} + let charactersCount = 0 + let reachedLimit = false + + const truncateTree = (node: Node | Root): Node | null => { + if (reachedLimit) { + return null + } + + if (node.type === 'text') { + assertsType(node) + const remainingCharacters = limit - charactersCount + if (node.value.length > remainingCharacters) { + node.value = node.value.substring(0, remainingCharacters) + '...' + reachedLimit = true + } + charactersCount += node.value.length + } else if ('children' in node) { + assertsType(node) + node.children = node.children.map(truncateTree).filter(x => x != null) as RootContent[] + } + + return node + } + + return (tree: Root) => { + truncateTree(tree) + } +} + +const removeImportsAndExports = () => { + return (tree: Root) => { + tree.children = tree.children.filter((node: Node) => { + return node.type !== 'mdxjsEsm' && // import tags + node.type !== 'mdxJsxFlowElement' // custom components (Image, Pintora) + }) + } +} +export const convertMDXToHTML = async (mdxContent: string, maxChars?: number): Promise => { + try { + const file = await unified() + .use(remarkParse) // Parse the Markdown syntax + .use(remarkMDX) // Parse the MDX syntax + .use(removeImportsAndExports) + .use(truncateMDXContent, maxChars ?? -1) + .use(remarkHtml) // Convert to HTML + .process(mdxContent) // Process the MDX content + return String(file) + } catch (error) { + console.error('Error converting MDX to HTML:', error) + throw error + } +}