docusaurus/website/src/plugins/changelog/index.js
Joshua Chen f6ff6474bc
feat(website): new plugin to load CHANGELOG and render as blog (#6414)
* feat(website): new plugin to load CHANGELOG and render as blog

* use CJS

* move footer links

* better design

* fixes

* correctly order posts

* add authors

* Add axios

* Update styles

* oops

* oops

* add expand button

* back to index page link

* fix styles

* add feed options

* fix

* fix

* Add fallback

* fix

* fixes
2022-01-27 23:17:31 +08:00

158 lines
4.5 KiB
JavaScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/* eslint-disable import/no-extraneous-dependencies */
const path = require('path');
const fs = require('fs-extra');
const pluginContentBlog = require('@docusaurus/plugin-content-blog');
const {aliasedSitePath, docuHash, normalizeUrl} = require('@docusaurus/utils');
const syncAvatars = require('./syncAvatars');
/**
* Multiple versions may be published on the same day, causing the order to be
* the reverse. Therefore, our publish time has a "fake hour" to order them.
*/
const publishTimes = new Set();
/** @type {Record<string, {name: string, url: string, alias: string, imageURL: string}>} */
const authorsMap = {};
/**
* @param {string} section
*/
function processSection(section) {
const title = section
.match(/\n## .*/)?.[0]
.trim()
.replace('## ', '');
if (!title) {
return null;
}
const content = section
.replace(/\n## .*/, '')
.trim()
.replace('running_woman', 'running');
let authors = content.match(/## Committers: \d+.*/ms);
if (authors) {
authors = authors[0]
.match(/- .*/g)
.map(
(line) =>
line.match(
/- (?:(?<name>.*?) \()?\[@(?<alias>.*)\]\((?<url>.*?)\)\)?/,
).groups,
)
.map((author) => ({
...author,
name: author.name ?? author.alias,
imageURL: `./img/${author.alias}.png`,
}))
.sort((a, b) => a.url.localeCompare(b.url));
authors.forEach((author) => {
authorsMap[author.alias] = author;
});
}
let hour = 20;
const date = title.match(/ \((.*)\)/)[1];
while (publishTimes.has(`${date}T${hour}:00`)) {
hour -= 1;
}
publishTimes.add(`${date}T${hour}:00`);
return {
title: title.replace(/ \(.*\)/, ''),
content: `---
date: ${`${date}T${hour}:00`}${
authors
? `
authors:
${authors.map((author) => ` - '${author.alias}'`).join('\n')}`
: ''
}
---
# ${title.replace(/ \(.*\)/, '')}
<!-- truncate -->
${content.replace(/####/g, '##')}`,
};
}
/**
* @param {import('@docusaurus/types').LoadContext} context
* @returns {import('@docusaurus/types').Plugin}
*/
async function ChangelogPlugin(context, options) {
const generateDir = path.join(context.siteDir, 'changelog/source');
const blogPlugin = await pluginContentBlog.default(context, {
...options,
path: generateDir,
id: 'changelog',
blogListComponent: '@theme/ChangelogList',
blogPostComponent: '@theme/ChangelogPage',
});
const changelogPath = path.join(__dirname, '../../../../CHANGELOG.md');
return {
...blogPlugin,
name: 'changelog-plugin',
async loadContent() {
const fileContent = await fs.readFile(changelogPath, 'utf-8');
const sections = fileContent
.split(/(?=\n## )/ms)
.map(processSection)
.filter(Boolean);
await Promise.all(
sections.map((section) =>
fs.outputFile(
path.join(generateDir, `${section.title}.md`),
section.content,
),
),
);
await syncAvatars(authorsMap, generateDir);
const content = await blogPlugin.loadContent();
content.blogPosts.forEach((post, index) => {
const pageIndex = Math.floor(index / options.postsPerPage);
post.metadata.listPageLink = normalizeUrl([
context.baseUrl,
options.routeBasePath,
pageIndex === 0 ? '/' : `/page/${pageIndex + 1}`,
]);
});
return content;
},
configureWebpack(...args) {
const config = blogPlugin.configureWebpack(...args);
const pluginDataDirRoot = path.join(
context.generatedFilesDir,
'changelog-plugin',
'default',
);
// Redirect the metadata path to our folder
config.module.rules[0].use[1].options.metadataPath = (mdxPath) => {
// Note that metadataPath must be the same/in-sync as
// the path from createData for each MDX.
const aliasedPath = aliasedSitePath(mdxPath, context.siteDir);
return path.join(pluginDataDirRoot, `${docuHash(aliasedPath)}.json`);
};
return config;
},
getThemePath() {
return path.join(__dirname, './theme');
},
getPathsToWatch() {
// Don't watch the generated dir
return [changelogPath];
},
};
}
ChangelogPlugin.validateOptions = pluginContentBlog.validateOptions;
module.exports = ChangelogPlugin;