mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-25 06:56:56 +02:00
feat(v2): enhance @docusaurus/plugin-content-blog (#1311)
* feat(v2): @docusaurus/plugin-content-blog * address code review
This commit is contained in:
parent
bd0cdbc701
commit
4bd5d3937e
11 changed files with 156 additions and 55 deletions
|
@ -6,11 +6,10 @@
|
|||
*/
|
||||
|
||||
import React, {useContext} from 'react';
|
||||
import Link from '@docusaurus/Link';
|
||||
import Head from '@docusaurus/Head';
|
||||
import Layout from '@theme/Layout'; // eslint-disable-line
|
||||
|
||||
import DocusaurusContext from '@docusaurus/context';
|
||||
import Post from '../Post';
|
||||
|
||||
function BlogPage(props) {
|
||||
const context = useContext(DocusaurusContext);
|
||||
|
@ -30,15 +29,13 @@ function BlogPage(props) {
|
|||
{language && <meta name="docsearch:language" content={language} />}
|
||||
</Head>
|
||||
<div>
|
||||
<ul>
|
||||
{posts.map(metadata => (
|
||||
<li key={metadata.permalink}>
|
||||
<Link to={metadata.permalink}>{metadata.permalink}</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{BlogPosts.map((BlogPost, index) => (
|
||||
<BlogPost key={index} />
|
||||
{BlogPosts.map((PostContent, index) => (
|
||||
<Post
|
||||
key={index}
|
||||
truncated={posts[index].truncatedSource}
|
||||
metadata={posts[index]}>
|
||||
<PostContent />
|
||||
</Post>
|
||||
))}
|
||||
</div>
|
||||
</Layout>
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import React, {useContext} from 'react';
|
||||
import Head from '@docusaurus/Head';
|
||||
import Layout from '@theme/Layout'; // eslint-disable-line
|
||||
|
||||
import DocusaurusContext from '@docusaurus/context';
|
||||
import Post from '../Post';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
function BlogPost(props) {
|
||||
const {metadata: contextMetadata = {}, siteConfig = {}} = useContext(
|
||||
DocusaurusContext,
|
||||
);
|
||||
const {baseUrl, favicon} = siteConfig;
|
||||
const {language, title} = contextMetadata;
|
||||
const {modules, metadata} = props;
|
||||
const BlogPostContents = modules[0];
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Head defaultTitle={siteConfig.title}>
|
||||
{title && <title>{title}</title>}
|
||||
{favicon && <link rel="shortcut icon" href={baseUrl + favicon} />}
|
||||
{language && <html lang={language} />}
|
||||
</Head>
|
||||
{BlogPostContents && (
|
||||
<div className={styles.blogPostContainer}>
|
||||
<Post metadata={metadata}>
|
||||
<BlogPostContents />
|
||||
</Post>
|
||||
</div>
|
||||
)}
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export default BlogPost;
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
.blogPostContainer {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
border-radius: 3px;
|
||||
padding-bottom: 20px;
|
||||
margin: 50px auto;
|
||||
text-align: left;
|
||||
max-width: 1100px;
|
||||
}
|
|
@ -7,21 +7,16 @@
|
|||
|
||||
import React from 'react';
|
||||
import Link from '@docusaurus/Link';
|
||||
import Head from '@docusaurus/Head';
|
||||
import classnames from 'classnames';
|
||||
import Layout from '@theme/Layout'; // eslint-disable-line
|
||||
|
||||
import DocusaurusContext from '@docusaurus/context';
|
||||
import classnames from 'classnames'; // eslint-disable-line
|
||||
|
||||
import styles from './styles.module.css';
|
||||
|
||||
class BlogPost extends React.Component {
|
||||
renderPostHeader() {
|
||||
const {metadata} = this.props;
|
||||
function Post(props) {
|
||||
const {metadata, children, truncated} = props;
|
||||
const renderPostHeader = () => {
|
||||
if (!metadata) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
date,
|
||||
author,
|
||||
|
@ -82,29 +77,21 @@ class BlogPost extends React.Component {
|
|||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {metadata = {}, siteConfig = {}} = this.context;
|
||||
const {baseUrl, favicon} = siteConfig;
|
||||
const {language, title} = metadata;
|
||||
const {modules} = this.props;
|
||||
const BlogPostContents = modules[0];
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Head defaultTitle={siteConfig.title}>
|
||||
{title && <title>{title}</title>}
|
||||
{favicon && <link rel="shortcut icon" href={baseUrl + favicon} />}
|
||||
{language && <html lang={language} />}
|
||||
</Head>
|
||||
{this.renderPostHeader()}
|
||||
<BlogPostContents />
|
||||
</Layout>
|
||||
<div className={styles.postContainer}>
|
||||
{renderPostHeader()}
|
||||
{children}
|
||||
{truncated && (
|
||||
<div className={styles.readMoreContainer}>
|
||||
<Link className={styles.readMoreLink} to={metadata.permalink}>
|
||||
Read More
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
BlogPost.contextType = DocusaurusContext;
|
||||
|
||||
export default BlogPost;
|
||||
export default Post;
|
|
@ -45,3 +45,38 @@
|
|||
height: 50px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.readMoreContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.readMoreLink {
|
||||
border: 1px solid #2e8555;
|
||||
border-radius: 3px;
|
||||
color: #2e8555;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 1.2em;
|
||||
padding: 10px;
|
||||
text-decoration: none!important;
|
||||
text-transform: uppercase;
|
||||
transition: background .3s,color .3s;
|
||||
}
|
||||
|
||||
.readMoreLink:hover {
|
||||
background-color: #00a388;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.postContainer {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
border-radius: 3px;
|
||||
padding-bottom: 20px;
|
||||
margin: 50px auto;
|
||||
text-align: left;
|
||||
width: 600px;
|
||||
max-width: 1100px;
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
const globby = require('globby');
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const {parse, idx, normalizeUrl} = require('@docusaurus/utils');
|
||||
const {parse, idx, normalizeUrl, generate} = require('@docusaurus/utils');
|
||||
|
||||
function fileToUrl(fileName) {
|
||||
return fileName
|
||||
|
@ -25,10 +25,12 @@ const DEFAULT_OPTIONS = {
|
|||
routeBasePath: 'blog', // URL Route.
|
||||
include: ['*.md, *.mdx'], // Extensions to include.
|
||||
pageCount: 10, // How many entries per page.
|
||||
blogPageComponent: '@theme/BlogPage',
|
||||
blogPostComponent: '@theme/BlogPost',
|
||||
blogPageComponent: path.resolve(__dirname, './components/BlogPage'),
|
||||
blogPostComponent: path.resolve(__dirname, './components/BlogPost'),
|
||||
};
|
||||
|
||||
const TRUNCATE_MARKER = /<!--\s*truncate\s*-->/;
|
||||
|
||||
class DocusaurusPluginContentBlog {
|
||||
constructor(opts, context) {
|
||||
this.options = {...DEFAULT_OPTIONS, ...opts};
|
||||
|
@ -43,7 +45,7 @@ class DocusaurusPluginContentBlog {
|
|||
// Fetches blog contents and returns metadata for the contents.
|
||||
async loadContent() {
|
||||
const {pageCount, include, routeBasePath} = this.options;
|
||||
const {env, siteConfig} = this.context;
|
||||
const {env, generatedFilesDir, siteConfig} = this.context;
|
||||
const blogDir = this.contentPath;
|
||||
|
||||
const {baseUrl} = siteConfig;
|
||||
|
@ -71,7 +73,20 @@ class DocusaurusPluginContentBlog {
|
|||
);
|
||||
|
||||
const fileString = await fs.readFile(source, 'utf-8');
|
||||
const {metadata: rawMetadata} = parse(fileString);
|
||||
const {metadata: rawMetadata, content} = parse(fileString);
|
||||
|
||||
let truncatedSource;
|
||||
const isTruncated = TRUNCATE_MARKER.test(content);
|
||||
if (isTruncated) {
|
||||
const pluginContentDir = path.join(generatedFilesDir, this.getName());
|
||||
await generate(
|
||||
pluginContentDir,
|
||||
blogFileName,
|
||||
content.split(TRUNCATE_MARKER)[0],
|
||||
);
|
||||
truncatedSource = path.join(pluginContentDir, blogFileName);
|
||||
}
|
||||
|
||||
const metadata = {
|
||||
permalink: normalizeUrl([
|
||||
baseUrl,
|
||||
|
@ -79,6 +94,7 @@ class DocusaurusPluginContentBlog {
|
|||
fileToUrl(blogFileName),
|
||||
]),
|
||||
source,
|
||||
truncatedSource,
|
||||
...rawMetadata,
|
||||
date,
|
||||
language: defaultLangTag,
|
||||
|
@ -119,7 +135,9 @@ class DocusaurusPluginContentBlog {
|
|||
path: permalink,
|
||||
component: blogPageComponent,
|
||||
metadata: metadataItem,
|
||||
modules: metadataItem.posts.map(post => post.source),
|
||||
modules: metadataItem.posts.map(
|
||||
post => post.truncatedSource || post.source,
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -3,10 +3,15 @@
|
|||
"version": "1.0.0",
|
||||
"description": "Blog content plugin for Docusaurus",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
"components"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/utils": "^1.0.0",
|
||||
"classnames": "^2.2.6",
|
||||
"fs-extra": "^7.0.1",
|
||||
"globby": "^9.1.0"
|
||||
"globby": "^9.1.0",
|
||||
"react": "^16.8.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ const genCache = new Map();
|
|||
async function generate(generatedFilesDir, file, content) {
|
||||
const cached = genCache.get(file);
|
||||
if (cached !== content) {
|
||||
await fs.ensureDir(generatedFilesDir);
|
||||
await fs.writeFile(path.join(generatedFilesDir, file), content);
|
||||
genCache.set(file, content);
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ module.exports = async function load(siteDir) {
|
|||
|
||||
// Process plugins.
|
||||
const pluginConfigs = siteConfig.plugins || [];
|
||||
const context = {env, siteDir, siteConfig};
|
||||
const context = {env, siteDir, generatedFilesDir, siteConfig};
|
||||
const {plugins, pluginRouteConfigs} = await loadPlugins({
|
||||
pluginConfigs,
|
||||
context,
|
||||
|
|
|
@ -16,8 +16,6 @@ module.exports = function loadConfig(siteDir) {
|
|||
|
||||
const themeComponents = [
|
||||
'Doc',
|
||||
'BlogPost',
|
||||
'BlogPage',
|
||||
'Pages',
|
||||
'Loading',
|
||||
'NotFound',
|
||||
|
@ -26,7 +24,9 @@ module.exports = function loadConfig(siteDir) {
|
|||
];
|
||||
|
||||
themeComponents.forEach(component => {
|
||||
if (!require.resolve(path.join(themePath, component))) {
|
||||
try {
|
||||
require.resolve(path.join(themePath, component));
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Failed to load ${themePath}/${component}. It does not exist.`,
|
||||
);
|
||||
|
|
|
@ -10736,7 +10736,7 @@ react-youtube@^7.9.0:
|
|||
prop-types "^15.5.3"
|
||||
youtube-player "^5.5.1"
|
||||
|
||||
react@^16.5.0, react@^16.8.4:
|
||||
react@^16.5.0, react@^16.8.4, react@^16.8.5:
|
||||
version "16.8.5"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.8.5.tgz#49be3b655489d74504ad994016407e8a0445de66"
|
||||
integrity sha512-daCb9TD6FZGvJ3sg8da1tRAtIuw29PbKZW++NN4wqkbEvxL+bZpaaYb4xuftW/SpXmgacf1skXl/ddX6CdOlDw==
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue