mirror of
https://github.com/facebook/docusaurus.git
synced 2025-08-03 08:49:51 +02:00
feat(v2): implement blog (#1062)
* feat(v2): implement blog * expect flat blog structure * \n * blogpage can import many posts
This commit is contained in:
parent
a2d3f26722
commit
12fd204840
22 changed files with 736 additions and 16 deletions
73
v2/lib/load/blog.js
Normal file
73
v2/lib/load/blog.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
const globby = require('globby');
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const {parse, idx} = require('./utils');
|
||||
|
||||
function fileToUrl(fileName) {
|
||||
return fileName
|
||||
.replace('-', '/')
|
||||
.replace('-', '/')
|
||||
.replace('-', '/')
|
||||
.replace(/\.md$/, '');
|
||||
}
|
||||
|
||||
async function loadBlog({blogDir, env, siteConfig}) {
|
||||
const blogFiles = await globby(['*.md'], {
|
||||
cwd: blogDir,
|
||||
});
|
||||
|
||||
const {baseUrl} = siteConfig;
|
||||
|
||||
/* Prepare metadata container */
|
||||
const blogMetadatas = [];
|
||||
|
||||
/* the language for each blog page */
|
||||
const defaultLangTag = idx(env, ['translation', 'defaultLanguage', 'tag']);
|
||||
|
||||
await Promise.all(
|
||||
blogFiles.map(async relativeSource => {
|
||||
const source = path.join(blogDir, relativeSource);
|
||||
|
||||
const blogFileName = path.basename(relativeSource);
|
||||
// Extract, YYYY, MM, DD from the file name
|
||||
const filePathDateArr = blogFileName.split('-');
|
||||
const date = new Date(
|
||||
`${filePathDateArr[0]}-${filePathDateArr[1]}-${
|
||||
filePathDateArr[2]
|
||||
}T06:00:00.000Z`,
|
||||
);
|
||||
|
||||
const fileString = await fs.readFile(source, 'utf-8');
|
||||
const {metadata: rawMetadata} = parse(fileString);
|
||||
const metadata = {
|
||||
permalink: path.join(baseUrl, `blog`, fileToUrl(blogFileName)),
|
||||
source,
|
||||
...rawMetadata,
|
||||
date,
|
||||
language: defaultLangTag,
|
||||
};
|
||||
blogMetadatas.push(metadata);
|
||||
}),
|
||||
);
|
||||
blogMetadatas.sort((a, b) => a.date - b.date);
|
||||
|
||||
// blogpage 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');
|
||||
|
||||
/* eslint-disable */
|
||||
for (let page = 0; page < numberOfPage; page++) {
|
||||
blogMetadatas.push({
|
||||
permalink: path.join(basePageUrl, `${page > 0 ? `page${page + 1}` : ''}`),
|
||||
language: defaultLangTag,
|
||||
isBlogPage: true,
|
||||
posts: blogMetadatas.slice(page * perPage, (page + 1) * perPage),
|
||||
});
|
||||
}
|
||||
|
||||
return blogMetadatas;
|
||||
}
|
||||
|
||||
module.exports = loadBlog;
|
|
@ -1,4 +1,5 @@
|
|||
const path = require('path');
|
||||
const loadBlog = require('./blog');
|
||||
const loadConfig = require('./config');
|
||||
const loadDocs = require('./docs');
|
||||
const loadEnv = require('./env');
|
||||
|
@ -55,6 +56,14 @@ 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(
|
||||
'blogMetadatas.js',
|
||||
`export default ${JSON.stringify(blogMetadatas, null, 2)};`,
|
||||
);
|
||||
|
||||
// resolve outDir
|
||||
const outDir = path.resolve(siteDir, 'build');
|
||||
|
||||
|
@ -68,6 +77,8 @@ module.exports = async function load(siteDir) {
|
|||
const props = {
|
||||
siteConfig,
|
||||
siteDir,
|
||||
blogDir,
|
||||
blogMetadatas,
|
||||
docsDir,
|
||||
docsMetadatas,
|
||||
docsSidebars,
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
async function genRoutesConfig({docsMetadatas = {}, pagesMetadatas = []}) {
|
||||
async function genRoutesConfig({
|
||||
docsMetadatas = {},
|
||||
pagesMetadatas = [],
|
||||
blogMetadatas = [],
|
||||
}) {
|
||||
function genDocsRoute(metadata) {
|
||||
const {permalink, source} = metadata;
|
||||
return `
|
||||
|
@ -41,6 +45,54 @@ async function genRoutesConfig({docsMetadatas = {}, pagesMetadatas = []}) {
|
|||
}`;
|
||||
}
|
||||
|
||||
function genBlogRoute(metadata) {
|
||||
const {permalink, source} = metadata;
|
||||
if (metadata.isBlogPage) {
|
||||
const {posts} = metadata;
|
||||
return `
|
||||
{
|
||||
path: ${JSON.stringify(permalink)},
|
||||
exact: true,
|
||||
component: Loadable.Map({
|
||||
loader: {
|
||||
${posts
|
||||
.map((p, i) => `post${i}: () => import(${JSON.stringify(p.source)})`)
|
||||
.join(',\n\t\t\t\t')}
|
||||
},
|
||||
loading: Loading,
|
||||
render(loaded, props) {
|
||||
${posts
|
||||
.map((p, i) => `const Post${i} = loaded.post${i}.default;`)
|
||||
.join('\n\t\t\t\t')}
|
||||
return (
|
||||
<BlogPage {...props} metadata={${JSON.stringify(metadata)}} >
|
||||
${posts.map((p, i) => `<Post${i} />`).join(' ')}
|
||||
</BlogPage>
|
||||
)
|
||||
}
|
||||
})
|
||||
}`;
|
||||
}
|
||||
|
||||
return `
|
||||
{
|
||||
path: ${JSON.stringify(permalink)},
|
||||
exact: true,
|
||||
component: Loadable({
|
||||
loader: () => import(${JSON.stringify(source)}),
|
||||
loading: Loading,
|
||||
render(loaded, props) {
|
||||
let MarkdownContent = loaded.default;
|
||||
return (
|
||||
<BlogPost {...props} metadata={${JSON.stringify(metadata)}}>
|
||||
<MarkdownContent />
|
||||
</BlogPost>
|
||||
);
|
||||
}
|
||||
})
|
||||
}`;
|
||||
}
|
||||
|
||||
const notFoundRoute = `,
|
||||
{
|
||||
path: '*',
|
||||
|
@ -56,10 +108,14 @@ async function genRoutesConfig({docsMetadatas = {}, pagesMetadatas = []}) {
|
|||
`import Loadable from 'react-loadable';\n` +
|
||||
`import Loading from '@theme/Loading';\n` +
|
||||
`import Doc from '@theme/Doc';\n` +
|
||||
`import BlogPost from '@theme/BlogPost';\n` +
|
||||
`import BlogPage from '@theme/BlogPage';\n` +
|
||||
`import Pages from '@theme/Pages';\n` +
|
||||
`import NotFound from '@theme/NotFound';\n` +
|
||||
`const routes = [${docsRoutes},${pagesMetadatas
|
||||
.map(genPagesRoute)
|
||||
.join(',')},${blogMetadatas
|
||||
.map(genBlogRoute)
|
||||
.join(',')}${notFoundRoute}\n];\n` +
|
||||
`export default routes;\n`
|
||||
);
|
||||
|
|
|
@ -7,7 +7,15 @@ module.exports = function loadConfig(siteDir) {
|
|||
? customThemePath
|
||||
: path.resolve(__dirname, '../theme');
|
||||
|
||||
const themeComponents = ['Doc', 'Pages', 'Loading', 'NotFound', 'Markdown'];
|
||||
const themeComponents = [
|
||||
'Doc',
|
||||
'BlogPost',
|
||||
'BlogPage',
|
||||
'Pages',
|
||||
'Loading',
|
||||
'NotFound',
|
||||
'Markdown',
|
||||
];
|
||||
themeComponents.forEach(component => {
|
||||
if (!require.resolve(path.join(themePath, component))) {
|
||||
throw new Error(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue