mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-10 23:57:22 +02:00
fix(v2): fix contentTitle issues when markdown h1 title contains code blocks (#4882)
* attempt to fix contentTitle issues when markdown h1 title contains inline code blocks * mention hide_title frontmatter only prevents frontmatter.title from being added in the dom (not a markdown # title in content) * alwayss insert MainHeading under the div.markdown container for consistency * ensure MainHeading has no useless id * revert https://github.com/facebook/docusaurus/pull/4859 as it's now useless: docMeta.title contains the text/frontmatter title in priority over the contentTitle * fix docs test after revert * improve markdownParser and fix tests * fix docs tests * markdownParser: restore option to remove contentTitle (mostly for blog plugin) * use removeContentTitle for blog
This commit is contained in:
parent
85e87b560e
commit
57806798c5
20 changed files with 246 additions and 178 deletions
|
@ -31,9 +31,8 @@ module.exports = async function docusaurusMdxLoader(fileString) {
|
||||||
|
|
||||||
const {frontMatter, content: contentWithTitle} = parseFrontMatter(fileString);
|
const {frontMatter, content: contentWithTitle} = parseFrontMatter(fileString);
|
||||||
|
|
||||||
// By default, will remove the markdown title from the content
|
const {content, contentTitle} = parseMarkdownContentTitle(contentWithTitle, {
|
||||||
const {content} = parseMarkdownContentTitle(contentWithTitle, {
|
removeContentTitle: reqOptions.removeContentTitle,
|
||||||
keepContentTitle: reqOptions.keepContentTitle,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const hasFrontMatter = Object.keys(frontMatter).length > 0;
|
const hasFrontMatter = Object.keys(frontMatter).length > 0;
|
||||||
|
@ -69,7 +68,11 @@ module.exports = async function docusaurusMdxLoader(fileString) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
let exportStr = `export const frontMatter = ${stringifyObject(frontMatter)};`;
|
let exportStr = ``;
|
||||||
|
exportStr += `\nexport const frontMatter = ${stringifyObject(frontMatter)};`;
|
||||||
|
exportStr += `\nexport const contentTitle = ${stringifyObject(
|
||||||
|
contentTitle,
|
||||||
|
)};`;
|
||||||
|
|
||||||
// Read metadata for this MDX and export it.
|
// Read metadata for this MDX and export it.
|
||||||
if (options.metadataPath && typeof options.metadataPath === 'function') {
|
if (options.metadataPath && typeof options.metadataPath === 'function') {
|
||||||
|
|
|
@ -146,7 +146,7 @@ export async function generateBlogPosts(
|
||||||
content,
|
content,
|
||||||
contentTitle,
|
contentTitle,
|
||||||
excerpt,
|
excerpt,
|
||||||
} = await parseMarkdownFile(source);
|
} = await parseMarkdownFile(source, {removeContentTitle: true});
|
||||||
const frontMatter = validateBlogPostFrontMatter(unsafeFrontMatter);
|
const frontMatter = validateBlogPostFrontMatter(unsafeFrontMatter);
|
||||||
|
|
||||||
const aliasedSource = aliasedSitePath(source, siteDir);
|
const aliasedSource = aliasedSitePath(source, siteDir);
|
||||||
|
|
|
@ -459,6 +459,9 @@ export default function pluginContentBlog(
|
||||||
`${docuHash(aliasedPath)}.json`,
|
`${docuHash(aliasedPath)}.json`,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
// For blog posts a title in markdown is always removed
|
||||||
|
// Blog posts title are rendered separately
|
||||||
|
removeContentTitle: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -168,7 +168,7 @@ Object {
|
||||||
\\"unversionedId\\": \\"foo/bar\\",
|
\\"unversionedId\\": \\"foo/bar\\",
|
||||||
\\"id\\": \\"foo/bar\\",
|
\\"id\\": \\"foo/bar\\",
|
||||||
\\"isDocsHomePage\\": false,
|
\\"isDocsHomePage\\": false,
|
||||||
\\"title\\": \\"Remarkable\\",
|
\\"title\\": \\"Bar\\",
|
||||||
\\"description\\": \\"This is custom description\\",
|
\\"description\\": \\"This is custom description\\",
|
||||||
\\"source\\": \\"@site/docs/foo/bar.md\\",
|
\\"source\\": \\"@site/docs/foo/bar.md\\",
|
||||||
\\"sourceDirName\\": \\"foo\\",
|
\\"sourceDirName\\": \\"foo\\",
|
||||||
|
@ -190,7 +190,7 @@ Object {
|
||||||
\\"unversionedId\\": \\"foo/baz\\",
|
\\"unversionedId\\": \\"foo/baz\\",
|
||||||
\\"id\\": \\"foo/baz\\",
|
\\"id\\": \\"foo/baz\\",
|
||||||
\\"isDocsHomePage\\": false,
|
\\"isDocsHomePage\\": false,
|
||||||
\\"title\\": \\"Baz markdown title\\",
|
\\"title\\": \\"baz\\",
|
||||||
\\"description\\": \\"Images\\",
|
\\"description\\": \\"Images\\",
|
||||||
\\"source\\": \\"@site/docs/foo/baz.md\\",
|
\\"source\\": \\"@site/docs/foo/baz.md\\",
|
||||||
\\"sourceDirName\\": \\"foo\\",
|
\\"sourceDirName\\": \\"foo\\",
|
||||||
|
@ -418,12 +418,12 @@ Object {
|
||||||
\\"items\\": [
|
\\"items\\": [
|
||||||
{
|
{
|
||||||
\\"type\\": \\"link\\",
|
\\"type\\": \\"link\\",
|
||||||
\\"label\\": \\"Remarkable\\",
|
\\"label\\": \\"Bar\\",
|
||||||
\\"href\\": \\"/docs/foo/bar\\"
|
\\"href\\": \\"/docs/foo/bar\\"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
\\"type\\": \\"link\\",
|
\\"type\\": \\"link\\",
|
||||||
\\"label\\": \\"Baz markdown title\\",
|
\\"label\\": \\"baz\\",
|
||||||
\\"href\\": \\"/docs/foo/bazSlug.html\\"
|
\\"href\\": \\"/docs/foo/bazSlug.html\\"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -180,7 +180,7 @@ describe('simple site', () => {
|
||||||
isDocsHomePage: false,
|
isDocsHomePage: false,
|
||||||
permalink: '/docs/foo/bar',
|
permalink: '/docs/foo/bar',
|
||||||
slug: '/foo/bar',
|
slug: '/foo/bar',
|
||||||
title: 'Remarkable',
|
title: 'Bar',
|
||||||
description: 'This is custom description',
|
description: 'This is custom description',
|
||||||
frontMatter: {
|
frontMatter: {
|
||||||
description: 'This is custom description',
|
description: 'This is custom description',
|
||||||
|
@ -254,7 +254,7 @@ describe('simple site', () => {
|
||||||
isDocsHomePage: true,
|
isDocsHomePage: true,
|
||||||
permalink: '/docs/',
|
permalink: '/docs/',
|
||||||
slug: '/',
|
slug: '/',
|
||||||
title: 'Remarkable',
|
title: 'Bar',
|
||||||
description: 'This is custom description',
|
description: 'This is custom description',
|
||||||
frontMatter: {
|
frontMatter: {
|
||||||
description: 'This is custom description',
|
description: 'This is custom description',
|
||||||
|
@ -286,7 +286,7 @@ describe('simple site', () => {
|
||||||
isDocsHomePage: false,
|
isDocsHomePage: false,
|
||||||
permalink: '/docs/foo/bazSlug.html',
|
permalink: '/docs/foo/bazSlug.html',
|
||||||
slug: '/foo/bazSlug.html',
|
slug: '/foo/bazSlug.html',
|
||||||
title: 'Baz markdown title',
|
title: 'baz',
|
||||||
editUrl:
|
editUrl:
|
||||||
'https://github.com/facebook/docusaurus/edit/master/website/docs/foo/baz.md',
|
'https://github.com/facebook/docusaurus/edit/master/website/docs/foo/baz.md',
|
||||||
description: 'Images',
|
description: 'Images',
|
||||||
|
@ -345,7 +345,7 @@ describe('simple site', () => {
|
||||||
isDocsHomePage: false,
|
isDocsHomePage: false,
|
||||||
permalink: '/docs/foo/bazSlug.html',
|
permalink: '/docs/foo/bazSlug.html',
|
||||||
slug: '/foo/bazSlug.html',
|
slug: '/foo/bazSlug.html',
|
||||||
title: 'Baz markdown title',
|
title: 'baz',
|
||||||
editUrl: hardcodedEditUrl,
|
editUrl: hardcodedEditUrl,
|
||||||
description: 'Images',
|
description: 'Images',
|
||||||
frontMatter: {
|
frontMatter: {
|
||||||
|
|
|
@ -375,7 +375,7 @@ describe('simple website', () => {
|
||||||
'foo',
|
'foo',
|
||||||
'bar.md',
|
'bar.md',
|
||||||
),
|
),
|
||||||
title: 'Remarkable',
|
title: 'Bar',
|
||||||
description: 'This is custom description',
|
description: 'This is custom description',
|
||||||
frontMatter: {
|
frontMatter: {
|
||||||
description: 'This is custom description',
|
description: 'This is custom description',
|
||||||
|
|
|
@ -202,11 +202,9 @@ export function processDocMetadata({
|
||||||
numberPrefixParser: options.numberPrefixParser,
|
numberPrefixParser: options.numberPrefixParser,
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO expose both headingTitle+metaTitle to theme?
|
// Note: the title is used by default for page title, sidebar label, pagination buttons...
|
||||||
// Different fallbacks order on purpose!
|
// frontMatter.title should be used in priority over contentTitle (because it can contain markdown/JSX syntax)
|
||||||
// See https://github.com/facebook/docusaurus/issues/4665#issuecomment-825831367
|
const title: string = frontMatter.title ?? contentTitle ?? baseID;
|
||||||
const headingTitle: string = contentTitle ?? frontMatter.title ?? baseID;
|
|
||||||
// const metaTitle: string = frontMatter.title ?? contentTitle ?? baseID;
|
|
||||||
|
|
||||||
const description: string = frontMatter.description ?? excerpt ?? '';
|
const description: string = frontMatter.description ?? excerpt ?? '';
|
||||||
|
|
||||||
|
@ -245,7 +243,7 @@ export function processDocMetadata({
|
||||||
unversionedId,
|
unversionedId,
|
||||||
id,
|
id,
|
||||||
isDocsHomePage,
|
isDocsHomePage,
|
||||||
title: headingTitle,
|
title,
|
||||||
description,
|
description,
|
||||||
source: aliasedSitePath(filePath, siteDir),
|
source: aliasedSitePath(filePath, siteDir),
|
||||||
sourceDirName,
|
sourceDirName,
|
||||||
|
|
|
@ -199,11 +199,7 @@ export default function pluginContentDocs(
|
||||||
nextId,
|
nextId,
|
||||||
} = sidebarsUtils.getDocNavigation(doc.id);
|
} = sidebarsUtils.getDocNavigation(doc.id);
|
||||||
const toDocNavLink = (navDocId: string): DocNavLink => ({
|
const toDocNavLink = (navDocId: string): DocNavLink => ({
|
||||||
// Use frontMatter.title in priority over a potential # title found in markdown
|
title: docsBaseById[navDocId].title,
|
||||||
// See https://github.com/facebook/docusaurus/issues/4665#issuecomment-825831367
|
|
||||||
title:
|
|
||||||
docsBaseById[navDocId].frontMatter.title ||
|
|
||||||
docsBaseById[navDocId].title,
|
|
||||||
permalink: docsBaseById[navDocId].permalink,
|
permalink: docsBaseById[navDocId].permalink,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -82,6 +82,7 @@ declare module '@theme/DocItem' {
|
||||||
readonly frontMatter: FrontMatter;
|
readonly frontMatter: FrontMatter;
|
||||||
readonly metadata: Metadata;
|
readonly metadata: Metadata;
|
||||||
readonly toc: readonly TOCItem[];
|
readonly toc: readonly TOCItem[];
|
||||||
|
readonly contentTitle: string | undefined;
|
||||||
(): JSX.Element;
|
(): JSX.Element;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -222,7 +222,6 @@ export default function pluginContentPages(
|
||||||
rehypePlugins,
|
rehypePlugins,
|
||||||
beforeDefaultRehypePlugins,
|
beforeDefaultRehypePlugins,
|
||||||
beforeDefaultRemarkPlugins,
|
beforeDefaultRemarkPlugins,
|
||||||
keepContentTitle: true,
|
|
||||||
staticDir: path.join(siteDir, STATIC_DIR_NAME),
|
staticDir: path.join(siteDir, STATIC_DIR_NAME),
|
||||||
// Note that metadataPath must be the same/in-sync as
|
// Note that metadataPath must be the same/in-sync as
|
||||||
// the path from createData for each MDX.
|
// the path from createData for each MDX.
|
||||||
|
|
|
@ -47,8 +47,8 @@ function BlogPostItem(props: Props): JSX.Element {
|
||||||
truncated,
|
truncated,
|
||||||
isBlogPostPage = false,
|
isBlogPostPage = false,
|
||||||
} = props;
|
} = props;
|
||||||
const {date, formattedDate, permalink, tags, readingTime} = metadata;
|
const {date, formattedDate, permalink, tags, readingTime, title} = metadata;
|
||||||
const {author, title, image, keywords} = frontMatter;
|
const {author, image, keywords} = frontMatter;
|
||||||
|
|
||||||
const authorURL = frontMatter.author_url || frontMatter.authorURL;
|
const authorURL = frontMatter.author_url || frontMatter.authorURL;
|
||||||
const authorTitle = frontMatter.author_title || frontMatter.authorTitle;
|
const authorTitle = frontMatter.author_title || frontMatter.authorTitle;
|
||||||
|
|
|
@ -13,6 +13,7 @@ import LastUpdated from '@theme/LastUpdated';
|
||||||
import type {Props} from '@theme/DocItem';
|
import type {Props} from '@theme/DocItem';
|
||||||
import TOC from '@theme/TOC';
|
import TOC from '@theme/TOC';
|
||||||
import EditThisPage from '@theme/EditThisPage';
|
import EditThisPage from '@theme/EditThisPage';
|
||||||
|
import {MainHeading} from '@theme/Heading';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
@ -49,13 +50,15 @@ function DocItem(props: Props): JSX.Element {
|
||||||
// See https://github.com/facebook/docusaurus/issues/3362
|
// See https://github.com/facebook/docusaurus/issues/3362
|
||||||
const showVersionBadge = versions.length > 1;
|
const showVersionBadge = versions.length > 1;
|
||||||
|
|
||||||
// For meta title, using frontMatter.title in priority over a potential # title found in markdown
|
// We only add a title if:
|
||||||
// See https://github.com/facebook/docusaurus/issues/4665#issuecomment-825831367
|
// - user asks to hide it with frontmatter
|
||||||
const metaTitle = frontMatter.title || title;
|
// - the markdown content does not already contain a top-level h1 heading
|
||||||
|
const shouldAddTitle =
|
||||||
|
!hideTitle && typeof DocContent.contentTitle === 'undefined';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Seo {...{title: metaTitle, description, keywords, image}} />
|
<Seo {...{title, description, keywords, image}} />
|
||||||
|
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div
|
<div
|
||||||
|
@ -72,12 +75,13 @@ function DocItem(props: Props): JSX.Element {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!hideTitle && (
|
|
||||||
<header>
|
|
||||||
<h1 className={styles.docTitle}>{title}</h1>
|
|
||||||
</header>
|
|
||||||
)}
|
|
||||||
<div className="markdown">
|
<div className="markdown">
|
||||||
|
{/*
|
||||||
|
Title can be declared inside md content or declared through frontmatter and added manually
|
||||||
|
To make both cases consistent, the added title is added under the same div.markdown block
|
||||||
|
See https://github.com/facebook/docusaurus/pull/4882#issuecomment-853021120
|
||||||
|
*/}
|
||||||
|
{shouldAddTitle && <MainHeading>{title}</MainHeading>}
|
||||||
<DocContent />
|
<DocContent />
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
|
@ -5,11 +5,6 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.docTitle {
|
|
||||||
font-size: 3rem;
|
|
||||||
margin-bottom: calc(var(--ifm-leading-desktop) * var(--ifm-leading));
|
|
||||||
}
|
|
||||||
|
|
||||||
.docItemContainer {
|
.docItemContainer {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0 0.5rem;
|
padding: 0 0.5rem;
|
||||||
|
|
|
@ -16,7 +16,24 @@ import {useThemeConfig} from '@docusaurus/theme-common';
|
||||||
import './styles.css';
|
import './styles.css';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
const Heading = (Tag: HeadingType): ((props: Props) => JSX.Element) =>
|
type HeadingComponent = (props: Props) => JSX.Element;
|
||||||
|
|
||||||
|
export const MainHeading: HeadingComponent = function MainHeading({...props}) {
|
||||||
|
return (
|
||||||
|
<header>
|
||||||
|
<h1
|
||||||
|
{...props}
|
||||||
|
id={undefined} // h1 headings do not need an id because they don't appear in the TOC
|
||||||
|
className={styles.h1Heading}>
|
||||||
|
{props.children}
|
||||||
|
</h1>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createAnchorHeading = (
|
||||||
|
Tag: HeadingType,
|
||||||
|
): ((props: Props) => JSX.Element) =>
|
||||||
function TargetComponent({id, ...props}) {
|
function TargetComponent({id, ...props}) {
|
||||||
const {
|
const {
|
||||||
navbar: {hideOnScroll},
|
navbar: {hideOnScroll},
|
||||||
|
@ -51,4 +68,8 @@ const Heading = (Tag: HeadingType): ((props: Props) => JSX.Element) =>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Heading = (headingType: HeadingType): ((props: Props) => JSX.Element) => {
|
||||||
|
return headingType === 'h1' ? MainHeading : createAnchorHeading(headingType);
|
||||||
|
};
|
||||||
|
|
||||||
export default Heading;
|
export default Heading;
|
||||||
|
|
|
@ -8,3 +8,8 @@
|
||||||
.enhancedAnchor {
|
.enhancedAnchor {
|
||||||
top: calc(var(--ifm-navbar-height) * -1 - 0.5rem);
|
top: calc(var(--ifm-navbar-height) * -1 - 0.5rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.h1Heading {
|
||||||
|
font-size: 3rem;
|
||||||
|
margin-bottom: calc(var(--ifm-leading-desktop) * var(--ifm-leading));
|
||||||
|
}
|
||||||
|
|
|
@ -114,6 +114,7 @@ declare module '@theme/Heading' {
|
||||||
|
|
||||||
const Heading: (Tag: HeadingType) => (props: Props) => JSX.Element;
|
const Heading: (Tag: HeadingType) => (props: Props) => JSX.Element;
|
||||||
export default Heading;
|
export default Heading;
|
||||||
|
export const MainHeading: (props: Props) => JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@theme/hooks/useAnnouncementBar' {
|
declare module '@theme/hooks/useAnnouncementBar' {
|
||||||
|
|
|
@ -141,11 +141,71 @@ describe('parseMarkdownContentTitle', () => {
|
||||||
|
|
||||||
`;
|
`;
|
||||||
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
|
content: markdown,
|
||||||
|
contentTitle: 'Markdown Title',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should parse markdown h1 title at the top and remove it', () => {
|
||||||
|
const markdown = dedent`
|
||||||
|
|
||||||
|
# Markdown Title
|
||||||
|
|
||||||
|
Lorem Ipsum
|
||||||
|
|
||||||
|
`;
|
||||||
|
expect(
|
||||||
|
parseMarkdownContentTitle(markdown, {removeContentTitle: true}),
|
||||||
|
).toEqual({
|
||||||
content: 'Lorem Ipsum',
|
content: 'Lorem Ipsum',
|
||||||
contentTitle: 'Markdown Title',
|
contentTitle: 'Markdown Title',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Should parse markdown h1 title at the top and unwrap inline code block', () => {
|
||||||
|
const markdown = dedent`
|
||||||
|
|
||||||
|
# \`Markdown Title\`
|
||||||
|
|
||||||
|
Lorem Ipsum
|
||||||
|
|
||||||
|
`;
|
||||||
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
|
content: markdown,
|
||||||
|
contentTitle: 'Markdown Title',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should parse markdown h1 title and trim content', () => {
|
||||||
|
const markdown = `
|
||||||
|
|
||||||
|
# Markdown Title
|
||||||
|
|
||||||
|
Lorem Ipsum
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
|
content: markdown.trim(),
|
||||||
|
contentTitle: 'Markdown Title',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should parse not parse markdown h1 title and trim content', () => {
|
||||||
|
const markdown = `
|
||||||
|
|
||||||
|
Lorem Ipsum
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
|
content: markdown.trim(),
|
||||||
|
contentTitle: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('Should parse markdown h1 title with fixed anchor-id syntax', () => {
|
test('Should parse markdown h1 title with fixed anchor-id syntax', () => {
|
||||||
const markdown = dedent`
|
const markdown = dedent`
|
||||||
|
|
||||||
|
@ -155,7 +215,7 @@ describe('parseMarkdownContentTitle', () => {
|
||||||
|
|
||||||
`;
|
`;
|
||||||
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
content: 'Lorem Ipsum',
|
content: markdown,
|
||||||
contentTitle: 'Markdown Title',
|
contentTitle: 'Markdown Title',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -169,7 +229,7 @@ describe('parseMarkdownContentTitle', () => {
|
||||||
|
|
||||||
`;
|
`;
|
||||||
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
content: 'Lorem Ipsum',
|
content: markdown,
|
||||||
contentTitle: 'Markdown Title',
|
contentTitle: 'Markdown Title',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -185,12 +245,7 @@ describe('parseMarkdownContentTitle', () => {
|
||||||
|
|
||||||
`;
|
`;
|
||||||
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
content: dedent`
|
content: markdown,
|
||||||
## Heading 2
|
|
||||||
|
|
||||||
Lorem Ipsum
|
|
||||||
|
|
||||||
`,
|
|
||||||
contentTitle: 'Markdown Title',
|
contentTitle: 'Markdown Title',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -206,12 +261,7 @@ describe('parseMarkdownContentTitle', () => {
|
||||||
|
|
||||||
`;
|
`;
|
||||||
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
content: dedent`
|
content: markdown,
|
||||||
# Markdown Title 2
|
|
||||||
|
|
||||||
Lorem Ipsum
|
|
||||||
|
|
||||||
`,
|
|
||||||
contentTitle: 'Markdown Title',
|
contentTitle: 'Markdown Title',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -227,15 +277,7 @@ describe('parseMarkdownContentTitle', () => {
|
||||||
|
|
||||||
`;
|
`;
|
||||||
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
content: dedent`
|
content: markdown,
|
||||||
|
|
||||||
Lorem Ipsum
|
|
||||||
|
|
||||||
# Markdown Title 2
|
|
||||||
|
|
||||||
Lorem Ipsum
|
|
||||||
|
|
||||||
`,
|
|
||||||
contentTitle: undefined,
|
contentTitle: undefined,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -250,6 +292,23 @@ describe('parseMarkdownContentTitle', () => {
|
||||||
|
|
||||||
`;
|
`;
|
||||||
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
|
content: markdown,
|
||||||
|
contentTitle: 'Markdown Title',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should parse markdown h1 alternate title and remove it', () => {
|
||||||
|
const markdown = dedent`
|
||||||
|
|
||||||
|
Markdown Title
|
||||||
|
================
|
||||||
|
|
||||||
|
Lorem Ipsum
|
||||||
|
|
||||||
|
`;
|
||||||
|
expect(
|
||||||
|
parseMarkdownContentTitle(markdown, {removeContentTitle: true}),
|
||||||
|
).toEqual({
|
||||||
content: 'Lorem Ipsum',
|
content: 'Lorem Ipsum',
|
||||||
contentTitle: 'Markdown Title',
|
contentTitle: 'Markdown Title',
|
||||||
});
|
});
|
||||||
|
@ -271,17 +330,7 @@ describe('parseMarkdownContentTitle', () => {
|
||||||
|
|
||||||
// remove the useless line breaks? Does not matter too much
|
// remove the useless line breaks? Does not matter too much
|
||||||
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
content: dedent`
|
content: markdown,
|
||||||
import Component1 from '@site/src/components/Component1';
|
|
||||||
|
|
||||||
import Component2 from '@site/src/components/Component2'
|
|
||||||
import Component3 from '@site/src/components/Component3'
|
|
||||||
import './styles.css';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lorem Ipsum
|
|
||||||
`,
|
|
||||||
contentTitle: 'Markdown Title',
|
contentTitle: 'Markdown Title',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -306,10 +355,16 @@ import "module-name"
|
||||||
# Markdown Title
|
# Markdown Title
|
||||||
|
|
||||||
Lorem Ipsum
|
Lorem Ipsum
|
||||||
`;
|
`;
|
||||||
|
|
||||||
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
content: `
|
content: markdown.trim(),
|
||||||
|
contentTitle: 'Markdown Title',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should parse markdown h1 title placed after various import declarations and remove it', () => {
|
||||||
|
const markdown = `
|
||||||
import DefaultComponent from '@site/src/components/Component1';
|
import DefaultComponent from '@site/src/components/Component1';
|
||||||
import DefaultComponent2 from '../relative/path/Component2';
|
import DefaultComponent2 from '../relative/path/Component2';
|
||||||
import * as EntireComponent from './relative/path/Component3';
|
import * as EntireComponent from './relative/path/Component3';
|
||||||
|
@ -325,16 +380,22 @@ import './styles.css';
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
import "module-name"
|
import "module-name"
|
||||||
|
|
||||||
|
# Markdown Title
|
||||||
|
|
||||||
Lorem Ipsum
|
Lorem Ipsum
|
||||||
`.trim(),
|
`;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
parseMarkdownContentTitle(markdown, {removeContentTitle: true}),
|
||||||
|
).toEqual({
|
||||||
|
content: markdown.trim().replace('# Markdown Title', ''),
|
||||||
contentTitle: 'Markdown Title',
|
contentTitle: 'Markdown Title',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Should parse markdown h1 alternate title placed after import declarations', () => {
|
test('Should parse markdown h1 alternate title placed after import declarations', () => {
|
||||||
const markdown = dedent`
|
const markdown = dedent`
|
||||||
|
|
||||||
import Component from '@site/src/components/Component';
|
import Component from '@site/src/components/Component';
|
||||||
import Component from '@site/src/components/Component'
|
import Component from '@site/src/components/Component'
|
||||||
import './styles.css';
|
import './styles.css';
|
||||||
|
@ -346,41 +407,40 @@ Lorem Ipsum
|
||||||
|
|
||||||
`;
|
`;
|
||||||
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
content: dedent`
|
content: markdown,
|
||||||
import Component from '@site/src/components/Component';
|
|
||||||
import Component from '@site/src/components/Component'
|
|
||||||
import './styles.css';
|
|
||||||
|
|
||||||
Lorem Ipsum
|
|
||||||
`,
|
|
||||||
contentTitle: 'Markdown Title',
|
contentTitle: 'Markdown Title',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Should parse title-only', () => {
|
test('Should parse markdown h1 alternate title placed after import declarations and remove it', () => {
|
||||||
const markdown = '# Document With Only A Title ';
|
|
||||||
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
|
||||||
content: '',
|
|
||||||
contentTitle: 'Document With Only A Title',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should parse markdown h1 title at the top but keep it in content', () => {
|
|
||||||
const markdown = dedent`
|
const markdown = dedent`
|
||||||
|
|
||||||
# Markdown Title
|
import Component from '@site/src/components/Component';
|
||||||
|
import Component from '@site/src/components/Component'
|
||||||
|
import './styles.css';
|
||||||
|
|
||||||
|
Markdown Title
|
||||||
|
==============
|
||||||
|
|
||||||
Lorem Ipsum
|
Lorem Ipsum
|
||||||
|
|
||||||
`;
|
`;
|
||||||
expect(
|
expect(
|
||||||
parseMarkdownContentTitle(markdown, {keepContentTitle: true}),
|
parseMarkdownContentTitle(markdown, {removeContentTitle: true}),
|
||||||
).toEqual({
|
).toEqual({
|
||||||
content: markdown.trim(),
|
content: markdown.replace('Markdown Title\n==============\n\n', ''),
|
||||||
contentTitle: 'Markdown Title',
|
contentTitle: 'Markdown Title',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Should parse title-only', () => {
|
||||||
|
const markdown = '# Document With Only A Title';
|
||||||
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
|
content: markdown,
|
||||||
|
contentTitle: 'Document With Only A Title',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('Should not parse markdown h1 title in the middle of a doc', () => {
|
test('Should not parse markdown h1 title in the middle of a doc', () => {
|
||||||
const markdown = dedent`
|
const markdown = dedent`
|
||||||
|
|
||||||
|
@ -439,7 +499,13 @@ Lorem Ipsum
|
||||||
`;
|
`;
|
||||||
|
|
||||||
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
expect(parseMarkdownContentTitle(markdown)).toEqual({
|
||||||
content: dedent`
|
content: markdown,
|
||||||
|
contentTitle: 'Markdown Title',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should parse markdown h1 title placed after multiple import declarations and remove it', () => {
|
||||||
|
const markdown = dedent`
|
||||||
import Component1 from '@site/src/components/Component1';
|
import Component1 from '@site/src/components/Component1';
|
||||||
import Component2 from '@site/src/components/Component2';
|
import Component2 from '@site/src/components/Component2';
|
||||||
import Component3 from '@site/src/components/Component3';
|
import Component3 from '@site/src/components/Component3';
|
||||||
|
@ -456,11 +522,16 @@ Lorem Ipsum
|
||||||
import Component14 from '@site/src/components/Component14';
|
import Component14 from '@site/src/components/Component14';
|
||||||
import Component15 from '@site/src/components/Component15';
|
import Component15 from '@site/src/components/Component15';
|
||||||
|
|
||||||
|
# Markdown Title
|
||||||
|
|
||||||
Lorem Ipsum
|
Lorem Ipsum
|
||||||
|
|
||||||
`,
|
`;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
parseMarkdownContentTitle(markdown, {removeContentTitle: true}),
|
||||||
|
).toEqual({
|
||||||
|
content: markdown.replace('# Markdown Title', ''),
|
||||||
contentTitle: 'Markdown Title',
|
contentTitle: 'Markdown Title',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -497,7 +568,9 @@ describe('parseMarkdownString', () => {
|
||||||
`),
|
`),
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"content": "Some text",
|
"content": "# Markdown Title
|
||||||
|
|
||||||
|
Some text",
|
||||||
"contentTitle": "Markdown Title",
|
"contentTitle": "Markdown Title",
|
||||||
"excerpt": "Some text",
|
"excerpt": "Some text",
|
||||||
"frontMatter": Object {},
|
"frontMatter": Object {},
|
||||||
|
@ -518,7 +591,9 @@ describe('parseMarkdownString', () => {
|
||||||
`),
|
`),
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"content": "Some text",
|
"content": "# Markdown Title
|
||||||
|
|
||||||
|
Some text",
|
||||||
"contentTitle": "Markdown Title",
|
"contentTitle": "Markdown Title",
|
||||||
"excerpt": "Some text",
|
"excerpt": "Some text",
|
||||||
"frontMatter": Object {
|
"frontMatter": Object {
|
||||||
|
@ -542,36 +617,11 @@ describe('parseMarkdownString', () => {
|
||||||
`),
|
`),
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"content": "Some text",
|
"content": "Markdown Title alternate
|
||||||
"contentTitle": "Markdown Title alternate",
|
================
|
||||||
"excerpt": "Some text",
|
|
||||||
"frontMatter": Object {
|
|
||||||
"title": "Frontmatter title",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should not warn for duplicate title if keepContentTitle=true', () => {
|
|
||||||
expect(
|
|
||||||
parseMarkdownString(
|
|
||||||
dedent`
|
|
||||||
---
|
|
||||||
title: Frontmatter title
|
|
||||||
---
|
|
||||||
|
|
||||||
# Markdown Title
|
|
||||||
|
|
||||||
Some text
|
|
||||||
`,
|
|
||||||
{keepContentTitle: true},
|
|
||||||
),
|
|
||||||
).toMatchInlineSnapshot(`
|
|
||||||
Object {
|
|
||||||
"content": "# Markdown Title
|
|
||||||
|
|
||||||
Some text",
|
Some text",
|
||||||
"contentTitle": "Markdown Title",
|
"contentTitle": "Markdown Title alternate",
|
||||||
"excerpt": "Some text",
|
"excerpt": "Some text",
|
||||||
"frontMatter": Object {
|
"frontMatter": Object {
|
||||||
"title": "Frontmatter title",
|
"title": "Frontmatter title",
|
||||||
|
@ -605,24 +655,6 @@ describe('parseMarkdownString', () => {
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should parse markdown title and keep it in content', () => {
|
|
||||||
expect(
|
|
||||||
parseMarkdownString(
|
|
||||||
dedent`
|
|
||||||
# Markdown Title
|
|
||||||
`,
|
|
||||||
{keepContentTitle: true},
|
|
||||||
),
|
|
||||||
).toMatchInlineSnapshot(`
|
|
||||||
Object {
|
|
||||||
"content": "# Markdown Title",
|
|
||||||
"contentTitle": "Markdown Title",
|
|
||||||
"excerpt": undefined,
|
|
||||||
"frontMatter": Object {},
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should delete only first heading', () => {
|
test('should delete only first heading', () => {
|
||||||
expect(
|
expect(
|
||||||
parseMarkdownString(dedent`
|
parseMarkdownString(dedent`
|
||||||
|
@ -636,7 +668,9 @@ describe('parseMarkdownString', () => {
|
||||||
`),
|
`),
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"content": "test test test # test bar
|
"content": "# Markdown Title
|
||||||
|
|
||||||
|
test test test # test bar
|
||||||
|
|
||||||
# Markdown Title 2
|
# Markdown Title 2
|
||||||
|
|
||||||
|
@ -692,7 +726,7 @@ describe('parseMarkdownString', () => {
|
||||||
test('should parse title only', () => {
|
test('should parse title only', () => {
|
||||||
expect(parseMarkdownString('# test')).toMatchInlineSnapshot(`
|
expect(parseMarkdownString('# test')).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"content": "",
|
"content": "# test",
|
||||||
"contentTitle": "test",
|
"contentTitle": "test",
|
||||||
"excerpt": undefined,
|
"excerpt": undefined,
|
||||||
"frontMatter": Object {},
|
"frontMatter": Object {},
|
||||||
|
@ -708,7 +742,8 @@ describe('parseMarkdownString', () => {
|
||||||
`),
|
`),
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"content": "",
|
"content": "test
|
||||||
|
===",
|
||||||
"contentTitle": "test",
|
"contentTitle": "test",
|
||||||
"excerpt": undefined,
|
"excerpt": undefined,
|
||||||
"frontMatter": Object {},
|
"frontMatter": Object {},
|
||||||
|
@ -726,7 +761,7 @@ describe('parseMarkdownString', () => {
|
||||||
`),
|
`),
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"content": "",
|
"content": "# test",
|
||||||
"contentTitle": "test",
|
"contentTitle": "test",
|
||||||
"excerpt": undefined,
|
"excerpt": undefined,
|
||||||
"frontMatter": Object {
|
"frontMatter": Object {
|
||||||
|
@ -766,7 +801,9 @@ describe('parseMarkdownString', () => {
|
||||||
`),
|
`),
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"content": "test test test test test test
|
"content": "# test
|
||||||
|
|
||||||
|
test test test test test test
|
||||||
test test test # test bar
|
test test test # test bar
|
||||||
# test2
|
# test2
|
||||||
### test
|
### test
|
||||||
|
|
|
@ -80,11 +80,21 @@ export function parseFrontMatter(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to convert markdown heading as text
|
||||||
|
// Does not need to be perfect, it is only used as a fallback when frontMatter.title is not provided
|
||||||
|
// For now, we just unwrap possible inline code blocks (# `config.js`)
|
||||||
|
function toTextContentTitle(contentTitle: string): string {
|
||||||
|
if (contentTitle.startsWith('`') && contentTitle.endsWith('`')) {
|
||||||
|
return contentTitle.substring(1, contentTitle.length - 1);
|
||||||
|
}
|
||||||
|
return contentTitle;
|
||||||
|
}
|
||||||
|
|
||||||
export function parseMarkdownContentTitle(
|
export function parseMarkdownContentTitle(
|
||||||
contentUntrimmed: string,
|
contentUntrimmed: string,
|
||||||
options?: {keepContentTitle?: boolean},
|
options?: {removeContentTitle?: boolean},
|
||||||
): {content: string; contentTitle: string | undefined} {
|
): {content: string; contentTitle: string | undefined} {
|
||||||
const keepContentTitleOption = options?.keepContentTitle ?? false;
|
const removeContentTitleOption = options?.removeContentTitle ?? false;
|
||||||
|
|
||||||
const content = contentUntrimmed.trim();
|
const content = contentUntrimmed.trim();
|
||||||
|
|
||||||
|
@ -108,16 +118,15 @@ export function parseMarkdownContentTitle(
|
||||||
|
|
||||||
if (!pattern || !title) {
|
if (!pattern || !title) {
|
||||||
return {content, contentTitle: undefined};
|
return {content, contentTitle: undefined};
|
||||||
|
} else {
|
||||||
|
const newContent = removeContentTitleOption
|
||||||
|
? content.replace(pattern, '')
|
||||||
|
: content;
|
||||||
|
return {
|
||||||
|
content: newContent.trim(),
|
||||||
|
contentTitle: toTextContentTitle(title.trim()).trim(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const newContent = keepContentTitleOption
|
|
||||||
? content
|
|
||||||
: content.replace(pattern, '');
|
|
||||||
|
|
||||||
return {
|
|
||||||
content: newContent.trim(),
|
|
||||||
contentTitle: title.trim(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ParsedMarkdown = {
|
type ParsedMarkdown = {
|
||||||
|
@ -129,22 +138,16 @@ type ParsedMarkdown = {
|
||||||
|
|
||||||
export function parseMarkdownString(
|
export function parseMarkdownString(
|
||||||
markdownFileContent: string,
|
markdownFileContent: string,
|
||||||
options?: {
|
options?: {removeContentTitle?: boolean},
|
||||||
keepContentTitle?: boolean;
|
|
||||||
},
|
|
||||||
): ParsedMarkdown {
|
): ParsedMarkdown {
|
||||||
try {
|
try {
|
||||||
const keepContentTitle = options?.keepContentTitle ?? false;
|
|
||||||
|
|
||||||
const {frontMatter, content: contentWithoutFrontMatter} = parseFrontMatter(
|
const {frontMatter, content: contentWithoutFrontMatter} = parseFrontMatter(
|
||||||
markdownFileContent,
|
markdownFileContent,
|
||||||
);
|
);
|
||||||
|
|
||||||
const {content, contentTitle} = parseMarkdownContentTitle(
|
const {content, contentTitle} = parseMarkdownContentTitle(
|
||||||
contentWithoutFrontMatter,
|
contentWithoutFrontMatter,
|
||||||
{
|
options,
|
||||||
keepContentTitle,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const excerpt = createExcerpt(content);
|
const excerpt = createExcerpt(content);
|
||||||
|
@ -166,10 +169,11 @@ This can happen if you use special characters like : in frontmatter values (try
|
||||||
|
|
||||||
export async function parseMarkdownFile(
|
export async function parseMarkdownFile(
|
||||||
source: string,
|
source: string,
|
||||||
|
options?: {removeContentTitle?: boolean},
|
||||||
): Promise<ParsedMarkdown> {
|
): Promise<ParsedMarkdown> {
|
||||||
const markdownString = await fs.readFile(source, 'utf-8');
|
const markdownString = await fs.readFile(source, 'utf-8');
|
||||||
try {
|
try {
|
||||||
return parseMarkdownString(markdownString);
|
return parseMarkdownString(markdownString, options);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Error while parsing markdown file ${source}
|
`Error while parsing markdown file ${source}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
---
|
---
|
||||||
id: docusaurus.config.js
|
id: docusaurus.config.js
|
||||||
title: docusaurus.config.js
|
|
||||||
description: API reference for Docusaurus configuration file.
|
description: API reference for Docusaurus configuration file.
|
||||||
slug: /docusaurus.config.js
|
slug: /docusaurus.config.js
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# `docusaurus.config.js`
|
||||||
|
|
||||||
## Overview {#overview}
|
## Overview {#overview}
|
||||||
|
|
||||||
`docusaurus.config.js` contains configurations for your site and is placed in the root directory of your site.
|
`docusaurus.config.js` contains configurations for your site and is placed in the root directory of your site.
|
||||||
|
|
|
@ -197,7 +197,7 @@ Markdown documents can use the following markdown frontmatter metadata fields, e
|
||||||
|
|
||||||
- `id`: A unique document id. If this field is not present, the document's `id` will default to its file name (without the extension)
|
- `id`: A unique document id. If this field is not present, the document's `id` will default to its file name (without the extension)
|
||||||
- `title`: The title of your document. If this field is not present, the document's `title` will default to its `id`
|
- `title`: The title of your document. If this field is not present, the document's `title` will default to its `id`
|
||||||
- `hide_title`: Whether to hide the title at the top of the doc. By default, it is `false`
|
- `hide_title`: Whether to hide the title at the top of the doc. It only hides a title declared through the frontmatter, and have no effect on a title at the top of your Markdown document. By default, it is `false`
|
||||||
- `hide_table_of_contents`: Whether to hide the table of contents to the right. By default it is `false`
|
- `hide_table_of_contents`: Whether to hide the table of contents to the right. By default it is `false`
|
||||||
- `sidebar_label`: The text shown in the document sidebar and in the next/previous button for this document. If this field is not present, the document's `sidebar_label` will default to its `title`
|
- `sidebar_label`: The text shown in the document sidebar and in the next/previous button for this document. If this field is not present, the document's `sidebar_label` will default to its `title`
|
||||||
- `sidebar_position`: Permits to control the position of a doc inside the generated sidebar slice, when using `autogenerated` sidebar items. Can be Int or Float.
|
- `sidebar_position`: Permits to control the position of a doc inside the generated sidebar slice, when using `autogenerated` sidebar items. Can be Int or Float.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue