diff --git a/packages/docusaurus/lib/theme/BlogPage/index.js b/packages/docusaurus-plugin-content-blog/components/BlogPage/index.js similarity index 74% rename from packages/docusaurus/lib/theme/BlogPage/index.js rename to packages/docusaurus-plugin-content-blog/components/BlogPage/index.js index d024db3463..16d2ab08d3 100644 --- a/packages/docusaurus/lib/theme/BlogPage/index.js +++ b/packages/docusaurus-plugin-content-blog/components/BlogPage/index.js @@ -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 && }
- - {BlogPosts.map((BlogPost, index) => ( - + {BlogPosts.map((PostContent, index) => ( + + + ))}
diff --git a/packages/docusaurus-plugin-content-blog/components/BlogPost/index.js b/packages/docusaurus-plugin-content-blog/components/BlogPost/index.js new file mode 100644 index 0000000000..109afd4711 --- /dev/null +++ b/packages/docusaurus-plugin-content-blog/components/BlogPost/index.js @@ -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 ( + + + {title && {title}} + {favicon && } + {language && } + + {BlogPostContents && ( +
+ + + +
+ )} +
+ ); +} + +export default BlogPost; diff --git a/packages/docusaurus-plugin-content-blog/components/BlogPost/styles.module.css b/packages/docusaurus-plugin-content-blog/components/BlogPost/styles.module.css new file mode 100644 index 0000000000..41181ea246 --- /dev/null +++ b/packages/docusaurus-plugin-content-blog/components/BlogPost/styles.module.css @@ -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; +} diff --git a/packages/docusaurus/lib/theme/BlogPost/index.js b/packages/docusaurus-plugin-content-blog/components/Post/index.js similarity index 67% rename from packages/docusaurus/lib/theme/BlogPost/index.js rename to packages/docusaurus-plugin-content-blog/components/Post/index.js index 635294363e..5fc08b0c62 100644 --- a/packages/docusaurus/lib/theme/BlogPost/index.js +++ b/packages/docusaurus-plugin-content-blog/components/Post/index.js @@ -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 { ); - } + }; - render() { - const {metadata = {}, siteConfig = {}} = this.context; - const {baseUrl, favicon} = siteConfig; - const {language, title} = metadata; - const {modules} = this.props; - const BlogPostContents = modules[0]; - - return ( - - - {title && {title}} - {favicon && } - {language && } - - {this.renderPostHeader()} - - - ); - } + return ( +
+ {renderPostHeader()} + {children} + {truncated && ( +
+ + Read More + +
+ )} +
+ ); } -BlogPost.contextType = DocusaurusContext; - -export default BlogPost; +export default Post; diff --git a/packages/docusaurus/lib/theme/BlogPost/styles.module.css b/packages/docusaurus-plugin-content-blog/components/Post/styles.module.css similarity index 52% rename from packages/docusaurus/lib/theme/BlogPost/styles.module.css rename to packages/docusaurus-plugin-content-blog/components/Post/styles.module.css index eac69f06fa..1114b8b419 100644 --- a/packages/docusaurus/lib/theme/BlogPost/styles.module.css +++ b/packages/docusaurus-plugin-content-blog/components/Post/styles.module.css @@ -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; +} diff --git a/packages/docusaurus-plugin-content-blog/index.js b/packages/docusaurus-plugin-content-blog/index.js index c2127e1630..2531bc7caf 100644 --- a/packages/docusaurus-plugin-content-blog/index.js +++ b/packages/docusaurus-plugin-content-blog/index.js @@ -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 = //; + 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; } diff --git a/packages/docusaurus-plugin-content-blog/package.json b/packages/docusaurus-plugin-content-blog/package.json index 2aa53ae4b1..5b12fbc341 100644 --- a/packages/docusaurus-plugin-content-blog/package.json +++ b/packages/docusaurus-plugin-content-blog/package.json @@ -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" } } diff --git a/packages/docusaurus-utils/index.js b/packages/docusaurus-utils/index.js index 9879b0dc27..aa98e70d51 100644 --- a/packages/docusaurus-utils/index.js +++ b/packages/docusaurus-utils/index.js @@ -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); } diff --git a/packages/docusaurus/lib/load/index.js b/packages/docusaurus/lib/load/index.js index 37b5585e64..5f8d9a14c7 100644 --- a/packages/docusaurus/lib/load/index.js +++ b/packages/docusaurus/lib/load/index.js @@ -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, diff --git a/packages/docusaurus/lib/load/theme.js b/packages/docusaurus/lib/load/theme.js index 14d8ffa155..819cd9737a 100644 --- a/packages/docusaurus/lib/load/theme.js +++ b/packages/docusaurus/lib/load/theme.js @@ -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.`, ); diff --git a/yarn.lock b/yarn.lock index 426d005e0d..8927926861 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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==