|
|
|
@ -23,38 +23,27 @@ marked.use({
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export async function readBlogPosts(
|
|
|
|
|
condition?: (body: BlogPost) => boolean,
|
|
|
|
|
render = true,
|
|
|
|
|
includeContent = true
|
|
|
|
|
) {
|
|
|
|
|
async function readBlogPosts() {
|
|
|
|
|
const files = await fs.readdir(dir);
|
|
|
|
|
const readMD = files.filter((file) => file.endsWith('.md'));
|
|
|
|
|
const readFiles = [];
|
|
|
|
|
|
|
|
|
|
for (const file of readMD) {
|
|
|
|
|
const post = await readBlogPost(
|
|
|
|
|
file.replace('.md', ''),
|
|
|
|
|
condition,
|
|
|
|
|
render,
|
|
|
|
|
includeContent
|
|
|
|
|
);
|
|
|
|
|
const post = await readBlogPost(file.replace('.md', ''));
|
|
|
|
|
if (!post) continue;
|
|
|
|
|
readFiles.push(post);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readFiles.sort((a, b) =>
|
|
|
|
|
new Date(b.date)
|
|
|
|
|
.toISOString()
|
|
|
|
|
.localeCompare(new Date(a.date).toISOString(), 'en', { numeric: true })
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return readFiles;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function readBlogPost(
|
|
|
|
|
slug: string,
|
|
|
|
|
condition?: (body: BlogPost) => boolean,
|
|
|
|
|
render = true,
|
|
|
|
|
includeContent = true
|
|
|
|
|
): Promise<BlogPost[]> {
|
|
|
|
|
async function readBlogPost(slug: string): Promise<BlogPost[]> {
|
|
|
|
|
const decoded = decodeURIComponent(slug);
|
|
|
|
|
if (!slug || decoded.includes('/') || decoded.includes('.'))
|
|
|
|
|
throw new Error('Invalid post slug');
|
|
|
|
@ -64,30 +53,74 @@ export async function readBlogPost(
|
|
|
|
|
const read = await fs.readFile(mdpath, { encoding: 'utf-8' });
|
|
|
|
|
|
|
|
|
|
const { header: parsedHeader, length: headerLength } = await readHeader(read);
|
|
|
|
|
const renderedMd = render
|
|
|
|
|
? await marked(read.substring(headerLength), { async: true })
|
|
|
|
|
: undefined;
|
|
|
|
|
const renderedMd = await marked(read.substring(headerLength), {
|
|
|
|
|
async: true,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const { year, month, day } = getDateObject(parsedHeader);
|
|
|
|
|
const content = {
|
|
|
|
|
...parsedHeader,
|
|
|
|
|
file,
|
|
|
|
|
slug,
|
|
|
|
|
fullSlug: `${year}/${month}/${day}/${slug}`,
|
|
|
|
|
markdown: includeContent ? read.substring(headerLength) : undefined,
|
|
|
|
|
markdown: read.substring(headerLength),
|
|
|
|
|
html: renderedMd,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (condition) {
|
|
|
|
|
if (!condition(content)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return content;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let blogPostCache: BlogPost[] = [];
|
|
|
|
|
export const blogPostList = async () => {
|
|
|
|
|
if (!blogPostCache.length) {
|
|
|
|
|
blogPostCache = await readBlogPosts();
|
|
|
|
|
}
|
|
|
|
|
return blogPostCache;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getFilteredBlogPosts = async (
|
|
|
|
|
condition?: (body: BlogPost) => boolean,
|
|
|
|
|
htmlContent = true,
|
|
|
|
|
includeMarkdown = true
|
|
|
|
|
): Promise<BlogPost[]> => {
|
|
|
|
|
const posts = await blogPostList();
|
|
|
|
|
return posts
|
|
|
|
|
.filter((item) => (condition ? condition(item) : true))
|
|
|
|
|
.map((item) => ({
|
|
|
|
|
...item,
|
|
|
|
|
html: htmlContent ? item.html : undefined,
|
|
|
|
|
markdown: includeMarkdown ? item.markdown : undefined,
|
|
|
|
|
}));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getBlogPost = async (
|
|
|
|
|
slug: string,
|
|
|
|
|
htmlContent = true,
|
|
|
|
|
includeMarkdown = true
|
|
|
|
|
): Promise<BlogPost> => {
|
|
|
|
|
const posts = await blogPostList();
|
|
|
|
|
const item = posts.find((item) => item.slug === slug);
|
|
|
|
|
if (!item) return;
|
|
|
|
|
|
|
|
|
|
const index = posts.indexOf(item);
|
|
|
|
|
const next = index > 0 ? posts[index - 1] : undefined;
|
|
|
|
|
const prev = posts[index + 1];
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...item,
|
|
|
|
|
html: htmlContent ? item.html : undefined,
|
|
|
|
|
markdown: includeMarkdown ? item.markdown : undefined,
|
|
|
|
|
next: next
|
|
|
|
|
? { title: next.title, slug: next.slug, fullSlug: next.fullSlug }
|
|
|
|
|
: undefined,
|
|
|
|
|
prev: prev
|
|
|
|
|
? { title: prev.title, slug: prev.slug, fullSlug: prev.fullSlug }
|
|
|
|
|
: undefined,
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export async function getTags(): Promise<BlogPostTag[]> {
|
|
|
|
|
const posts = await readBlogPosts(undefined, false, false);
|
|
|
|
|
const posts = await getFilteredBlogPosts(undefined, false, false);
|
|
|
|
|
const obj: BlogPostTag[] = [];
|
|
|
|
|
|
|
|
|
|
for (const post of posts) {
|
|
|
|
@ -113,7 +146,7 @@ export async function getTags(): Promise<BlogPostTag[]> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function getArchiveTree(condition?: (body: BlogPost) => boolean) {
|
|
|
|
|
const posts = await readBlogPosts(condition, false, false);
|
|
|
|
|
const posts = await getFilteredBlogPosts(condition, false, false);
|
|
|
|
|
const obj = {};
|
|
|
|
|
|
|
|
|
|
for (const post of posts) {
|
|
|
|
|