Skip to content

Commit

Permalink
feat(v2): read first heading as title and use it in front-matter (#4485)
Browse files Browse the repository at this point in the history
* feat(v2): read first heading as title and pass it to front-matter

* fix(v2): always trim content after extracting front-matter

* fix(v2): remove heading from rss and keep duplicate heading

* fix(v2): rollback some unnecessary comment changes

* test(v2): add unit tests to blog

* test(v2): add unit tests to docs

* test(v2): correct issue on windows

* test(v2): add additional test cases
  • Loading branch information
armano2 authored Mar 23, 2021
1 parent fb372c5 commit ea13c94
Show file tree
Hide file tree
Showing 16 changed files with 448 additions and 35 deletions.
4 changes: 2 additions & 2 deletions packages/docusaurus-mdx-loader/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"access": "public"
},
"scripts": {
"build": "tsc"
"build": "tsc",
"watch": "tsc --watch"
},
"repository": {
"type": "git",
Expand All @@ -27,7 +28,6 @@
"file-loader": "^6.2.0",
"fs-extra": "^9.1.0",
"github-slugger": "^1.3.0",
"gray-matter": "^4.0.2",
"loader-utils": "^2.0.0",
"mdast-util-to-string": "^2.0.0",
"remark-emoji": "^2.1.0",
Expand Down
19 changes: 11 additions & 8 deletions packages/docusaurus-mdx-loader/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const {getOptions} = require('loader-utils');
const {readFile} = require('fs-extra');
const mdx = require('@mdx-js/mdx');
const emoji = require('remark-emoji');
const matter = require('gray-matter');
const {readFrontMatter} = require('@docusaurus/utils');
const stringifyObject = require('stringify-object');
const headings = require('./remark/headings');
const toc = require('./remark/toc');
Expand All @@ -24,9 +24,15 @@ const DEFAULT_OPTIONS = {

module.exports = async function docusaurusMdxLoader(fileString) {
const callback = this.async();

const {data, content} = matter(fileString);
const reqOptions = getOptions(this) || {};

const {frontMatter, content, hasFrontMatter} = readFrontMatter(
fileString,
this.resourcePath,
{},
reqOptions.removeTitleHeading,
);

const options = {
...reqOptions,
remarkPlugins: [
Expand Down Expand Up @@ -58,7 +64,7 @@ module.exports = async function docusaurusMdxLoader(fileString) {
return callback(err);
}

let exportStr = `export const frontMatter = ${stringifyObject(data)};`;
let exportStr = `export const frontMatter = ${stringifyObject(frontMatter)};`;

// Read metadata for this MDX and export it.
if (options.metadataPath && typeof options.metadataPath === 'function') {
Expand All @@ -77,10 +83,7 @@ module.exports = async function docusaurusMdxLoader(fileString) {
options.forbidFrontMatter &&
typeof options.forbidFrontMatter === 'function'
) {
if (
options.forbidFrontMatter(this.resourcePath) &&
Object.keys(data).length > 0
) {
if (options.forbidFrontMatter(this.resourcePath) && hasFrontMatter) {
return callback(new Error(`Front matter is forbidden in this file`));
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
date: 2019-01-02
---

# some heading
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,33 @@ describe('loadBlog', () => {
tags: [],
truncated: false,
});

expect({
...blogPosts.find((v) => v.metadata.title === 'some heading')!.metadata,
prevItem: undefined,
}).toEqual({
editUrl: `${BaseEditUrl}/blog/heading-as-title.md`,
permalink: '/blog/heading-as-title',
readingTime: 0,
source: path.posix.join('@site', PluginPath, 'heading-as-title.md'),
title: 'some heading',
description: '',
date: new Date('2019-01-02'),
formattedDate: 'January 2, 2019',
prevItem: undefined,
tags: [],
nextItem: {
permalink: '/blog/date-matter',
title: 'date-matter',
},
truncated: false,
});
});

test('simple website blog dates localized', async () => {
const siteDir = path.join(__dirname, '__fixtures__', 'website');
const blogPostsFrench = await getBlogPosts(siteDir, {}, getI18n('fr'));
expect(blogPostsFrench).toHaveLength(5);
expect(blogPostsFrench).toHaveLength(6);
expect(blogPostsFrench[0].metadata.formattedDate).toMatchInlineSnapshot(
`"16 août 2020"`,
);
Expand All @@ -180,9 +201,12 @@ describe('loadBlog', () => {
`"27 février 2020"`,
);
expect(blogPostsFrench[3].metadata.formattedDate).toMatchInlineSnapshot(
`"1 janvier 2019"`,
`"2 janvier 2019"`,
);
expect(blogPostsFrench[4].metadata.formattedDate).toMatchInlineSnapshot(
`"1 janvier 2019"`,
);
expect(blogPostsFrench[5].metadata.formattedDate).toMatchInlineSnapshot(
`"14 décembre 2018"`,
);
});
Expand Down Expand Up @@ -212,7 +236,7 @@ describe('loadBlog', () => {
expect(blogPost.metadata.editUrl).toEqual(hardcodedEditUrl);
});

expect(editUrlFunction).toHaveBeenCalledTimes(5);
expect(editUrlFunction).toHaveBeenCalledTimes(6);
expect(editUrlFunction).toHaveBeenCalledWith({
blogDirPath: 'blog',
blogPath: 'date-matter.md',
Expand Down Expand Up @@ -243,6 +267,12 @@ describe('loadBlog', () => {
permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash',
locale: 'en',
});
expect(editUrlFunction).toHaveBeenCalledWith({
blogDirPath: 'blog',
blogPath: 'heading-as-title.md',
locale: 'en',
permalink: '/blog/heading-as-title',
});
});

test('draft blog post not exists in production build', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# My heading as title
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ Object {
"path": "/docs/foo/bazSlug.html",
"sidebar": "docs",
},
Object {
"id": "headingAsTitle",
"path": "/docs/headingAsTitle",
"sidebar": undefined,
},
Object {
"id": "hello",
"path": "/docs/",
Expand Down Expand Up @@ -172,6 +177,17 @@ Object {
\\"title\\": \\"Hello, World !\\",
\\"permalink\\": \\"/docs/\\"
}
}",
"site-docs-heading-as-title-md-c6d.json": "{
\\"unversionedId\\": \\"headingAsTitle\\",
\\"id\\": \\"headingAsTitle\\",
\\"isDocsHomePage\\": false,
\\"title\\": \\"My heading as title\\",
\\"description\\": \\"\\",
\\"source\\": \\"@site/docs/headingAsTitle.md\\",
\\"slug\\": \\"/headingAsTitle\\",
\\"permalink\\": \\"/docs/headingAsTitle\\",
\\"version\\": \\"current\\"
}",
"site-docs-hello-md-9df.json": "{
\\"unversionedId\\": \\"hello\\",
Expand Down Expand Up @@ -383,6 +399,11 @@ Object {
"path": "/docs/foo/bazSlug.html",
"sidebar": "docs",
},
Object {
"id": "headingAsTitle",
"path": "/docs/headingAsTitle",
"sidebar": undefined,
},
Object {
"id": "hello",
"path": "/docs/",
Expand Down Expand Up @@ -494,6 +515,14 @@ Array [
},
"path": "/docs/foo/bazSlug.html",
},
Object {
"component": "@theme/DocItem",
"exact": true,
"modules": Object {
"content": "@site/docs/headingAsTitle.md",
},
"path": "/docs/headingAsTitle",
},
Object {
"component": "@theme/DocItem",
"exact": true,
Expand Down Expand Up @@ -579,6 +608,7 @@ These sidebar document ids do not exist:
Available document ids=
- foo/bar
- foo/baz
- headingAsTitle
- hello
- ipsum
- lorem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ describe('simple site', () => {
'rootRelativeSlug.md',
'rootResolvedSlug.md',
'rootTryToEscapeSlug.md',
'headingAsTitle.md',
'foo/bar.md',
'foo/baz.md',
'slugs/absoluteSlug.md',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ describe('simple website', () => {
expect(isMatch('docs/hello.js', matchPattern)).toEqual(false);
expect(isMatch('docs/super.mdl', matchPattern)).toEqual(false);
expect(isMatch('docs/mdx', matchPattern)).toEqual(false);
expect(isMatch('docs/headingAsTitle.md', matchPattern)).toEqual(true);
expect(isMatch('sidebars.json', matchPattern)).toEqual(true);
expect(isMatch('versioned_docs/hello.md', matchPattern)).toEqual(false);
expect(isMatch('hello.md', matchPattern)).toEqual(false);
Expand Down
2 changes: 1 addition & 1 deletion packages/docusaurus-plugin-content-docs/src/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export function processDocMetadata({
// ex: myDoc -> .
const docsFileDirName = path.dirname(source);

const {frontMatter = {}, excerpt} = parseMarkdownString(content);
const {frontMatter = {}, excerpt} = parseMarkdownString(content, source);
const {
sidebar_label: sidebarLabel,
custom_edit_url: customEditURL,
Expand Down
1 change: 1 addition & 0 deletions packages/docusaurus-plugin-content-pages/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ export default function pluginContentPages(
rehypePlugins,
beforeDefaultRehypePlugins,
beforeDefaultRemarkPlugins,
removeTitleHeading: false,
staticDir: path.join(siteDir, STATIC_DIR_NAME),
// Note that metadataPath must be the same/in-sync as
// the path from createData for each MDX.
Expand Down
4 changes: 4 additions & 0 deletions packages/docusaurus-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,9 @@
},
"engines": {
"node": ">=12.13.0"
},
"devDependencies": {
"@types/dedent": "^0.7.0",
"dedent": "^0.7.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`load utils: parseMarkdown parseMarkdownString should delete only first heading 1`] = `
Object {
"content": "
test test test test test test
test test test # test bar
# test
### test",
"excerpt": "",
"frontMatter": Object {
"title": "test",
},
"hasFrontMatter": false,
}
`;

exports[`load utils: parseMarkdown parseMarkdownString should ignore heading if its not a first text 1`] = `
Object {
"content": "foo
# test",
"excerpt": "foo",
"frontMatter": Object {},
"hasFrontMatter": false,
}
`;

exports[`load utils: parseMarkdown parseMarkdownString should parse first heading as title 1`] = `
Object {
"content": "",
"excerpt": "",
"frontMatter": Object {
"title": "test",
},
"hasFrontMatter": false,
}
`;

exports[`load utils: parseMarkdown parseMarkdownString should preserve front-matter title and warn about duplication 1`] = `
Object {
"content": "# test",
"excerpt": "test",
"frontMatter": Object {
"title": "title",
},
"hasFrontMatter": true,
}
`;

exports[`load utils: parseMarkdown parseMarkdownString should read front matter 1`] = `
Object {
"content": "",
"excerpt": undefined,
"frontMatter": Object {
"title": "test",
},
"hasFrontMatter": true,
}
`;

exports[`load utils: parseMarkdown readFrontMatter should delete only first heading 1`] = `
Object {
"content": "test test test # test bar
# test
### test",
"excerpt": "",
"frontMatter": Object {
"title": "test",
},
"hasFrontMatter": false,
}
`;

exports[`load utils: parseMarkdown readFrontMatter should ignore heading if its not a first text 1`] = `
Object {
"content": "foo
# test",
"excerpt": "",
"frontMatter": Object {},
"hasFrontMatter": false,
}
`;

exports[`load utils: parseMarkdown readFrontMatter should parse first heading as title 1`] = `
Object {
"content": "",
"excerpt": "",
"frontMatter": Object {
"title": "test",
},
"hasFrontMatter": false,
}
`;

exports[`load utils: parseMarkdown readFrontMatter should parse first heading as title and keep it in content 1`] = `
Object {
"content": "# test",
"excerpt": "",
"frontMatter": Object {
"title": "test",
},
"hasFrontMatter": false,
}
`;

exports[`load utils: parseMarkdown readFrontMatter should parse front-matter and ignore h2 1`] = `
Object {
"content": "## test",
"excerpt": "",
"frontMatter": Object {
"title": "title",
},
"hasFrontMatter": true,
}
`;

exports[`load utils: parseMarkdown readFrontMatter should preserve front-matter title and warn about duplication 1`] = `
Object {
"content": "# test",
"excerpt": "",
"frontMatter": Object {
"title": "title",
},
"hasFrontMatter": true,
}
`;

exports[`load utils: parseMarkdown readFrontMatter should read front matter 1`] = `
Object {
"content": "",
"excerpt": "",
"frontMatter": Object {
"title": "test",
},
"hasFrontMatter": true,
}
`;
Loading

0 comments on commit ea13c94

Please sign in to comment.