140
packages/twenty-website/src/app/_server-utils/get-posts.tsx
Normal file
140
packages/twenty-website/src/app/_server-utils/get-posts.tsx
Normal file
@ -0,0 +1,140 @@
|
||||
import { ReactElement } from 'react';
|
||||
import { toc } from '@jsdevtools/rehype-toc';
|
||||
import fs from 'fs';
|
||||
import { compileMDX } from 'next-mdx-remote/rsc';
|
||||
import path from 'path';
|
||||
import rehypeSlug from 'rehype-slug';
|
||||
import gfm from 'remark-gfm';
|
||||
|
||||
interface ItemInfo {
|
||||
title: string;
|
||||
position?: number;
|
||||
path: string;
|
||||
type: 'file' | 'directory';
|
||||
icon?: string;
|
||||
info?: string;
|
||||
image?: string;
|
||||
}
|
||||
|
||||
export interface FileContent {
|
||||
content: ReactElement;
|
||||
itemInfo: ItemInfo;
|
||||
}
|
||||
|
||||
export interface Directory {
|
||||
[key: string]: FileContent | Directory | ItemInfo;
|
||||
itemInfo: ItemInfo;
|
||||
}
|
||||
|
||||
async function getFiles(
|
||||
filePath: string,
|
||||
basePath: string,
|
||||
position: number = 0,
|
||||
): Promise<Directory> {
|
||||
const entries = fs.readdirSync(filePath, { withFileTypes: true });
|
||||
|
||||
const urlpath = path.toString().split(basePath);
|
||||
const pathName = urlpath.length > 1 ? urlpath[1] : path.basename(filePath);
|
||||
|
||||
const directory: Directory = {
|
||||
itemInfo: {
|
||||
title: path.basename(filePath),
|
||||
position,
|
||||
type: 'directory',
|
||||
path: pathName,
|
||||
},
|
||||
};
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
directory[entry.name] = await getFiles(
|
||||
path.join(filePath, entry.name),
|
||||
basePath,
|
||||
position++,
|
||||
);
|
||||
} else if (entry.isFile() && path.extname(entry.name) === '.mdx') {
|
||||
const { content, frontmatter } = await compileMDXFile(
|
||||
path.join(filePath, entry.name),
|
||||
);
|
||||
directory[entry.name] = {
|
||||
content,
|
||||
itemInfo: {
|
||||
...frontmatter,
|
||||
type: 'file',
|
||||
path: pathName + '/' + entry.name.replace(/\.mdx$/, ''),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
async function parseFrontMatterAndCategory(
|
||||
directory: Directory,
|
||||
dirPath: string,
|
||||
): Promise<Directory> {
|
||||
const parsedDirectory: Directory = {
|
||||
itemInfo: directory.itemInfo,
|
||||
};
|
||||
|
||||
for (const entry in directory) {
|
||||
if (entry !== 'itemInfo' && directory[entry] instanceof Object) {
|
||||
parsedDirectory[entry] = await parseFrontMatterAndCategory(
|
||||
directory[entry] as Directory,
|
||||
path.join(dirPath, entry),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const categoryPath = path.join(dirPath, '_category_.json');
|
||||
|
||||
if (fs.existsSync(categoryPath)) {
|
||||
const categoryJson: ItemInfo = JSON.parse(
|
||||
fs.readFileSync(categoryPath, 'utf8'),
|
||||
);
|
||||
parsedDirectory.itemInfo = categoryJson;
|
||||
}
|
||||
|
||||
return parsedDirectory;
|
||||
}
|
||||
|
||||
export async function compileMDXFile(filePath: string, addToc: boolean = true) {
|
||||
const fileContent = fs.readFileSync(filePath, 'utf8');
|
||||
const compiled = await compileMDX<{ title: string; position?: number }>({
|
||||
source: fileContent,
|
||||
options: {
|
||||
parseFrontmatter: true,
|
||||
mdxOptions: {
|
||||
remarkPlugins: [gfm],
|
||||
rehypePlugins: [rehypeSlug, ...(addToc ? [toc] : [])],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return compiled;
|
||||
}
|
||||
|
||||
export async function getPosts(basePath: string): Promise<Directory> {
|
||||
const postsDirectory = path.join(process.cwd(), basePath);
|
||||
const directory = await getFiles(postsDirectory, basePath);
|
||||
return parseFrontMatterAndCategory(directory, postsDirectory);
|
||||
}
|
||||
|
||||
export async function getPost(
|
||||
slug: string,
|
||||
basePath: string,
|
||||
): Promise<FileContent | null> {
|
||||
const postsDirectory = path.join(process.cwd(), basePath);
|
||||
const filePath = path.join(postsDirectory, `${slug}.mdx`);
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return null;
|
||||
}
|
||||
const { content, frontmatter } = await compileMDXFile(filePath, true);
|
||||
|
||||
return {
|
||||
content,
|
||||
itemInfo: { ...frontmatter, type: 'file', path: slug },
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user