diff --git a/packages/docusaurus-plugin-content-blog/src/index.js b/packages/docusaurus-plugin-content-blog/src/index.js index 25f377c5e9c6..069d2b143d6c 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.js +++ b/packages/docusaurus-plugin-content-blog/src/index.js @@ -26,6 +26,8 @@ const DEFAULT_OPTIONS = { postsPerPage: 10, // How many posts per page. blogListComponent: '@theme/BlogListPage', blogPostComponent: '@theme/BlogPostPage', + blogTagsListComponent: '@theme/BlogTagsListPage', + blogTagsPostsComponent: '@theme/BlogTagsPostsPage', }; class DocusaurusPluginContentBlog { @@ -87,6 +89,7 @@ class DocusaurusPluginContentBlog { source, description: frontMatter.description || excerpt, date, + tags: frontMatter.tags, title: frontMatter.title || blogFileName, }, }); @@ -126,16 +129,39 @@ class DocusaurusPluginContentBlog { }); } + const blogTags = {}; + blogPosts.forEach(blogPost => { + const {tags} = blogPost.metadata; + if (!tags || tags.length === 0) { + return; + } + + tags.forEach(tag => { + const normalizedTag = tag.toLowerCase(); + if (!blogTags[normalizedTag]) { + blogTags[normalizedTag] = []; + } + blogTags[normalizedTag].push(blogPost.id); + }); + }); + return { blogPosts, blogListPaginated, + blogTags, }; } async contentLoaded({content: blogContents, actions}) { - const {blogListComponent, blogPostComponent} = this.options; + const { + blogListComponent, + blogPostComponent, + blogTagsListComponent, + blogTagsPostsComponent, + } = this.options; + const {addRoute, createData} = actions; - const {blogPosts, blogListPaginated} = blogContents; + const {blogPosts, blogListPaginated, blogTags} = blogContents; const blogItemsToModules = {}; // Create routes for blog entries. @@ -213,6 +239,76 @@ class DocusaurusPluginContentBlog { }); }), ); + + // Tags. + const {routeBasePath} = this.options; + const { + siteConfig: {baseUrl}, + } = this.context; + + const basePageUrl = normalizeUrl([baseUrl, routeBasePath]); + const tagsPath = normalizeUrl([basePageUrl, 'tags']); + const tagsModule = {}; + + await Promise.all( + Object.keys(blogTags).map(async tag => { + const permalink = normalizeUrl([tagsPath, tag]); + const postIDs = blogTags[tag]; + tagsModule[tag] = { + count: postIDs.length, + permalink, + }; + + const tagsMetadataPath = await createData( + `${docuHash(permalink)}.json`, + JSON.stringify( + { + tag, + }, + null, + 2, + ), + ); + + addRoute({ + path: permalink, + component: blogTagsPostsComponent, + exact: true, + modules: { + items: postIDs.map(postID => { + const {metadata: postMetadata, metadataPath} = blogItemsToModules[ + postID + ]; + return { + content: { + __import: true, + path: postMetadata.source, + query: { + truncated: true, + }, + }, + metadata: metadataPath, + }; + }), + metadata: tagsMetadataPath, + }, + }); + }), + ); + + const tagsListPath = await createData( + `${docuHash(`${tagsPath}-tags`)}.json`, + JSON.stringify(tagsModule, null, 2), + ); + + addRoute({ + path: tagsPath, + component: blogTagsListComponent, + exact: true, + modules: { + tags: tagsListPath, + }, + }); } getThemePath() { diff --git a/packages/docusaurus-plugin-content-blog/src/theme/BlogTagsListPage/index.js b/packages/docusaurus-plugin-content-blog/src/theme/BlogTagsListPage/index.js new file mode 100644 index 000000000000..a083e7b8d80b --- /dev/null +++ b/packages/docusaurus-plugin-content-blog/src/theme/BlogTagsListPage/index.js @@ -0,0 +1,69 @@ +/** + * 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 from 'react'; + +import Layout from '@theme/Layout'; // eslint-disable-line +import Link from '@docusaurus/Link'; + +const CHARS_IN_ALPHABET = 26; +const ASCII_LOWERCASE_A = 97; + +function BlogTagsListPage(props) { + const {tags} = props; + + const tagsList = Array(CHARS_IN_ALPHABET) + .fill(null) + .map(() => []); + const allTags = Object.keys(tags).sort(); + + allTags.forEach(tag => { + const firstLetter = tag.charCodeAt(0); + tagsList[firstLetter - ASCII_LOWERCASE_A].push(tag); + }); + + const tagsSection = tagsList + .map((tagsForLetter, index) => { + if (tagsForLetter.length === 0) { + return null; + } + const letter = String.fromCharCode( + ASCII_LOWERCASE_A + index, + ).toUpperCase(); + + return ( +
+

{letter}

+ {tagsForLetter.map(tag => ( + + {tag} ({tags[tag].count}) + + ))} +
+
+ ); + }) + .filter(item => item != null); + + return ( + +
+
+
+

Tags

+
{tagsSection}
+
+
+
+
+ ); +} + +export default BlogTagsListPage; diff --git a/packages/docusaurus-plugin-content-blog/src/theme/BlogTagsPostsPage/index.js b/packages/docusaurus-plugin-content-blog/src/theme/BlogTagsPostsPage/index.js new file mode 100644 index 000000000000..5ab4f30f50b0 --- /dev/null +++ b/packages/docusaurus-plugin-content-blog/src/theme/BlogTagsPostsPage/index.js @@ -0,0 +1,46 @@ +/** + * 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 from 'react'; + +import Layout from '@theme/Layout'; // eslint-disable-line +import BlogPostItem from '@theme/BlogPostItem'; + +function BlogTagsPostPage(props) { + const {metadata, items} = props; + const {tag} = metadata; + + return ( + +
+
+
+

+ {items.length} post(s) tagged with "{tag}" +

+
+ {items.map( + ({content: BlogPostContent, metadata: blogPostMetadata}) => ( +
+ + + +
+ ), + )} +
+
+
+
+
+ ); +} + +export default BlogTagsPostPage; diff --git a/website-1.x/blog/2017-12-14-introducing-docusaurus.md b/website-1.x/blog/2017-12-14-introducing-docusaurus.md index d60b78be7a8f..d8231a159ec3 100644 --- a/website-1.x/blog/2017-12-14-introducing-docusaurus.md +++ b/website-1.x/blog/2017-12-14-introducing-docusaurus.md @@ -4,6 +4,7 @@ author: Joel Marcey authorURL: http://twitter.com/JoelMarcey authorFBID: 611217057 authorTwitter: JoelMarcey +tags: [birth] --- ![Introducing Slash](/img/slash-introducing.svg) diff --git a/website-1.x/blog/2018-04-30-How-I-Converted-Profilo-To-Docusaurus.md b/website-1.x/blog/2018-04-30-How-I-Converted-Profilo-To-Docusaurus.md index 52e4f7c869f8..fc92dd0088d2 100644 --- a/website-1.x/blog/2018-04-30-How-I-Converted-Profilo-To-Docusaurus.md +++ b/website-1.x/blog/2018-04-30-How-I-Converted-Profilo-To-Docusaurus.md @@ -4,6 +4,7 @@ author: Christine Abernathy authorURL: http://twitter.com/abernathyca authorFBID: 1424840234 authorTwitter: abernathyca +tags: [profilo, adoption] --- > *“Joel and I were discussing having a website and how it would have been great to launch with it. So I challenged myself to add Docusaurus support. It took just over an hour and a half. I'm going to send you a PR with the addition so you can take a look and see if you like it. Your workflow for adding docs wouldn't be much different from editing those markdown files.”* diff --git a/website-1.x/blog/2018-09-11-Towards-Docusaurus-2.md b/website-1.x/blog/2018-09-11-Towards-Docusaurus-2.md index ac74ea3f40f0..00893f3d9c69 100644 --- a/website-1.x/blog/2018-09-11-Towards-Docusaurus-2.md +++ b/website-1.x/blog/2018-09-11-Towards-Docusaurus-2.md @@ -5,6 +5,7 @@ authorTitle: Maintainer of Docusaurus authorURL: https://github.com/endiliey authorImageURL: https://avatars1.githubusercontent.com/u/17883920?s=460&v=4 authorTwitter: endiliey +tags: [new, adoption] --- Docusaurus was [officially announced](https://docusaurus.io/blog/2017/12/14/introducing-docusaurus) over nine months ago as a way to easily build open source documentation websites. Since then, it has amassed over 8,600 GitHub Stars, and is used by many popular open source projects such as [React Native](https://facebook.github.io/react-native/), [Babel](https://babeljs.io/), [Jest](https://jestjs.io/), [Reason](https://reasonml.github.io/) and [Prettier](https://prettier.io/). @@ -107,7 +108,7 @@ If you've read the post up until to this point, you should be able to notice tha The exact list of breaking changes is not totally known yet as development is not 100% finalized. However, one thing that I will highlight is that we will deprecate a lot of options in `siteConfig.js` and we plan to keep it as lean as possible. For example, the `cleanUrl` siteConfig will be deprecated as all the URL for Docusaurus 2 sites will be without the `.html` suffix. -Our goal is that most sites should be able to upgrade to Docusaurus 2 without a lot of pain. We will also include a migration guide when we release Docusaurus 2. When the times come, feel free to ping us on [Discord](https://discord.gg/docusaurus) or [Twitter](https://twitter.com/docusaurus) for questions and help. +Our goal is that most sites should be able to upgrade to Docusaurus 2 without a lot of pain. We will also include a migration guide when we release Docusaurus 2. When the times come, feel free to ping us on [Discord](https://discord.gg/docusaurus) or [Twitter](https://twitter.com/docusaurus) for questions and help. ### When is the release of Docusaurus 2? diff --git a/website-1.x/blog/2018-12-14-Happy-First-Birthday-Slash.md b/website-1.x/blog/2018-12-14-Happy-First-Birthday-Slash.md index 06366432b788..97b54fa7a12a 100644 --- a/website-1.x/blog/2018-12-14-Happy-First-Birthday-Slash.md +++ b/website-1.x/blog/2018-12-14-Happy-First-Birthday-Slash.md @@ -5,6 +5,7 @@ authorTitle: Co-creator of Docusaurus authorURL: https://github.com/JoelMarcey authorFBID: 611217057 authorTwitter: JoelMarcey +tags: [birth] --- ![First Birthday Slash](/img/docusaurus-slash-first-birthday.svg)