Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(v2): frontmatter-less: read first heading as title and use it in front-matter #4485

Merged
merged 9 commits into from
Mar 23, 2021
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