Skip to content

Commit

Permalink
feat(v2): editUrl function for advanced use-cases (#4121)
Browse files Browse the repository at this point in the history
* EditUrl function

* normalize blog/docs regarding the editUrl feature + editUrl function

* editUrl fn => always inject posix style relative paths, make tests more reliable
(see also #4124)

* fix editUrl on windows
  • Loading branch information
slorber authored Jan 29, 2021
1 parent 15c50e2 commit be7b5dc
Show file tree
Hide file tree
Showing 15 changed files with 368 additions and 69 deletions.
112 changes: 86 additions & 26 deletions packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@ import path from 'path';
import pluginContentBlog from '../index';
import {DocusaurusConfig, LoadContext, I18n} from '@docusaurus/types';
import {PluginOptionSchema} from '../pluginOptionSchema';
import {PluginOptions, EditUrlFunction} from '../types';
import Joi from 'joi';

const DefaultI18N: I18n = {
currentLocale: 'en',
locales: ['en'],
defaultLocale: 'en',
localeConfigs: {},
};

function validateAndNormalize(schema, options) {
function validateAndNormalize(
schema: Joi.ObjectSchema,
options: Partial<PluginOptions>,
) {
const {value, error} = schema.validate(options);
if (error) {
throw error;
Expand All @@ -27,8 +33,14 @@ function validateAndNormalize(schema, options) {
}

describe('loadBlog', () => {
const pluginPath = 'blog';
const getBlogPosts = async (siteDir) => {
const PluginPath = 'blog';

const BaseEditUrl = 'https://baseEditUrl.com/edit';

const getBlogPosts = async (
siteDir: string,
pluginOptions: Partial<PluginOptions> = {},
) => {
const generatedFilesDir: string = path.resolve(siteDir, '.docusaurus');
const siteConfig = {
title: 'Hello',
Expand All @@ -43,12 +55,12 @@ describe('loadBlog', () => {
i18n: DefaultI18N,
} as LoadContext,
validateAndNormalize(PluginOptionSchema, {
path: pluginPath,
editUrl:
'https://github.com/facebook/docusaurus/edit/master/website-1x',
path: PluginPath,
editUrl: BaseEditUrl,
...pluginOptions,
}),
);
const {blogPosts} = await plugin.loadContent();
const {blogPosts} = (await plugin.loadContent!())!;

return blogPosts;
};
Expand All @@ -58,14 +70,13 @@ describe('loadBlog', () => {
const blogPosts = await getBlogPosts(siteDir);

expect({
...blogPosts.find((v) => v.metadata.title === 'date-matter').metadata,
...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',
editUrl: `${BaseEditUrl}/blog/date-matter.md`,
permalink: '/blog/date-matter',
readingTime: 0.02,
source: path.posix.join('@site', pluginPath, 'date-matter.md'),
source: path.posix.join('@site', PluginPath, 'date-matter.md'),
title: 'date-matter',
description: `date inside front matter`,
date: new Date('2019-01-01'),
Expand All @@ -81,10 +92,9 @@ describe('loadBlog', () => {
expect(
blogPosts.find(
(v) => v.metadata.title === 'Happy 1st Birthday Slash! (translated)',
).metadata,
)!.metadata,
).toEqual({
editUrl:
'https://github.com/facebook/docusaurus/edit/master/website-1x/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md',
editUrl: `${BaseEditUrl}/blog/2018-12-14-Happy-First-Birthday-Slash.md`,
permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash',
readingTime: 0.015,
source: path.posix.join(
Expand All @@ -105,14 +115,13 @@ describe('loadBlog', () => {
});

expect({
...blogPosts.find((v) => v.metadata.title === 'Complex Slug').metadata,
...blogPosts.find((v) => v.metadata.title === 'Complex Slug')!.metadata,
...{prevItem: undefined},
}).toEqual({
editUrl:
'https://github.com/facebook/docusaurus/edit/master/website-1x/blog/complex-slug.md',
editUrl: `${BaseEditUrl}/blog/complex-slug.md`,
permalink: '/blog/hey/my super path/héllô',
readingTime: 0.015,
source: path.posix.join('@site', pluginPath, 'complex-slug.md'),
source: path.posix.join('@site', PluginPath, 'complex-slug.md'),
title: 'Complex Slug',
description: `complex url slug`,
prevItem: undefined,
Expand All @@ -126,14 +135,13 @@ describe('loadBlog', () => {
});

expect({
...blogPosts.find((v) => v.metadata.title === 'Simple Slug').metadata,
...blogPosts.find((v) => v.metadata.title === 'Simple Slug')!.metadata,
...{prevItem: undefined},
}).toEqual({
editUrl:
'https://github.com/facebook/docusaurus/edit/master/website-1x/blog/simple-slug.md',
editUrl: `${BaseEditUrl}/blog/simple-slug.md`,
permalink: '/blog/simple/slug',
readingTime: 0.015,
source: path.posix.join('@site', pluginPath, 'simple-slug.md'),
source: path.posix.join('@site', PluginPath, 'simple-slug.md'),
title: 'Simple Slug',
description: `simple url slug`,
prevItem: undefined,
Expand All @@ -147,6 +155,59 @@ describe('loadBlog', () => {
});
});

test('edit url with editLocalizedBlogs true', async () => {
const siteDir = path.join(__dirname, '__fixtures__', 'website');
const blogPosts = await getBlogPosts(siteDir, {editLocalizedFiles: true});

const localizedBlogPost = blogPosts.find(
(v) => v.metadata.title === 'Happy 1st Birthday Slash! (translated)',
)!;

expect(localizedBlogPost.metadata.editUrl).toEqual(
`${BaseEditUrl}/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md`,
);
});

test('edit url with editUrl function', async () => {
const siteDir = path.join(__dirname, '__fixtures__', 'website');

const hardcodedEditUrl = 'hardcoded-edit-url';
const editUrlFunction: EditUrlFunction = jest.fn(() => hardcodedEditUrl);

const blogPosts = await getBlogPosts(siteDir, {editUrl: editUrlFunction});

blogPosts.forEach((blogPost) => {
expect(blogPost.metadata.editUrl).toEqual(hardcodedEditUrl);
});

expect(editUrlFunction).toHaveBeenCalledTimes(5);
expect(editUrlFunction).toHaveBeenCalledWith({
blogDirPath: 'blog',
blogPath: 'date-matter.md',
locale: 'en',
});
expect(editUrlFunction).toHaveBeenCalledWith({
blogDirPath: 'blog',
blogPath: 'draft.md',
locale: 'en',
});
expect(editUrlFunction).toHaveBeenCalledWith({
blogDirPath: 'blog',
blogPath: 'complex-slug.md',
locale: 'en',
});
expect(editUrlFunction).toHaveBeenCalledWith({
blogDirPath: 'blog',
blogPath: 'simple-slug.md',
locale: 'en',
});
expect(editUrlFunction).toHaveBeenCalledWith({
blogDirPath: 'i18n/en/docusaurus-plugin-content-blog',
blogPath: '2018-12-14-Happy-First-Birthday-Slash.md',
locale: 'en',
});
});

test('draft blog post not exists in production build', async () => {
process.env.NODE_ENV = 'production';
const siteDir = path.join(__dirname, '__fixtures__', 'website');
Expand All @@ -162,17 +223,16 @@ describe('loadBlog', () => {
'website-blog-without-date',
);
const blogPosts = await getBlogPosts(siteDir);
const noDateSource = path.posix.join('@site', pluginPath, 'no date.md');
const noDateSource = path.posix.join('@site', PluginPath, 'no date.md');
const noDateSourceBirthTime = (
await fs.stat(noDateSource.replace('@site', siteDir))
).birthtime;

expect({
...blogPosts.find((v) => v.metadata.title === 'no date').metadata,
...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',
editUrl: `${BaseEditUrl}/blog/no date.md`,
permalink: '/blog/no date',
readingTime: 0.01,
source: noDateSource,
Expand Down
42 changes: 36 additions & 6 deletions packages/docusaurus-plugin-content-blog/src/blogUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
aliasedSitePath,
getEditUrl,
getFolderContainingFile,
posixPath,
} from '@docusaurus/utils';
import {LoadContext} from '@docusaurus/types';
import {keyBy} from 'lodash';
Expand Down Expand Up @@ -99,15 +100,15 @@ export async function generateBlogFeed(

export async function generateBlogPosts(
contentPaths: BlogContentPaths,
{siteConfig, siteDir}: LoadContext,
{siteConfig, siteDir, i18n}: LoadContext,
options: PluginOptions,
): Promise<BlogPost[]> {
const {
include,
routeBasePath,
truncateMarker,
showReadingTime,
editUrl,
editUrl: siteEditUrl,
} = options;

if (!fs.existsSync(contentPaths.contentPath)) {
Expand All @@ -124,18 +125,47 @@ export async function generateBlogPosts(
await Promise.all(
blogSourceFiles.map(async (blogSourceFile: string) => {
// Lookup in localized folder in priority
const contentPath = await getFolderContainingFile(
const blogDirPath = await getFolderContainingFile(
getContentPathList(contentPaths),
blogSourceFile,
);

const source = path.join(contentPath, blogSourceFile);
const source = path.join(blogDirPath, blogSourceFile);

const aliasedSource = aliasedSitePath(source, siteDir);

const relativePath = path.relative(siteDir, source);
const blogFileName = path.basename(blogSourceFile);

const editBlogUrl = getEditUrl(relativePath, editUrl);
function getBlogEditUrl() {
const blogPathRelative = path.relative(
blogDirPath,
path.resolve(source),
);

if (typeof siteEditUrl === 'function') {
return siteEditUrl({
blogDirPath: posixPath(path.relative(siteDir, blogDirPath)),
blogPath: posixPath(blogPathRelative),
locale: i18n.currentLocale,
});
} else if (typeof siteEditUrl === 'string') {
const isLocalized = blogDirPath === contentPaths.contentPathLocalized;
const fileContentPath =
isLocalized && options.editLocalizedFiles
? contentPaths.contentPathLocalized
: contentPaths.contentPath;

const contentPathEditUrl = normalizeUrl([
siteEditUrl,
posixPath(path.relative(siteDir, fileContentPath)),
]);

return getEditUrl(blogPathRelative, contentPathEditUrl);
} else {
return undefined;
}
}
const editBlogUrl = getBlogEditUrl();

const {frontMatter, content, excerpt} = await parseMarkdownFile(source);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const DEFAULT_OPTIONS = {
include: ['*.md', '*.mdx'],
routeBasePath: 'blog',
path: 'blog',
editLocalizedFiles: false,
};

export const PluginOptionSchema = Joi.object({
Expand Down Expand Up @@ -67,7 +68,8 @@ export const PluginOptionSchema = Joi.object({
remarkPlugins: RemarkPluginsSchema.default(DEFAULT_OPTIONS.remarkPlugins),
rehypePlugins: RehypePluginsSchema.default(DEFAULT_OPTIONS.rehypePlugins),
admonitions: AdmonitionsSchema.default(DEFAULT_OPTIONS.admonitions),
editUrl: URISchema,
editUrl: Joi.alternatives().try(URISchema, Joi.function()),
editLocalizedFiles: Joi.boolean().default(DEFAULT_OPTIONS.editLocalizedFiles),
truncateMarker: Joi.object().default(DEFAULT_OPTIONS.truncateMarker),
beforeDefaultRemarkPlugins: RemarkPluginsSchema.default(
DEFAULT_OPTIONS.beforeDefaultRemarkPlugins,
Expand Down
9 changes: 8 additions & 1 deletion packages/docusaurus-plugin-content-blog/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ export interface DateLink {

export type FeedType = 'rss' | 'atom';

export type EditUrlFunction = (editUrlParams: {
blogDirPath: string;
blogPath: string;
locale: string;
}) => string | undefined;

export interface PluginOptions {
id?: string;
path: string;
Expand Down Expand Up @@ -57,7 +63,8 @@ export interface PluginOptions {
copyright: string;
language?: string;
};
editUrl?: string;
editUrl?: string | EditUrlFunction;
editLocalizedFiles?: boolean;
admonitions: Record<string, unknown>;
}

Expand Down
Loading

0 comments on commit be7b5dc

Please sign in to comment.