diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts index b96bdeb67a..fc663e557a 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts @@ -28,6 +28,8 @@ describe('loadBlog', () => { } as LoadContext, { path: pluginPath, + editUrl: + 'https://github.com/facebook/docusaurus/edit/master/website-1x', }, ); const {blogPosts} = await plugin.loadContent(); @@ -50,12 +52,15 @@ describe('loadBlog', () => { ...blogPosts.find(v => v.metadata.title === 'date-matter').metadata, ...{prevItem: undefined}, }).toEqual({ + editUrl: + 'https://github.com/facebook/docusaurus/edit/master/website-1x/blog/date-matter.md', permalink: '/blog/2019/01/01/date-matter', readingTime: 0.02, source: path.join('@site', pluginPath, 'date-matter.md'), title: 'date-matter', description: `date inside front matter`, date: new Date('2019-01-01'), + prevItem: undefined, tags: [], nextItem: { permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash', @@ -68,6 +73,8 @@ describe('loadBlog', () => { blogPosts.find(v => v.metadata.title === 'Happy 1st Birthday Slash!') .metadata, ).toEqual({ + editUrl: + 'https://github.com/facebook/docusaurus/edit/master/website-1x/blog/2018-12-14-Happy-First-Birthday-Slash.md', permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash', readingTime: 0.01, source: path.join( @@ -90,6 +97,8 @@ describe('loadBlog', () => { ...blogPosts.find(v => v.metadata.title === 'no date').metadata, ...{prevItem: undefined}, }).toEqual({ + editUrl: + 'https://github.com/facebook/docusaurus/edit/master/website-1x/blog/no date.md', permalink: noDatePermalink, readingTime: 0.01, source: noDateSource, @@ -97,6 +106,7 @@ describe('loadBlog', () => { description: `no date`, date: noDateSourceBirthTime, tags: [], + prevItem: undefined, nextItem: { permalink: '/blog/2020/02/27/draft', title: 'draft', diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 863a3d2efc..aa876e49f0 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -11,7 +11,12 @@ import path from 'path'; import readingTime from 'reading-time'; import {Feed} from 'feed'; import {PluginOptions, BlogPost, DateLink} from './types'; -import {parse, normalizeUrl, aliasedSitePath} from '@docusaurus/utils'; +import { + parse, + normalizeUrl, + aliasedSitePath, + getEditUrl, +} from '@docusaurus/utils'; import {LoadContext} from '@docusaurus/types'; export function truncate(fileString: string, truncateMarker: RegExp) { @@ -86,7 +91,13 @@ export async function generateBlogPosts( {siteConfig, siteDir}: LoadContext, options: PluginOptions, ) { - const {include, routeBasePath, truncateMarker, showReadingTime} = options; + const { + include, + routeBasePath, + truncateMarker, + showReadingTime, + editUrl, + } = options; if (!fs.existsSync(blogDir)) { return []; @@ -103,8 +114,12 @@ export async function generateBlogPosts( blogFiles.map(async (relativeSource: string) => { const source = path.join(blogDir, relativeSource); const aliasedSource = aliasedSitePath(source, siteDir); + const refDir = path.parse(blogDir).dir; + const relativePath = path.relative(refDir, source); const blogFileName = path.basename(relativeSource); + const editBlogUrl = getEditUrl(relativePath, editUrl); + const fileString = await fs.readFile(source, 'utf-8'); const {frontMatter, content, excerpt} = parse(fileString); @@ -140,6 +155,7 @@ export async function generateBlogPosts( routeBasePath, frontMatter.id || toUrl({date, link: linkName}), ]), + editUrl: editBlogUrl, source: aliasedSource, description: frontMatter.description || excerpt, date, diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index 8dd9cecd53..fe833e4834 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -43,6 +43,7 @@ const DEFAULT_OPTIONS: PluginOptions = { showReadingTime: true, remarkPlugins: [], rehypePlugins: [], + editUrl: undefined, truncateMarker: //, // Regex. }; diff --git a/packages/docusaurus-plugin-content-blog/src/types.ts b/packages/docusaurus-plugin-content-blog/src/types.ts index 174e67ad6e..80c9ddd6b4 100644 --- a/packages/docusaurus-plugin-content-blog/src/types.ts +++ b/packages/docusaurus-plugin-content-blog/src/types.ts @@ -39,6 +39,7 @@ export interface PluginOptions { copyright: string; language?: string; }; + editUrl?: string; } export interface BlogTags { @@ -82,6 +83,7 @@ export interface MetaData { prevItem?: Paginator; nextItem?: Paginator; truncated: boolean; + editUrl?: string; } export interface Paginator { diff --git a/packages/docusaurus-plugin-content-docs/src/metadata.ts b/packages/docusaurus-plugin-content-docs/src/metadata.ts index 1a276ca592..6dfd498243 100644 --- a/packages/docusaurus-plugin-content-docs/src/metadata.ts +++ b/packages/docusaurus-plugin-content-docs/src/metadata.ts @@ -11,7 +11,7 @@ import { parse, aliasedSitePath, normalizeUrl, - posixPath, + getEditUrl, } from '@docusaurus/utils'; import {LoadContext} from '@docusaurus/types'; @@ -90,9 +90,7 @@ export default async function processMetadata({ const relativePath = path.relative(siteDir, filePath); - const docsEditUrl = editUrl - ? normalizeUrl([editUrl, posixPath(relativePath)]) - : undefined; + const docsEditUrl = getEditUrl(relativePath, editUrl); const {frontMatter = {}, excerpt} = parse(await fileStringPromise); const {sidebar_label, custom_edit_url} = frontMatter; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.js b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.js index ad3c90d02e..a939baff25 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.js +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.js @@ -14,8 +14,10 @@ import BlogPostPaginator from '@theme/BlogPostPaginator'; function BlogPostPage(props) { const {content: BlogPostContents} = props; const {frontMatter, metadata} = BlogPostContents; + const {title, description, nextItem, prevItem, editUrl} = metadata; + return ( - + {BlogPostContents && (
@@ -26,12 +28,30 @@ function BlogPostPage(props) { isBlogPostPage> - {(metadata.nextItem || metadata.prevItem) && ( +
+ {editUrl && ( + + + + + + + Edit this page + + )} +
+ {(nextItem || prevItem) && (
- +
)}
diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index 3336bbdfbe..a689d5d071 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -273,3 +273,9 @@ export function aliasedSitePath(filePath: string, siteDir: string) { // the '@site'. Let webpack loader resolve it. return `@site/${relativePath}`; } + +export function getEditUrl(fileRelativePath: string, editUrl?: string) { + return editUrl + ? normalizeUrl([editUrl, posixPath(fileRelativePath)]) + : undefined; +} diff --git a/website/docs/using-plugins.md b/website/docs/using-plugins.md index cbce525bf1..33e05c32a1 100644 --- a/website/docs/using-plugins.md +++ b/website/docs/using-plugins.md @@ -149,6 +149,11 @@ module.exports = { * relative to site dir */ path: 'blog', + /** + * URL for editing a blog post, example: 'https://github.com/facebook/docusaurus/edit/master/website/blog/' + */ + editUrl: + 'https://github.com/facebook/docusaurus/edit/master/website/blog/', /** * URL route for the blog section of your site * do not include trailing slash diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 1017cdc230..31a8a5e51c 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -46,6 +46,8 @@ module.exports = { }, blog: { path: '../website-1.x/blog', + editUrl: + 'https://github.com/facebook/docusaurus/edit/master/website-1.x/', postsPerPage: 3, feedOptions: { type: 'all',