Cleanup
This commit is contained in:
221
apps/web/contentlayer.config.js
Normal file
221
apps/web/contentlayer.config.js
Normal file
@@ -0,0 +1,221 @@
|
||||
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)
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user