plugins/blog-plugin.js (305 lines of code) (raw):
const path = require("path");
const blogPluginExports = require("@docusaurus/plugin-content-blog");
const utils = require("@docusaurus/utils");
const lodash = require("lodash");
const { generateCategoryPath } = require('../src/utils/index');
const defaultBlogPlugin = blogPluginExports.default;
// get featured post list
function getFeaturedList ({
blogPosts, // all posts data
}) {
const result = blogPosts.filter((post) => post.metadata?.frontMatter.featured).slice(0, 5)?.map((post) => {
return {
...post.metadata?.frontMatter,
tags: post.metadata.tags,
permalink: post.metadata.permalink,
}
});
return result;
}
function getAllCategories (allBlogPosts) {
const categories = allBlogPosts.map((post) => {
const label = post.metadata.frontMatter.category;
return {
label,
permalink: generateCategoryPath(label),
count: allBlogPosts.filter((v) => v.metadata.frontMatter.category === label).length,
};
});
return lodash.uniqBy(categories, 'label');
}
function getAllTags(blogTags){
const tagsProp = Object.values(blogTags).map((tag) => ({
label: tag.label,
permalink: tag.permalink,
count: tag.items.length,
}));
return Array.from(new Set(tagsProp));
}
// paginate blog posts
function paginateBlogPosts({
filtedBlogPosts,
basePageUrl,
blogTitle,
blogDescription,
postsPerPageOption,
}) {
const totalCount = filtedBlogPosts.length;
const postsPerPage =
postsPerPageOption === "ALL" ? totalCount : postsPerPageOption;
const numberOfPages = Math.ceil(totalCount / postsPerPage);
const pages = [];
function permalink(page) {
return page > 0
? utils.normalizeUrl([basePageUrl, `page/${page + 1}`])
: basePageUrl;
}
for (let page = 0; page < numberOfPages; page += 1) {
pages.push({
items: filtedBlogPosts
.slice(page * postsPerPage, (page + 1) * postsPerPage)
.map((item) => item.id),
metadata: {
permalink: permalink(page),
page: page + 1,
postsPerPage,
totalPages: numberOfPages,
totalCount,
previousPage: page !== 0 ? permalink(page - 1) : '',
nextPage:
page < numberOfPages - 1 ? permalink(page + 1) : '',
blogDescription,
blogTitle,
},
});
}
return pages;
}
// related posts by tag
function getReletadPosts(allBlogPosts, metadata) {
const relatedFourPosts = allBlogPosts.filter(
(post) =>
post.metadata.frontMatter.tags?.some((tag) =>
metadata.frontMatter.tags?.includes(tag),
) && post.metadata.title !== metadata.title,
).slice(0, 4);
const filteredPostInfos = relatedFourPosts.map((post) => {
return {
content: {
frontMatter: {
image: post.metadata.frontMatter.image,
category: post.metadata.frontMatter.category,
},
metadata: post.metadata,
}
};
});
return filteredPostInfos;
}
// get i18n router base path
function generateI18nRoutePath(i18nConfig, path) {
const {currentLocale, defaultLocale} = i18nConfig;
return currentLocale === defaultLocale ? path : `/${currentLocale}${path}`;
}
const pluginDataDirRoot = path.join(
".docusaurus",
"docusaurus-plugin-content-blog",
);
const aliasedSource = (source) =>
`~blog/${utils.posixPath(path.relative(pluginDataDirRoot, source))}`;
async function blogPluginExtended(...pluginArgs) {
const blogPluginInstance = await defaultBlogPlugin(...pluginArgs);
const { blogTitle, blogDescription, postsPerPage } = pluginArgs[1];
const { i18n } = pluginArgs[0];
console.log('siteConfig NODE_ENV production', process.env.NODE_ENV);
return {
// Add all properties of the default blog plugin so existing functionality is preserved
...blogPluginInstance,
/**
* Override the default `contentLoaded` hook to access blog posts data
*/
async contentLoaded(data) {
// Get the 5 latest blog posts
const { content: blogContents, actions } = data;
const {
blogPosts: allBlogPosts,
blogTags,
blogTagsListPath,
} = blogContents;
const { createData, addRoute } = actions;
const removeDraftPosts = allBlogPosts.filter((post) => !post.metadata.frontMatter?.draft);
// Fecommend posts list
const featuredPosts = getFeaturedList({blogPosts: removeDraftPosts});
const featuredPostsJson = await createData(
`${utils.docuHash('featuredPosts')}.json`,
JSON.stringify(featuredPosts, null, 2)
);
// filted reommend posts
const filtedBlogPosts = removeDraftPosts.filter((post) => {
return !featuredPosts.find((v) => v.permalink === post.metadata.permalink);
});
// Blog Home page pagenation
const paginateBlogs = paginateBlogPosts({
filtedBlogPosts,
basePageUrl: "/blog",
blogTitle,
blogDescription,
postsPerPageOption: postsPerPage,
});
// Tags list
const tags = getAllTags(blogTags);
const tagsJson = await createData(
`${utils.docuHash('tags')}.json`,
JSON.stringify(tags, null, 2)
);
// global meta data
const globalMeta = {
blogTitle,
blogDescription,
}
const globalMetaJson = await createData(
`${utils.docuHash('globalMeta')}.json`,
JSON.stringify(globalMeta, null, 2)
);
const blogItemsToMetadata = {};
function blogPostItemsModule(items) {
return items.map((postId) => {
const blogPostMetadata = blogItemsToMetadata[postId];
return {
content: {
__import: true,
path: blogPostMetadata?.source,
query: {
truncated: true,
},
},
};
});
}
// Create routes for blog details page.
await Promise.all(
allBlogPosts.map(async (blogPostItem) => {
const { id, metadata } = blogPostItem;
const relatedPosts = getReletadPosts(
removeDraftPosts,
metadata,
);
const relatedList = await createData(
`realted-${utils.docuHash(id)}.json`,
JSON.stringify(relatedPosts, null, 2),
)
addRoute({
path: metadata.permalink,
component: "@site/src/components/blog/BlogDetailPage",
exact: true,
modules: {
relatedList,
globalMeta: globalMetaJson,
content: metadata.source,
},
});
blogItemsToMetadata[id] = metadata;
}),
);
// Categoery List
const categories = getAllCategories(removeDraftPosts);
const categoriesJson = await createData(
`${utils.docuHash('categories')}.json`,
JSON.stringify(categories, null, 2)
);
// Create routes for blog categories post list entries.
categories.map(async (category) => {
const categoryBlogs = removeDraftPosts.filter((post) => post.metadata.frontMatter.category === category.label);
const categoryListPaginated = paginateBlogPosts({
filtedBlogPosts: categoryBlogs,
basePageUrl: generateCategoryPath(category.label),
blogTitle,
blogDescription,
postsPerPageOption: postsPerPage,
});
const categoryProp = {
label: category.label,
count: category.count,
permalink: generateCategoryPath(category.label),
};
const categoryPropPath = await createData(
`${utils.docuHash(categoryProp.label)}.json`,
JSON.stringify(categoryProp, null, 2),
);
categoryListPaginated.map(async (categoryListPage) => {
const { metadata, items } = categoryListPage;
const { permalink } = metadata;
const categoryMetadataPath = await createData(
`category-metadata-${utils.docuHash(permalink)}.json`,
JSON.stringify(metadata, null, 2),
)
addRoute({
path: generateI18nRoutePath(i18n, permalink),
component: "@site/src/components/blog/BlogCategoryPostsPage",
exact: true,
modules: {
items: blogPostItemsModule(items),
category: categoryPropPath,
categoriyList: categoriesJson,
listMetadata: aliasedSource(categoryMetadataPath),
},
});
});
});
// Create routes for blog's paginated list entries.
await Promise.all(
paginateBlogs.map(async (listPage, index) => {
const { metadata, items } = listPage;
const { permalink } = metadata;
const pageMetadataPath = await createData(
`home-${utils.docuHash(permalink)}.json`,
JSON.stringify(metadata, null, 2),
);
addRoute({
path: generateI18nRoutePath(i18n, permalink),
exact: true,
component: "@site/src/components/blog/BlogHome",
modules: {
featuredPosts: permalink === '/blog' ? featuredPostsJson : [],
categoyList: categoriesJson,
blogList: blogPostItemsModule([...items]),
metadata: pageMetadataPath,
}
})
}),
);
// Create routes for blog tags post list entries.
async function createTagPostsListPage(tag) {
await Promise.all(
tag.pages.map(async (blogPaginated) => {
const { metadata, items } = blogPaginated;
const tagProp = {
label: tag.label,
permalink: tag.permalink,
allTagsPath: blogTagsListPath,
count: tag.items.length,
};
const tagPropPath = await createData(
`${utils.docuHash(metadata.permalink)}.json`,
JSON.stringify(tagProp, null, 2),
);
const listMetadataPath = await createData(
`${utils.docuHash(metadata.permalink)}-list.json`,
JSON.stringify(metadata, null, 2),
);
addRoute({
path: metadata.permalink,
component: "@site/src/components/blog/BlogTagsPostsPage",
exact: true,
modules: {
items: blogPostItemsModule(items),
tag: aliasedSource(tagPropPath),
tags: tagsJson,
listMetadata: aliasedSource(listMetadataPath),
},
});
}),
);
}
await Promise.all(
Object.values(blogTags).map(createTagPostsListPage),
);
// Call the default overridden `contentLoaded` implementation
return blogPluginInstance.contentLoaded({content: blogContents, actions});
},
};
}
module.exports = {
...blogPluginExports,
default: blogPluginExtended,
};