Skip to content

Commit

Permalink
render excerpts of MDX differently
Browse files Browse the repository at this point in the history
  • Loading branch information
Timothée Rebours committed Feb 22, 2024
1 parent 6a63cf9 commit 9f05a2f
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 30 deletions.
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 3 additions & 2 deletions src/pages/[lang]/blog/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -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 } }))
Expand All @@ -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)
---

Expand All @@ -45,7 +46,7 @@ const formatDate = useFormatDate(lang)
</div>
<div class="width-full aspect-video overflow-hidden relative p-6">
<h2 class="text-xl font-medium mb-4">{post.data.title}</h2>
<div class="italic text-justify mb-4 line-clamp-3 px-1 text-ellipsis overflow-hidden"><post.Component/></div>
<div class="italic text-justify mb-4 line-clamp-3 px-1 text-ellipsis overflow-hidden"><Fragment set:html={post.html}/></div>
<div class="absolute bottom-6 right-6 text-sm text-gray-600">
<div class="first-letter:uppercase" title={formatDateAbsolute(post.data.date)}>{formatDate(post.data.date)}</div>
</div>
Expand Down
29 changes: 1 addition & 28 deletions src/pages/[lang]/rss.xml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> => {
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
Expand Down
62 changes: 62 additions & 0 deletions src/utils/mdxToHtml.ts
Original file line number Diff line number Diff line change
@@ -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<T> (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<Text>(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<Parent>(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<string> => {
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
}
}

0 comments on commit 9f05a2f

Please sign in to comment.