feat(v2): create docusaurus-content-blog plugin draft

This commit is contained in:
Yangshun Tay 2019-02-24 23:11:15 -08:00
parent 211e04f409
commit e5b7daef33
8 changed files with 95 additions and 41 deletions

View file

@ -29,4 +29,12 @@ module.exports = {
indexName: 'docusaurus',
algoliaOptions: {},
},
plugins: [
{
name: 'docusaurus-content-blog',
options: {
include: ['*.md', '*.mdx'],
},
},
],
};

View file

@ -18,8 +18,8 @@ const REQUIRED_FIELDS = [
'headerIcon',
'organizationName',
'projectName',
'title',
'tagline',
'title',
'url',
];
@ -34,6 +34,7 @@ const OPTIONAL_FIELDS = [
'githubHost',
'highlight',
'markdownPlugins',
'plugins',
];
const DEFAULT_CONFIG = {
@ -94,12 +95,12 @@ function loadConfig(siteDir, deleteCache = true) {
}
config.headerLinks = headerLinks;
/*
User's own array of custom fields,
e.g: if they want to include some field so they can access it later from `props.siteConfig`
*/
// User's own array of custom fields/
// e.g: if they want to include some.field so they can access it later from `props.siteConfig`.
const {customFields = []} = config;
// TODO: Check that plugins mentioned exist.
// Don't allow unrecognized fields.
const allowedFields = [
...REQUIRED_FIELDS,

View file

@ -7,7 +7,6 @@
const fs = require('fs-extra');
const path = require('path');
const loadBlog = require('./blog');
const loadConfig = require('./config');
const loadDocs = require('./docs');
const loadEnv = require('./env');
@ -80,14 +79,27 @@ module.exports = async function load(siteDir) {
`export default ${JSON.stringify(pagesMetadatas, null, 2)};`,
);
// Blog.
const blogDir = path.resolve(siteDir, 'blog');
const blogMetadatas = await loadBlog({blogDir, env, siteConfig});
const contentsStore = {};
// Process plugins.
if (siteConfig.plugins) {
const context = {env, siteDir, siteConfig};
await Promise.all(
siteConfig.plugins.map(async ({name, options: opts}) => {
// TODO: Resolve using node_modules as well.
// eslint-disable-next-line
const plugin = require(path.resolve(__dirname, '../../plugins', name));
const pluginContent = await plugin.onLoadContent(opts, context);
const {options, contents} = pluginContent;
contentsStore[options.contentKey] = pluginContent;
await generate(
generatedFilesDir,
'blogMetadatas.js',
`export default ${JSON.stringify(blogMetadatas, null, 2)};`,
options.cachePath,
`export default ${JSON.stringify(contents, null, 2)};`,
);
}),
);
}
// Resolve outDir.
const outDir = path.resolve(siteDir, 'build');
@ -102,8 +114,6 @@ module.exports = async function load(siteDir) {
const props = {
siteConfig,
siteDir,
blogDir,
blogMetadatas,
docsDir,
docsMetadatas,
docsSidebars,
@ -117,6 +127,7 @@ module.exports = async function load(siteDir) {
versionedDir,
translatedDir,
generatedFilesDir,
contentsStore,
};
// Generate React Router Config.

View file

@ -11,7 +11,7 @@ async function genRoutesConfig({
siteConfig = {},
docsMetadatas = {},
pagesMetadatas = [],
blogMetadatas = [],
contentsStore = {},
}) {
const {docsUrl, baseUrl} = siteConfig;
function genDocsRoute(metadata) {
@ -138,7 +138,11 @@ async function genRoutesConfig({
`const routes = [
${pagesMetadatas.map(genPagesRoute).join(',')},
${docsRoutes},
${blogMetadatas.map(genBlogRoute).join(',')},
${
contentsStore.blog
? contentsStore.blog.contents.map(genBlogRoute).join(',')
: ''
},
${notFoundRoute}\n];\n` +
`export default routes;\n`
);

View file

@ -15,6 +15,7 @@ import DocusaurusContext from '@docusaurus/context';
function BlogPage(props) {
const context = useContext(DocusaurusContext);
console.log(context);
const {blogMetadatas, language, siteConfig = {}} = context;
const {baseUrl, favicon} = siteConfig;

View file

@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
const _ = require('lodash');
const path = require('path');
const staticSiteGenerator = require('static-site-generator-webpack-plugin');
const webpackNiceLog = require('webpack-nicelog');
@ -21,13 +22,18 @@ module.exports = function createServerConfig(props) {
// Workaround for Webpack 4 Bug (https://github.com/webpack/webpack/issues/6522)
config.output.globalObject('this');
const {siteConfig, blogMetadatas, docsMetadatas, pagesMetadatas} = props;
const {siteConfig, docsMetadatas, pagesMetadatas, contentsStore} = props;
// Static site generator webpack plugin.
const docsFlatMetadatas = Object.values(docsMetadatas);
const paths = [...blogMetadatas, ...docsFlatMetadatas, ...pagesMetadatas].map(
data => data.permalink,
);
// TODO: Generalize this into blog plugin.
const blogPermalinks = _.get(contentsStore, ['blog', 'contents'], []);
const paths = [
...blogPermalinks,
...docsFlatMetadatas,
...pagesMetadatas,
].map(data => data.permalink);
config.plugin('siteGenerator').use(staticSiteGenerator, [
{
entry: 'main',

View file

@ -8,7 +8,8 @@
const globby = require('globby');
const path = require('path');
const fs = require('fs-extra');
const {parse, idx, normalizeUrl} = require('./utils');
// TODO: Do not make it relative because plugins can be from node_modules.
const {parse, idx, normalizeUrl} = require('../lib/load/utils');
function fileToUrl(fileName) {
return fileName
@ -18,15 +19,29 @@ function fileToUrl(fileName) {
.replace(/\.md$/, '');
}
async function loadBlog({blogDir, env, siteConfig}) {
const blogFiles = await globby(['*.md'], {
const DEFAULT_OPTIONS = {
contentKey: 'blog',
path: 'blog', // Path to data on filesystem.
routeBasePath: 'blog', // URL Route.
include: ['*.md'], // Extensions to include.
pageCount: 10, // How many entries per page.
cachePath: 'blogMetadata.js',
};
async function onLoadContent(opts, context) {
const options = {...DEFAULT_OPTIONS, ...opts};
const {env, siteConfig, siteDir} = context;
const {pageCount, path: filePath, routeBasePath} = options;
const blogDir = path.resolve(siteDir, filePath);
const {baseUrl} = siteConfig;
const blogFiles = await globby(options.include, {
cwd: blogDir,
});
const {baseUrl} = siteConfig;
// Prepare metadata container.
const blogMetadatas = [];
const blogMetadata = [];
// Language for each blog page.
const defaultLangTag = idx(env, ['translation', 'defaultLanguage', 'tag']);
@ -36,7 +51,7 @@ async function loadBlog({blogDir, env, siteConfig}) {
const source = path.join(blogDir, relativeSource);
const blogFileName = path.basename(relativeSource);
// Extract, YYYY, MM, DD from the file name
// Extract, YYYY, MM, DD from the file name.
const filePathDateArr = blogFileName.split('-');
const date = new Date(
`${filePathDateArr[0]}-${filePathDateArr[1]}-${
@ -47,37 +62,45 @@ async function loadBlog({blogDir, env, siteConfig}) {
const fileString = await fs.readFile(source, 'utf-8');
const {metadata: rawMetadata} = parse(fileString);
const metadata = {
permalink: normalizeUrl([baseUrl, `blog`, fileToUrl(blogFileName)]),
permalink: normalizeUrl([
baseUrl,
routeBasePath,
fileToUrl(blogFileName),
]),
source,
...rawMetadata,
date,
language: defaultLangTag,
};
blogMetadatas.push(metadata);
blogMetadata.push(metadata);
}),
);
blogMetadatas.sort((a, b) => a.date - b.date);
blogMetadata.sort((a, b) => a.date - b.date);
// Blog page handling. Example: `/blog`, `/blog/page1`, `/blog/page2`
const perPage = 10;
const numOfBlog = blogMetadatas.length;
const numberOfPage = Math.ceil(numOfBlog / perPage);
const basePageUrl = path.join(baseUrl, 'blog');
const numOfBlog = blogMetadata.length;
const numberOfPage = Math.ceil(numOfBlog / pageCount);
const basePageUrl = path.join(baseUrl, routeBasePath);
// eslint-disable-next-line
for (let page = 0; page < numberOfPage; page++) {
blogMetadatas.push({
blogMetadata.push({
permalink: normalizeUrl([
basePageUrl,
`${page > 0 ? `page${page + 1}` : ''}`,
]),
language: defaultLangTag,
isBlogPage: true,
posts: blogMetadatas.slice(page * perPage, (page + 1) * perPage),
posts: blogMetadata.slice(page * pageCount, (page + 1) * pageCount),
});
}
return blogMetadatas;
return {
contents: blogMetadata,
options,
};
}
module.exports = loadBlog;
module.exports = {
onLoadContent,
};

View file

@ -66,7 +66,7 @@ Object {
expect(() => {
loadConfig(siteDir);
}).toThrowErrorMatchingInlineSnapshot(
`"The required field(s) 'baseUrl', 'favicon', 'headerLinks', 'headerIcon', 'organizationName', 'projectName', 'title', 'tagline', 'url' are missing from docusaurus.config.js"`,
`"The required field(s) 'baseUrl', 'favicon', 'headerLinks', 'headerIcon', 'organizationName', 'projectName', 'tagline', 'title', 'url' are missing from docusaurus.config.js"`,
);
});
});