starter-blog/lib/mdx.js

138 lines
4.3 KiB
JavaScript
Raw Normal View History

2021-06-26 12:46:45 +02:00
import { bundleMDX } from 'mdx-bundler'
2021-01-09 10:50:45 +01:00
import fs from 'fs'
import matter from 'gray-matter'
import path from 'path'
import readingTime from 'reading-time'
2021-08-14 17:11:18 +02:00
import { visit } from 'unist-util-visit'
import getAllFilesRecursively from './utils/files'
2021-08-14 17:11:18 +02:00
// Remark packages
import remarkGfm from 'remark-gfm'
import remarkFootnotes from 'remark-footnotes'
import remarkMath from 'remark-math'
import remarkCodeTitles from './remark-code-title'
import remarkTocHeadings from './remark-toc-headings'
import remarkImgToJsx from './remark-img-to-jsx'
// Rehype packages
import rehypeSlug from 'rehype-slug'
import rehypeAutolinkHeadings from 'rehype-autolink-headings'
2021-08-14 17:11:18 +02:00
import rehypeKatex from 'rehype-katex'
import rehypeCitation from 'rehype-citation'
2021-08-14 17:11:18 +02:00
import rehypePrismPlus from 'rehype-prism-plus'
2021-01-09 10:50:45 +01:00
const root = process.cwd()
export function getFiles(type) {
const prefixPaths = path.join(root, 'data', type)
const files = getAllFilesRecursively(prefixPaths)
2021-05-11 16:34:26 +02:00
// Only want to return blog/path and ignore root, replace is needed to work on Windows
return files.map((file) => file.slice(prefixPaths.length + 1).replace(/\\/g, '/'))
2021-01-09 10:50:45 +01:00
}
2021-01-16 11:38:45 +01:00
export function formatSlug(slug) {
return slug.replace(/\.(mdx|md)/, '')
}
2021-01-09 10:50:45 +01:00
export function dateSortDesc(a, b) {
if (a > b) return -1
if (a < b) return 1
return 0
}
export async function getFileBySlug(type, slug) {
2021-05-26 13:20:24 +02:00
const mdxPath = path.join(root, 'data', type, `${slug}.mdx`)
const mdPath = path.join(root, 'data', type, `${slug}.md`)
2021-01-09 10:50:45 +01:00
const source = fs.existsSync(mdxPath)
? fs.readFileSync(mdxPath, 'utf8')
: fs.readFileSync(mdPath, 'utf8')
2021-06-26 12:46:45 +02:00
// https://github.com/kentcdodds/mdx-bundler#nextjs-esbuild-enoent
if (process.platform === 'win32') {
process.env.ESBUILD_BINARY_PATH = path.join(root, 'node_modules', 'esbuild', 'esbuild.exe')
2021-06-26 12:46:45 +02:00
} else {
process.env.ESBUILD_BINARY_PATH = path.join(root, 'node_modules', 'esbuild', 'bin', 'esbuild')
2021-06-26 12:46:45 +02:00
}
2021-08-06 16:13:30 +02:00
let toc = []
// Parsing frontmatter here to pass it in as options to rehype plugin
const { data: frontmatter } = matter(source)
const { code } = await bundleMDX({
source,
// mdx imports can be automatically source from the components directory
cwd: path.join(root, 'components'),
2021-06-26 12:46:45 +02:00
xdmOptions(options) {
// this is the recommended way to add custom remark/rehype plugins:
// The syntax might look weird, but it protects you in case we add/remove
// plugins in the future.
options.remarkPlugins = [
...(options.remarkPlugins ?? []),
2021-08-06 16:13:30 +02:00
[remarkTocHeadings, { exportRef: toc }],
2021-08-14 17:11:18 +02:00
remarkGfm,
remarkCodeTitles,
[remarkFootnotes, { inlineNotes: true }],
remarkMath,
remarkImgToJsx,
2021-06-26 12:46:45 +02:00
]
options.rehypePlugins = [
...(options.rehypePlugins ?? []),
rehypeSlug,
rehypeAutolinkHeadings,
2021-08-14 17:11:18 +02:00
rehypeKatex,
[
rehypeCitation,
{ bibliography: frontmatter?.bibliography, path: path.join(root, 'data') },
],
2021-08-14 17:11:18 +02:00
[rehypePrismPlus, { ignoreMissing: true }],
2021-06-26 12:46:45 +02:00
]
return options
2021-01-09 10:50:45 +01:00
},
esbuildOptions: (options) => {
options.loader = {
...options.loader,
'.js': 'jsx',
}
return options
},
2021-01-09 10:50:45 +01:00
})
return {
2021-06-26 12:46:45 +02:00
mdxSource: code,
2021-08-06 16:13:30 +02:00
toc,
2021-01-09 10:50:45 +01:00
frontMatter: {
2021-07-04 09:03:52 +02:00
readingTime: readingTime(code),
2021-01-09 10:50:45 +01:00
slug: slug || null,
2021-01-10 09:55:38 +01:00
fileName: fs.existsSync(mdxPath) ? `${slug}.mdx` : `${slug}.md`,
2021-07-04 09:03:52 +02:00
...frontmatter,
date: frontmatter.date ? new Date(frontmatter.date).toISOString() : null,
2021-01-09 10:50:45 +01:00
},
}
}
export async function getAllFilesFrontMatter(folder) {
const prefixPaths = path.join(root, 'data', folder)
const files = getAllFilesRecursively(prefixPaths)
2021-01-09 10:50:45 +01:00
2021-01-16 11:36:25 +01:00
const allFrontMatter = []
files.forEach((file) => {
2021-05-11 16:34:26 +02:00
// Replace is needed to work on Windows
const fileName = file.slice(prefixPaths.length + 1).replace(/\\/g, '/')
2021-06-18 12:12:12 +02:00
// Remove Unexpected File
if (path.extname(fileName) !== '.md' && path.extname(fileName) !== '.mdx') {
return
2021-06-18 12:12:12 +02:00
}
const source = fs.readFileSync(file, 'utf8')
const { data: frontmatter } = matter(source)
if (frontmatter.draft !== true) {
allFrontMatter.push({
...frontmatter,
slug: formatSlug(fileName),
date: frontmatter.date ? new Date(frontmatter.date).toISOString() : null,
})
2021-01-16 11:36:25 +01:00
}
})
2021-01-09 10:50:45 +01:00
return allFrontMatter.sort((a, b) => dateSortDesc(a.date, b.date))
}