222 lines
5.1 KiB
JavaScript
222 lines
5.1 KiB
JavaScript
import { defineDocumentType, makeSource } from 'contentlayer/source-files';
|
|
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
|
import rehypeSlug from 'rehype-slug';
|
|
|
|
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000';
|
|
|
|
export const Post = defineDocumentType(() => ({
|
|
name: 'Post',
|
|
filePathPattern: `posts/*.mdx`,
|
|
contentType: 'mdx',
|
|
fields: {
|
|
title: {
|
|
type: 'string',
|
|
description: 'The title of the post',
|
|
required: true,
|
|
},
|
|
date: {
|
|
type: 'date',
|
|
description: 'The date of the post',
|
|
required: true,
|
|
},
|
|
live: {
|
|
type: 'boolean',
|
|
description: 'Whether the post is live or not',
|
|
required: true,
|
|
default: false,
|
|
},
|
|
image: {
|
|
type: 'string',
|
|
description: 'The path to the cover image',
|
|
},
|
|
description: {
|
|
type: 'string',
|
|
description: 'The description of the post',
|
|
},
|
|
},
|
|
computedFields: {
|
|
url: {
|
|
type: 'string',
|
|
resolve: (post) => `/blog/${getSlug(post._raw.sourceFileName)}`,
|
|
},
|
|
readingTime: {
|
|
type: 'number',
|
|
resolve: (post) => calculateReadingTime(post.body.raw),
|
|
},
|
|
slug: {
|
|
type: 'string',
|
|
resolve: (post) => getSlug(post._raw.sourceFileName),
|
|
},
|
|
structuredData: {
|
|
type: 'object',
|
|
resolve: (doc) => ({
|
|
'@context': 'https://schema.org',
|
|
'@type': 'BlogPosting',
|
|
headline: doc.title,
|
|
datePublished: doc.date,
|
|
dateModified: doc.date,
|
|
description: doc.description,
|
|
image: [siteUrl, doc.image].join(''),
|
|
url: [siteUrl, 'blog', doc._raw.flattenedPath].join('/'),
|
|
author: {
|
|
'@type': 'Organization',
|
|
name: `Makerkit`,
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
}));
|
|
|
|
export const DocumentationPage = defineDocumentType(() => ({
|
|
name: 'DocumentationPage',
|
|
filePathPattern: `docs/**/*.mdx`,
|
|
contentType: 'mdx',
|
|
fields: {
|
|
title: {
|
|
type: 'string',
|
|
description: 'The title of the post',
|
|
required: true,
|
|
},
|
|
label: {
|
|
type: 'string',
|
|
description: 'The label of the page in the sidebar',
|
|
required: true,
|
|
},
|
|
cardCTA: {
|
|
type: 'string',
|
|
description: 'The label of the CTA link on the card',
|
|
required: false,
|
|
},
|
|
description: {
|
|
type: 'string',
|
|
description: 'The description of the post',
|
|
},
|
|
show_child_cards: {
|
|
type: 'boolean',
|
|
default: false,
|
|
},
|
|
collapsible: {
|
|
type: 'boolean',
|
|
required: false,
|
|
default: false,
|
|
},
|
|
collapsed: {
|
|
type: 'boolean',
|
|
required: false,
|
|
default: false,
|
|
},
|
|
},
|
|
computedFields: {
|
|
url: {
|
|
type: 'string',
|
|
resolve: (post) => `/blog/${getSlug(post._raw.sourceFileName)}`,
|
|
},
|
|
readingTime: {
|
|
type: 'number',
|
|
resolve: (post) => calculateReadingTime(post.body.raw),
|
|
},
|
|
slug: {
|
|
type: 'string',
|
|
resolve: (post) => getSlug(post._raw.sourceFileName),
|
|
},
|
|
structuredData: {
|
|
type: 'object',
|
|
resolve: (doc) => ({
|
|
'@context': 'https://schema.org',
|
|
'@type': 'LearningResource',
|
|
headline: doc.title,
|
|
datePublished: doc.date,
|
|
dateModified: doc.date,
|
|
description: doc.description,
|
|
image: [siteUrl, doc.image].join(''),
|
|
url: [siteUrl, 'blog', doc._raw.flattenedPath].join('/'),
|
|
author: {
|
|
'@type': 'Organization',
|
|
name: `Makerkit`,
|
|
},
|
|
}),
|
|
},
|
|
path: {
|
|
type: 'string',
|
|
resolve: (doc) => {
|
|
if (doc._id.startsWith('docs/index.md')) {
|
|
return '/docs';
|
|
}
|
|
|
|
return urlFromFilePath(doc);
|
|
},
|
|
},
|
|
pathSegments: {
|
|
type: 'json',
|
|
resolve: (doc) => getPathSegments(doc).map(getMetaFromFolderName),
|
|
},
|
|
resolvedPath: {
|
|
type: 'string',
|
|
resolve: (doc) => {
|
|
return getPathSegments(doc)
|
|
.map(getMetaFromFolderName)
|
|
.map(({ pathName }) => pathName)
|
|
.join('/');
|
|
},
|
|
},
|
|
},
|
|
}));
|
|
|
|
export default makeSource({
|
|
contentDirPath: './content',
|
|
documentTypes: [Post, DocumentationPage],
|
|
mdx: {
|
|
remarkPlugins: [],
|
|
rehypePlugins: [
|
|
rehypeSlug,
|
|
[
|
|
rehypeAutolinkHeadings,
|
|
{
|
|
properties: {
|
|
className: ['anchor'],
|
|
},
|
|
},
|
|
],
|
|
],
|
|
},
|
|
});
|
|
|
|
function calculateReadingTime(content) {
|
|
const wordsPerMinute = 235;
|
|
const numberOfWords = content.split(/\s/g).length;
|
|
const minutes = numberOfWords / wordsPerMinute;
|
|
|
|
return Math.ceil(minutes);
|
|
}
|
|
|
|
function getSlug(fileName) {
|
|
return fileName.replace('.mdx', '');
|
|
}
|
|
|
|
function urlFromFilePath(doc) {
|
|
let urlPath = doc._raw.flattenedPath.replace(/^app\/?/, '/');
|
|
|
|
if (!urlPath.startsWith('/')) {
|
|
urlPath = `/${urlPath}`;
|
|
}
|
|
|
|
return urlPath;
|
|
}
|
|
|
|
function getMetaFromFolderName(dirName) {
|
|
const re = /^((\d+)-)?(.*)$/;
|
|
const [, , orderStr, pathName] = dirName.match(re) ?? [];
|
|
const order = orderStr ? parseInt(orderStr) : 0;
|
|
|
|
return { order, pathName };
|
|
}
|
|
|
|
function getPathSegments(doc) {
|
|
return (
|
|
urlFromFilePath(doc)
|
|
.split('/')
|
|
// skip `/docs` prefix
|
|
.slice(2)
|
|
);
|
|
}
|