mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-09 23:27:28 +02:00
feat(v2): create docusaurus-content-blog plugin draft
This commit is contained in:
parent
211e04f409
commit
e5b7daef33
8 changed files with 95 additions and 41 deletions
|
@ -29,4 +29,12 @@ module.exports = {
|
|||
indexName: 'docusaurus',
|
||||
algoliaOptions: {},
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
name: 'docusaurus-content-blog',
|
||||
options: {
|
||||
include: ['*.md', '*.mdx'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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});
|
||||
await generate(
|
||||
generatedFilesDir,
|
||||
'blogMetadatas.js',
|
||||
`export default ${JSON.stringify(blogMetadatas, null, 2)};`,
|
||||
);
|
||||
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,
|
||||
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.
|
||||
|
|
|
@ -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`
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue