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
This commit is contained in:
Joshua Chen 2022-01-27 23:17:31 +08:00 committed by GitHub
parent 5c447b1ca3
commit f6ff6474bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 845 additions and 48 deletions

1
.gitignore vendored
View file

@ -27,6 +27,7 @@ packages/stylelint-copyright/lib/
packages/docusaurus-*/lib-next/
website/netlifyDeployPreview/*
website/changelog
!website/netlifyDeployPreview/index.html
!website/netlifyDeployPreview/_redirects

View file

@ -3686,7 +3686,7 @@ Failed release
- ehsan jso ([@ehsanjso](https://github.com/ehsanjso))
- matbub ([@hi-matbub](https://github.com/hi-matbub))
## 2.0.0-alpha.58
## 2.0.0-alpha.58 (2020-06-18)
#### :rocket: New Feature
@ -3825,11 +3825,11 @@ Failed release
- moonrailgun ([@moonrailgun](https://github.com/moonrailgun))
- tetunori ([@tetunori](https://github.com/tetunori))
## 2.0.0-alpha.57
## 2.0.0-alpha.57 (2020-06-18)
Bad release, check ## 2.0.0-alpha.58
## 2.0.0-alpha.56
## 2.0.0-alpha.56 (2020-05-28)
#### :boom: Breaking Change
@ -3898,7 +3898,7 @@ Bad release, check ## 2.0.0-alpha.58
- Sam Zhou ([@SamChou19815](https://github.com/SamChou19815))
- Sylvain Pace ([@s-pace](https://github.com/s-pace))
## 2.0.0-alpha.55
## 2.0.0-alpha.55 (2020-05-19)
#### :boom: Breaking Change
@ -3991,7 +3991,7 @@ Bad release, check ## 2.0.0-alpha.58
- Yamagishi Kazutoshi ([@ykzts](https://github.com/ykzts))
- Yangshun Tay ([@yangshun](https://github.com/yangshun))
## 2.0.0-alpha.54
## 2.0.0-alpha.54 (2020-04-28)
**HOTFIX for 2.0.0-alpha.53**.
@ -4012,7 +4012,7 @@ Bad release, check ## 2.0.0-alpha.58
- Joe Previte ([@jsjoeio](https://github.com/jsjoeio))
- Sam Zhou ([@SamChou19815](https://github.com/SamChou19815))
## 2.0.0-alpha.53
## 2.0.0-alpha.53 (2020-04-27)
**HOTFIX for 2.0.0-alpha.51**.
@ -4025,7 +4025,7 @@ Bad release, check ## 2.0.0-alpha.58
- Alexey Pyltsyn ([@lex111](https://github.com/lex111))
## 2.0.0-alpha.51
## 2.0.0-alpha.51 (2020-04-27)
#### :boom: Breaking Change
@ -4593,7 +4593,7 @@ Bad release, check ## 2.0.0-alpha.58
- Yangshun Tay ([@yangshun](https://github.com/yangshun))
- t11s ([@TransmissionsDev](https://github.com/TransmissionsDev))
## 2.0.0-alpha.39
## 2.0.0-alpha.39 (2019-12-07)
#### :bug: Bug Fix
@ -4613,7 +4613,7 @@ Bad release, check ## 2.0.0-alpha.58
- Endi ([@endiliey](https://github.com/endiliey))
## 2.0.0-alpha.38
## 2.0.0-alpha.38 (2019-12-06)
#### :boom: Breaking Change
@ -4666,7 +4666,7 @@ For example, if you've swizzled `@theme/DocItem`. You'll have to update
- Shivangna Kaistha ([@shivangna](https://github.com/shivangna))
- kaichu ([@qshiwu](https://github.com/qshiwu))
## 2.0.0-alpha.37
## 2.0.0-alpha.37 (2019-12-01)
#### :boom: Breaking Change
@ -4750,7 +4750,7 @@ For example, if you've swizzled `@theme/DocItem`. You'll have to update
- Endi ([@endiliey](https://github.com/endiliey))
- Wei Gao ([@wgao19](https://github.com/wgao19))
## 2.0.0-alpha.36
## 2.0.0-alpha.36 (2019-11-22)
#### :boom: Breaking Change
@ -4796,7 +4796,7 @@ For example, if you've swizzled `@theme/DocItem`. You'll have to update
- Endi ([@endiliey](https://github.com/endiliey))
- Yangshun Tay ([@yangshun](https://github.com/yangshun))
## 2.0.0-alpha.35
## 2.0.0-alpha.35 (2019-11-17)
#### :rocket: New Feature
@ -4871,7 +4871,7 @@ For example, if you've swizzled `@theme/DocItem`. You'll have to update
- Nick McCormick ([@kenning](https://github.com/kenning))
- Vincent van der Walt ([@vinnytheviking](https://github.com/vinnytheviking))
## 2.0.0-alpha.34
## 2.0.0-alpha.34 (2019-11-11)
#### :rocket: New Feature
@ -4916,7 +4916,7 @@ For example, if you've swizzled `@theme/DocItem`. You'll have to update
- Endi ([@endiliey](https://github.com/endiliey))
- Yangshun Tay ([@yangshun](https://github.com/yangshun))
## 2.0.0-alpha.33
## 2.0.0-alpha.33 (2019-11-08)
#### Features
@ -4946,7 +4946,7 @@ For example, if you've swizzled `@theme/DocItem`. You'll have to update
- Misc dependency upgrades.
- Stability improvement (more tests) & refactoring on docs plugin to prevent regression.
## 2.0.0-alpha.32
## 2.0.0-alpha.32 (2019-11-04)
#### Features
@ -4993,7 +4993,7 @@ function Home() {
- Convert sitemap plugin to TypeScript. ([#1894](https://github.com/facebook/Docusaurus/issues/1894))
- Refactor dark mode toggle into a hook. ([#1899](https://github.com/facebook/Docusaurus/issues/1899))
## 2.0.0-alpha.31
## 2.0.0-alpha.31 (2019-10-26)
- Footer is now sticky/ pinned to the bottom of the viewport in desktop browsers.
- Footer is now also displayed in docs page for consistency.
@ -5010,13 +5010,13 @@ function Home() {
- Increase sidebar width
- etc
## 2.0.0-alpha.30
## 2.0.0-alpha.30 (2019-10-22)
- Fix babel transpilation include/exclude logic to be more efficient. This also fix a very weird bug `TypeError: Cannot assign to read only property 'exports' of object '#<Object>'`.([#1868](https://github.com/facebook/docusaurus/pull/1868))
If you are still encountering the error. Please check whether you use `module.exports` for your `.js` file instead of doing `export` (mixing CJS and ES). See https://github.com/webpack/webpack/issues/4039#issuecomment-477779322 and https://github.com/webpack/webpack/issues/4039#issuecomment-273804003 for more context.
## 2.0.0-alpha.29
## 2.0.0-alpha.29 (2019-10-21)
**HOTFIX for 2.0.0-alpha.28**.
@ -5024,7 +5024,7 @@ If you are still encountering the error. Please check whether you use `module.ex
- Fix wrong `@babel/env` preset configuration that causes build compilation error.
- New UI for webpack compilation progress bar.
## 2.0.0-alpha.28
## 2.0.0-alpha.28 (2019-10-21)
- Further reduce memory usage to avoid heap memory allocation failure.
- Fix `keywords` frontmatter for SEO not working properly.
@ -5039,14 +5039,14 @@ If you are still encountering the error. Please check whether you use `module.ex
- Fix potential security vulnerability because we're exposing the directory structure of the host machine.
- Upgrade dependencies.
## 2.0.0-alpha.27
## 2.0.0-alpha.27 (2019-10-14)
- Add `@theme/Tabs` which can be used to implement multi-language code tabs.
- Implement `custom_edit_url` and `hide_title` markdown header for docusaurus v1 feature parity.
- Reduce memory usage and slightly faster production build.
- Misc dependency upgrades.
## 2.0.0-alpha.26
## 2.0.0-alpha.26 (2019-10-12)
- Docs, pages plugin is rewritten in TypeScript
- Docs improvements and tweaks
@ -5063,7 +5063,7 @@ If you are still encountering the error. Please check whether you use `module.ex
- Add `scripts` and `stylesheets` field to `docusaurus.config.js`
- More documentation...
## 2.0.0-alpha.25
## 2.0.0-alpha.25 (2019-10-01)
- Blog plugin is rewritten in TypeScript and can now support CJK
- Upgrade key direct dependencies such as webpack, mdx and babel to latest
@ -5073,7 +5073,7 @@ If you are still encountering the error. Please check whether you use `module.ex
- Add `truncateMarker` option to blog plugin, support string or regex.
- Webpack `optimization.removeAvailableModules` is now disabled for performance gain. See https://github.com/webpack/webpack/releases/tag/v4.38.0 for more context.
## 2.0.0-alpha.24
## 2.0.0-alpha.24 (2019-07-24)
- Remove unused metadata for pages. This minimize number of http request & smaller bundle size.
- Upgrade dependencies of css-loader from 2.x to 3.x. Css modules localIdentName hash now only use the last 4 characters instead of 8.
@ -5082,11 +5082,11 @@ If you are still encountering the error. Please check whether you use `module.ex
- Use contenthash instead of chunkhash for better long term caching
- Allow user to customize generated heading from MDX. Swizzle `@theme/Heading`
## 2.0.0-alpha.23
## 2.0.0-alpha.23 (2019-07-21)
- Fix docusaurus route config generation for certain edge case
## 2.0.0-alpha.22
## 2.0.0-alpha.22 (2019-07-20)
- Add missing dependencies on `@docusaurus/preset-classic`
- New plugin `@docusaurus/plugin-ideal-image` to generate an almost ideal image (responsive, lazy-loading, and low quality placeholder)
@ -5097,11 +5097,11 @@ If you are still encountering the error. Please check whether you use `module.ex
- `@docusaurus/theme-live-codeblock` is now much smaller in size and no longer only load on viewport
- Blog markdown files now support using the id field to specify the path
## 2.0.0-alpha.21
## 2.0.0-alpha.21 (2019-07-14)
- Fix babel-loader not transpiling docusaurus package
## 2.0.0-alpha.20
## 2.0.0-alpha.20 (2019-07-14)
- Add copy codeblock button
- Add Google analytics and Google gtag plugins.
@ -5117,7 +5117,7 @@ If you are still encountering the error. Please check whether you use `module.ex
- Drop cache-loader in CI and test environment because it has an initial overhead. We always start from scratch in vm instance like CI so cache-loader is useless
- Better splitchunks and babel default webpack config
## 2.0.0-alpha.19
## 2.0.0-alpha.19 (2019-06-07)
- Add a sensible default for browserslist config.
- UI
@ -5156,7 +5156,7 @@ presets: [
- Minify css for production build
- Fix weird scrolling problem when navigating to a route with a `hash` location
## V2 Changelog
## V2 Changelog (2019-04-10)
### `siteConfig.js` changes
@ -5189,7 +5189,7 @@ themeConfig: {
}
```
# Migration Guide
### Migration Guide
_Work in Progress_

View file

@ -1,13 +0,0 @@
---
title: Docusaurus 2 Changelog
hide_title: true
sidebar_label: Changelog
---
```mdx-code-block
import Changelog, {toc as ChangelogTOC} from "@site/../CHANGELOG.md"
<Changelog />
export const toc = ChangelogTOC;
```

View file

@ -112,6 +112,27 @@ const config = {
],
themes: ['live-codeblock'],
plugins: [
[
require.resolve('./src/plugins/changelog/index.js'),
{
blogTitle: 'Docusaurus changelog',
blogDescription: 'Keep yourself up-to-date about new features in every release',
blogSidebarCount: 'ALL',
blogSidebarTitle: 'Changelog',
routeBasePath: '/changelog',
showReadingTime: false,
postsPerPage: 20,
archiveBasePath: null,
authorsMapPath: 'authors.json',
feedOptions: {
type: 'all',
title: 'Docusaurus changelog',
description: 'Keep yourself up-to-date about new features in every release',
copyright: `Copyright © ${new Date().getFullYear()} Facebook, Inc.`,
language: 'en',
}
},
],
[
'content-docs',
/** @type {import('@docusaurus/plugin-content-docs').Options} */
@ -456,6 +477,10 @@ const config = {
label: 'Blog',
to: 'blog',
},
{
label: 'Changelog',
to: '/changelog',
},
{
label: 'GitHub',
href: 'https://github.com/facebook/docusaurus',
@ -510,7 +535,7 @@ const config = {
async function createConfig() {
const FeatureRequestsPlugin = (
await import('./src/featureRequests/FeatureRequestsPlugin.mjs')
await import('./src/plugins/featureRequests/FeatureRequestsPlugin.mjs')
).default;
const configTabs = (await import('./src/remark/configTabs.mjs')).default;
const lightTheme = (await import('./src/utils/prismLight.mjs')).default;

View file

@ -41,7 +41,9 @@
"@docusaurus/theme-common": "2.0.0-beta.15",
"@docusaurus/theme-live-codeblock": "2.0.0-beta.15",
"@docusaurus/utils": "2.0.0-beta.15",
"@docusaurus/utils-common": "2.0.0-beta.15",
"@popperjs/core": "^2.10.2",
"axios": "^0.25.0",
"clsx": "^1.1.1",
"color": "^4.0.1",
"esbuild-loader": "2.16.0",

View file

@ -11,6 +11,11 @@ module.exports = {
type: 'autogenerated',
dirName: '.',
},
{
type: 'link',
href: '/changelog',
label: 'Changelog',
},
{
type: 'link',
href: '/showcase',

View file

@ -0,0 +1,158 @@
/**
* 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;

View file

@ -0,0 +1,88 @@
/**
* 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 */
// @ts-check
const path = require('path');
const fs = require('fs-extra');
// const axios = require('axios').default;
// TODO not sure how the syncing should be done at all... for now it always
// pretends the limit is reached. We should only fetch a portion of the avatars
// at a time. But seems avatars.githubusercontent.com API doesn't like HTTP requests?
/**
* @param {string} username
* @param {Record<string, number>} lastUpdateCache
* @param {Record<string, {imageURL: string; url: string}>} authorsMap
* @returns true if saved successfully (including not found); false if limited reached
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async function fetchImage(username, lastUpdateCache, authorsMap) {
return false;
}
/**
* We need to keep track of all committers that are in the changelog, and fetch
* their avatars beforehand. This prevents sending too many requests to GitHub
* every time one visits a page. Avatar refreshing is done incrementally across
* each build.
*
* This method mutates the authorsMap. If the avatar fails to be fetched, the
* imageURL is re-written with the github remote URL. The authors map is then
* written to FS.
*
* @param {Record<string, {name: string, url: string, alias: string, imageURL: string}>} authorsMap
* @param {string} generateDir
*/
async function syncAvatars(authorsMap, generateDir) {
const imagePath = path.join(generateDir, 'img');
const lastUpdateCachePath = path.join(imagePath, 'lastUpdate.json');
const authorsPath = path.join(generateDir, 'authors.json');
if (!(await fs.pathExists(lastUpdateCachePath))) {
await fs.outputFile(lastUpdateCachePath, '{}');
}
/**
* Records the last time an avatar was successfully updated.
* If an entry doesn't exist, the file won't exist either.
* @type {Record<string, number>}
*/
const lastUpdateCache = await fs.readJSON(lastUpdateCachePath);
let limitReached = false;
// eslint-disable-next-line no-restricted-syntax
for (const username of Object.keys(authorsMap)) {
if (!limitReached && !lastUpdateCache[username]) {
if (!(await fetchImage(username, lastUpdateCache, authorsMap))) {
limitReached = true;
}
}
if (limitReached) {
authorsMap[username].imageURL = `https://github.com/${username}.png`;
}
}
const usersByLastUpdate = Object.entries(lastUpdateCache)
.sort((a, b) => a[1] - b[1])
.map((a) => a[0]);
// eslint-disable-next-line no-restricted-syntax
for (const username of usersByLastUpdate) {
if (
!limitReached &&
lastUpdateCache[username] < Date.now() - 24 * 3600 * 1000
) {
if (!(await fetchImage(username, lastUpdateCache, authorsMap))) {
break;
}
}
}
await fs.outputFile(
lastUpdateCachePath,
JSON.stringify(lastUpdateCache, null, 2),
);
await fs.outputFile(authorsPath, JSON.stringify(authorsMap, null, 2));
}
module.exports = syncAvatars;

View file

@ -0,0 +1,36 @@
/**
* 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.
*/
import React from 'react';
import Link from '@docusaurus/Link';
import type {Props} from '@theme/BlogPostAuthor';
import styles from './styles.module.css';
function ChangelogAuthor({author}: Props): JSX.Element {
const {name, url, imageURL} = author;
return (
<div className="avatar margin-bottom--sm">
{imageURL && (
<Link className="avatar__photo-link avatar__photo" href={url}>
<img
className={styles.image}
src={imageURL}
alt={name}
onError={(e) => {
// Image returns 404 if the user's handle changes. We display a fallback instead.
e.currentTarget.src =
'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600" fill="none" stroke="%2325c2a0" stroke-width="30" version="1.1"><circle cx="300" cy="230" r="115"/><path stroke-linecap="butt" d="M106.81863443903,481.4 a205,205 1 0,1 386.36273112194,0"/></svg>';
}}
/>
</Link>
)}
</div>
);
}
export default ChangelogAuthor;

View file

@ -0,0 +1,12 @@
/**
* 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.
*/
.image {
width: 100%;
height: 100%;
object-fit: cover;
}

View file

@ -0,0 +1,55 @@
/**
* 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.
*/
import React, {useState} from 'react';
import clsx from 'clsx';
import type {Props} from '@theme/BlogPostAuthors';
import ChangelogAuthor from '@theme/ChangelogAuthor';
import styles from './styles.module.css';
import IconExpand from '@theme/IconExpand';
// Component responsible for the authors layout
export default function BlogPostAuthors({
authors,
assets,
}: Props): JSX.Element | null {
const [expanded, setExpanded] = useState(false);
const authorsCount = authors.length;
if (authorsCount === 0) {
return null;
}
const filteredAuthors = authors.slice(0, expanded ? authors.length : 10);
return (
<div
className={clsx(
'margin-top--md margin-bottom--sm',
styles.imageOnlyAuthorRow,
)}>
{filteredAuthors.map((author, idx) => (
<div className={styles.imageOnlyAuthorCol} key={idx}>
<ChangelogAuthor
author={{
...author,
// Handle author images using relative paths
imageURL: assets.authorsImageUrls[idx] ?? author.imageURL,
}}
/>
</div>
))}
{authors.length > 10 && (
<button
className={clsx('clean-btn', styles.toggleButton)}
type="button"
onClick={() => setExpanded((v) => !v)}
aria-label="expand">
<IconExpand expanded={expanded} />
</button>
)}
</div>
);
}

View file

@ -0,0 +1,38 @@
/**
* 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.
*/
.authorCol {
max-width: inherit !important;
flex-grow: 1 !important;
}
.imageOnlyAuthorRow {
display: flex;
flex-flow: row wrap;
}
.imageOnlyAuthorCol {
margin-left: 0.3rem;
margin-right: 0.3rem;
}
.imageOnlyAuthorCol [class^='image'] {
background-color: var(--ifm-color-emphasis-100);
}
.toggleButton {
margin-left: 0.3rem;
margin-right: 0.3rem;
border-radius: 50%;
width: var(--ifm-avatar-photo-size-md);
height: var(--ifm-avatar-photo-size-md);
background-color: var(--ifm-color-emphasis-100);
}
.toggleButtonIconExpanded {
transform: rotate(180deg);
}

View file

@ -0,0 +1,79 @@
/**
* 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.
*/
import React from 'react';
import clsx from 'clsx';
// eslint-disable-next-line import/no-extraneous-dependencies
import {MDXProvider} from '@mdx-js/react';
import Link from '@docusaurus/Link';
import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
import {blogPostContainerID} from '@docusaurus/utils-common';
import MDXComponents from '@theme/MDXComponents';
import type {Props} from '@theme/BlogPostItem';
import styles from './styles.module.css';
import ChangelogAuthors from '@theme/ChangelogAuthors';
function ChangelogItem(props: Props): JSX.Element {
const {withBaseUrl} = useBaseUrlUtils();
const {
children,
frontMatter,
assets,
metadata,
isBlogPostPage = false,
} = props;
const {date, formattedDate, permalink, title, authors} = metadata;
const image = assets.image ?? frontMatter.image;
const TitleHeading = isBlogPostPage ? 'h1' : 'h2';
return (
<article
className={!isBlogPostPage ? 'margin-bottom--md' : undefined}
itemProp="blogPost"
itemScope
itemType="http://schema.org/BlogPosting">
<header>
<TitleHeading
className={clsx(
isBlogPostPage ? styles.blogPostPageTitle : styles.blogPostTitle,
)}
itemProp="headline">
{isBlogPostPage ? (
title
) : (
<Link itemProp="url" to={permalink}>
{title}
</Link>
)}
</TitleHeading>
<div className={clsx(styles.blogPostData, 'margin-vert--md')}>
<time dateTime={date} itemProp="datePublished">
{formattedDate}
</time>
</div>
<ChangelogAuthors authors={authors} assets={assets} />
</header>
{image && (
<meta itemProp="image" content={withBaseUrl(image, {absolute: true})} />
)}
<div
// This ID is used for the feed generation to locate the main content
id={isBlogPostPage ? blogPostContainerID : undefined}
className="markdown"
itemProp="articleBody">
<MDXProvider components={MDXComponents}>{children}</MDXProvider>
</div>
</article>
);
}
export default ChangelogItem;

View file

@ -0,0 +1,22 @@
/**
* 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.
*/
.blogPostTitle {
font-size: 2rem;
}
.blogPostPageTitle {
font-size: 3rem;
}
.blogPostData {
font-size: 0.9rem;
}
.blogPostDetailsFull {
flex-direction: column;
}

View file

@ -0,0 +1,92 @@
/**
* 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.
*/
import React from 'react';
import BlogLayout from '@theme/BlogLayout';
import BlogListPaginator from '@theme/BlogListPaginator';
import type {Props} from '@theme/BlogListPage';
import {ThemeClassNames} from '@docusaurus/theme-common';
import Link from '@docusaurus/Link';
import ChangelogItem from '@theme/ChangelogItem';
import styles from './styles.module.css';
function ChangelogList(props: Props): JSX.Element {
const {metadata, items, sidebar} = props;
const {blogDescription, blogTitle} = metadata;
return (
<BlogLayout
title={blogTitle}
description={blogDescription}
wrapperClassName={ThemeClassNames.wrapper.blogPages}
pageClassName={ThemeClassNames.page.blogListPage}
searchMetadata={{
// assign unique search tag to exclude this page from search results!
tag: 'blog_posts_list',
}}
sidebar={sidebar}>
<header className="margin-bottom--lg">
<h1 style={{fontSize: '3rem'}}>{blogTitle}</h1>
<p>
Subscribe through{' '}
<Link href="pathname:///changelog/rss.xml" className={styles.rss}>
<b>RSS feeds</b>
<svg
style={{
fill: '#f26522',
position: 'relative',
left: 4,
top: 1,
marginRight: 8,
}}
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24">
<path d="M6.503 20.752c0 1.794-1.456 3.248-3.251 3.248-1.796 0-3.252-1.454-3.252-3.248 0-1.794 1.456-3.248 3.252-3.248 1.795.001 3.251 1.454 3.251 3.248zm-6.503-12.572v4.811c6.05.062 10.96 4.966 11.022 11.009h4.817c-.062-8.71-7.118-15.758-15.839-15.82zm0-3.368c10.58.046 19.152 8.594 19.183 19.188h4.817c-.03-13.231-10.755-23.954-24-24v4.812z" />
</svg>
</Link>{' '}
or follow us on{' '}
<Link
href="https://twitter.com/docusaurus"
className={styles.twitter}>
<b>Twitter</b>
<svg
style={{
fill: '#1da1f2',
position: 'relative',
left: 4,
top: 1,
marginRight: 8,
}}
width="16"
height="16"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512">
<path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z" />
</svg>
</Link>{' '}
to stay up-to-date with new releases!
</p>
</header>
{items.map(({content: BlogPostContent}) => (
<ChangelogItem
key={BlogPostContent.metadata.permalink}
frontMatter={BlogPostContent.frontMatter}
assets={BlogPostContent.assets}
metadata={BlogPostContent.metadata}
truncated={BlogPostContent.metadata.truncated}>
<BlogPostContent />
</ChangelogItem>
))}
<BlogListPaginator metadata={metadata} />
</BlogLayout>
);
}
export default ChangelogList;

View file

@ -0,0 +1,28 @@
/**
* 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.
*/
.blogPostTitle {
font-size: 2rem;
}
.blogPostData {
font-size: 0.9rem;
}
.blogPostDetailsFull {
flex-direction: column;
}
.rss,
.rss:hover {
color: #f26522;
}
.twitter,
.twitter:hover {
color: #1da1f2;
}

View file

@ -0,0 +1,102 @@
/**
* 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.
*/
import React from 'react';
import Seo from '@theme/Seo';
import BlogLayout from '@theme/BlogLayout';
import ChangelogItem from '@theme/ChangelogItem';
import BlogPostPaginator from '@theme/BlogPostPaginator';
import type {Props} from '@theme/BlogPostPage';
import {ThemeClassNames} from '@docusaurus/theme-common';
import TOC from '@theme/TOC';
import Link from '@docusaurus/Link';
// This page doesn't change anything. It's just swapping BlogPostItem with our
// own ChangelogItem. We don't want to apply the swizzled item to the actual blog.
function BlogPostPage(props: Props): JSX.Element {
const {content: BlogPostContents, sidebar} = props;
const {assets, metadata} = BlogPostContents;
const {
title,
description,
nextItem,
prevItem,
date,
tags,
authors,
frontMatter,
// @ts-expect-error: we injected this
listPageLink,
} = metadata;
const {
hide_table_of_contents: hideTableOfContents,
keywords,
toc_min_heading_level: tocMinHeadingLevel,
toc_max_heading_level: tocMaxHeadingLevel,
} = frontMatter;
const image = assets.image ?? frontMatter.image;
return (
<BlogLayout
wrapperClassName={ThemeClassNames.wrapper.blogPages}
pageClassName={ThemeClassNames.page.blogPostPage}
sidebar={sidebar}
toc={
!hideTableOfContents &&
BlogPostContents.toc &&
BlogPostContents.toc.length > 0 ? (
<TOC
toc={BlogPostContents.toc}
minHeadingLevel={tocMinHeadingLevel}
maxHeadingLevel={tocMaxHeadingLevel}
/>
) : undefined
}>
<Seo
title={title}
description={description}
keywords={keywords}
image={image}>
<meta property="og:type" content="article" />
<meta property="article:published_time" content={date} />
{authors.some((author) => author.url) && (
<meta
property="article:author"
content={authors
.map((author) => author.url)
.filter(Boolean)
.join(',')}
/>
)}
{tags.length > 0 && (
<meta
property="article:tag"
content={tags.map((tag) => tag.label).join(',')}
/>
)}
</Seo>
<Link to={listPageLink}> Back to index page</Link>
<ChangelogItem
frontMatter={frontMatter}
assets={assets}
metadata={metadata}
isBlogPostPage>
<BlogPostContents />
</ChangelogItem>
{(nextItem || prevItem) && (
<BlogPostPaginator nextItem={nextItem} prevItem={prevItem} />
)}
</BlogLayout>
);
}
export default BlogPostPage;

View file

@ -0,0 +1,37 @@
/**
* 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.
*/
import React from 'react';
import type {Props} from '@theme/IconExpand';
function IconExpand({expanded, ...props}: Props): JSX.Element {
if (expanded) {
return (
<svg
viewBox="0 0 1024 1024"
width="20"
height="20"
fill="currentColor"
{...props}>
<path d="M783.915092 1009.031953l-271.898251-277.615587-271.930737 277.550617a49.214558 49.214558 0 0 1-70.752018 0 51.780862 51.780862 0 0 1 0-72.246322l307.274261-313.706262a49.279528 49.279528 0 0 1 70.784503 0l307.33923 313.706262a51.975771 51.975771 0 0 1 0 72.311292 49.409467 49.409467 0 0 1-70.816988 0z m-307.306745-608.05155L169.269117 87.274141A51.975771 51.975771 0 0 1 169.269117 14.96285a49.409467 49.409467 0 0 1 70.816987 0l271.930737 277.615586L783.850122 14.96285a49.409467 49.409467 0 0 1 70.816988 0 51.975771 51.975771 0 0 1 0 72.311291l-307.33923 313.706262a49.376982 49.376982 0 0 1-70.719533 0z" />
</svg>
);
}
return (
<svg
viewBox="0 0 1024 1024"
width="20"
height="20"
fill="currentColor"
{...props}>
<path d="M476.612887 1009.12034L169.240699 695.380437a51.981345 51.981345 0 0 1 0-72.319045 49.382277 49.382277 0 0 1 70.824582 0l271.959897 277.645356 271.862433-277.645356a49.382277 49.382277 0 0 1 70.824582 0 51.981345 51.981345 0 0 1 0 72.319045l-307.307212 313.739903a49.447254 49.447254 0 0 1-70.792094 0z m307.274724-608.116755L511.99269 123.455693l-271.959897 277.645357a49.382277 49.382277 0 0 1-70.824582 0 51.981345 51.981345 0 0 1 0-72.319045L476.580399 15.042102a49.382277 49.382277 0 0 1 70.727117 0l307.372188 313.739903a51.981345 51.981345 0 0 1 0 72.319045 49.414766 49.414766 0 0 1-70.824582 0z" />
</svg>
);
}
export default IconExpand;

View file

@ -0,0 +1,20 @@
/**
* 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.
*/
declare module '@theme/ChangelogItem';
declare module '@theme/ChangelogAuthors';
declare module '@theme/ChangelogAuthor';
declare module '@theme/IconExpand' {
import type {ComponentProps} from 'react';
export interface Props extends ComponentProps<'svg'> {
expanded?: boolean;
}
const IconExpand: (props: Props) => JSX.Element;
export default IconExpand;
}

View file

@ -19,13 +19,16 @@ export default function FeatureRequestsPlugin(context) {
context.baseUrl,
'/feature-requests',
]);
await actions.createData('paths.json', JSON.stringify(basePath));
const paths = await actions.createData(
'paths.json',
JSON.stringify(basePath),
);
actions.addRoute({
path: basePath,
exact: false,
component: '@site/src/featureRequests/FeatureRequestsPage',
component: '@site/src/plugins/featureRequests/FeatureRequestsPage',
modules: {
basePath: './feature-requests-plugin/default/paths.json',
basePath: paths,
},
});
},

View file

@ -5303,6 +5303,13 @@ axios@^0.21.1:
dependencies:
follow-redirects "^1.14.0"
axios@^0.25.0:
version "0.25.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a"
integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==
dependencies:
follow-redirects "^1.14.7"
axobject-query@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
@ -9182,7 +9189,7 @@ folder-walker@^3.2.0:
dependencies:
from2 "^2.1.0"
follow-redirects@^1.0.0, follow-redirects@^1.14.0:
follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.14.7:
version "1.14.7"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==