import { defineDocumentType, ComputedFields, makeSource } from 'contentlayer/source-files' import readingTime from 'reading-time' import path from 'path' // Remark packages import remarkGfm from 'remark-gfm' import remarkMath from 'remark-math' import { remarkExtractFrontmatter, remarkCodeTitles, remarkImgToJsx, extractTocHeadings, } from 'pliny/mdx-plugins.js' // Rehype packages import rehypeSlug from 'rehype-slug' import rehypeAutolinkHeadings from 'rehype-autolink-headings' import rehypeKatex from 'rehype-katex' import rehypeCitation from 'rehype-citation' import rehypePrismPlus from 'rehype-prism-plus' import rehypePresetMinify from 'rehype-preset-minify' import siteMetadata from './data/siteMetadata' const root = process.cwd() const computedFields: ComputedFields = { readingTime: { type: 'json', resolve: (doc) => readingTime(doc.body.raw) }, slug: { type: 'string', resolve: (doc) => doc._raw.flattenedPath.replace(/^.+?(\/)/, ''), }, path: { type: 'string', resolve: (doc) => doc._raw.flattenedPath, }, filePath: { type: 'string', resolve: (doc) => doc._raw.sourceFilePath, }, toc: { type: 'string', resolve: (doc) => extractTocHeadings(doc.body.raw) }, } export const Blog = defineDocumentType(() => ({ name: 'Blog', filePathPattern: 'blog/**/*.mdx', contentType: 'mdx', fields: { title: { type: 'string', required: true }, date: { type: 'date', required: true }, tags: { type: 'list', of: { type: 'string' } }, lastmod: { type: 'date' }, draft: { type: 'boolean' }, summary: { type: 'string' }, images: { type: 'list', of: { type: 'string' } }, authors: { type: 'list', of: { type: 'string' } }, layout: { type: 'string' }, bibliography: { type: 'string' }, canonicalUrl: { type: 'string' }, }, computedFields: { ...computedFields, structuredData: { type: 'json', resolve: (doc) => ({ '@context': 'https://schema.org', '@type': 'BlogPosting', headline: doc.title, datePublished: doc.date, dateModified: doc.lastmod || doc.date, description: doc.summary, image: doc.images ? doc.images[0] : siteMetadata.socialBanner, url: `${siteMetadata.siteUrl}/${doc._raw.flattenedPath}`, author: doc.authors, }), }, }, })) export const Authors = defineDocumentType(() => ({ name: 'Authors', filePathPattern: 'authors/**/*.mdx', contentType: 'mdx', fields: { name: { type: 'string', required: true }, avatar: { type: 'string' }, occupation: { type: 'string' }, company: { type: 'string' }, email: { type: 'string' }, twitter: { type: 'string' }, linkedin: { type: 'string' }, github: { type: 'string' }, layout: { type: 'string' }, }, computedFields, })) export default makeSource({ contentDirPath: 'data', documentTypes: [Blog, Authors], mdx: { cwd: process.cwd(), remarkPlugins: [ remarkExtractFrontmatter, remarkGfm, remarkCodeTitles, remarkMath, remarkImgToJsx, ], rehypePlugins: [ rehypeSlug, rehypeAutolinkHeadings, rehypeKatex, [rehypeCitation, { path: path.join(root, 'data') }], [rehypePrismPlus, { defaultLanguage: 'js' }], rehypePresetMinify, ], }, })