From 89e146f596468d3e533d755b3cf7e5e7116cf23c Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Thu, 16 Jun 2022 18:32:42 +0800 Subject: [PATCH] fix(utils): allow any non-boundary characters in Markdown heading ID (#7604) --- .../src/__tests__/markdownUtils.test.ts | 47 +++++++++++++++++++ .../docusaurus-utils/src/markdownUtils.ts | 8 ++-- .../_pages tests/markdownPageTests.md | 10 ++++ 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/packages/docusaurus-utils/src/__tests__/markdownUtils.test.ts b/packages/docusaurus-utils/src/__tests__/markdownUtils.test.ts index 2f4baa74cc..42df2a38d4 100644 --- a/packages/docusaurus-utils/src/__tests__/markdownUtils.test.ts +++ b/packages/docusaurus-utils/src/__tests__/markdownUtils.test.ts @@ -833,6 +833,53 @@ describe('parseMarkdownHeadingId', () => { id: 'id', }); }); + + it('does not parse empty id', () => { + expect(parseMarkdownHeadingId('## a {#}')).toEqual({ + text: '## a {#}', + id: undefined, + }); + }); + + it('can parse id with more characters', () => { + expect(parseMarkdownHeadingId('## a {#你好}')).toEqual({ + text: '## a', + id: '你好', + }); + + expect(parseMarkdownHeadingId('## a {#2022.1.1}')).toEqual({ + text: '## a', + id: '2022.1.1', + }); + + expect(parseMarkdownHeadingId('## a {#a#b}')).toEqual({ + text: '## a', + id: 'a#b', + }); + }); + + // The actual behavior is unspecified, just need to ensure it stays consistent + it('handles unmatched boundaries', () => { + expect(parseMarkdownHeadingId('## a {# a {#bcd}')).toEqual({ + text: '## a {# a', + id: 'bcd', + }); + + expect(parseMarkdownHeadingId('## a {#bcd}}')).toEqual({ + text: '## a {#bcd}}', + id: undefined, + }); + + expect(parseMarkdownHeadingId('## a {#b{cd}')).toEqual({ + text: '## a', + id: 'b{cd', + }); + + expect(parseMarkdownHeadingId('## a {#b{#b}')).toEqual({ + text: '## a {#b', + id: 'b', + }); + }); }); describe('writeMarkdownHeadingId', () => { diff --git a/packages/docusaurus-utils/src/markdownUtils.ts b/packages/docusaurus-utils/src/markdownUtils.ts index fd11f5e667..512a09bf91 100644 --- a/packages/docusaurus-utils/src/markdownUtils.ts +++ b/packages/docusaurus-utils/src/markdownUtils.ts @@ -14,8 +14,8 @@ import {createSlugger, type Slugger, type SluggerOptions} from './slugger'; // content. Most parsing is still done in MDX through the mdx-loader. /** - * Parses custom ID from a heading. The ID must be composed of letters, - * underscores, and dashes only. + * Parses custom ID from a heading. The ID can contain any characters except + * `{#` and `}`. * * @param heading e.g. `## Some heading {#some-heading}` where the last * character must be `}` for the ID to be recognized @@ -26,9 +26,9 @@ export function parseMarkdownHeadingId(heading: string): { */ text: string; /** The heading ID. e.g. `some-heading` */ - id?: string; + id: string | undefined; } { - const customHeadingIdRegex = /\s*\{#(?[\w-]+)\}$/; + const customHeadingIdRegex = /\s*\{#(?(?:.(?!\{#|\}))*.)\}$/; const matches = customHeadingIdRegex.exec(heading); if (matches) { return { diff --git a/website/_dogfooding/_pages tests/markdownPageTests.md b/website/_dogfooding/_pages tests/markdownPageTests.md index 8dcbf82a82..323c741ee3 100644 --- a/website/_dogfooding/_pages tests/markdownPageTests.md +++ b/website/_dogfooding/_pages tests/markdownPageTests.md @@ -162,6 +162,16 @@ function Clock(props) { ## Custom heading ID {#custom} +### Weird heading {#你好} + +### Weird heading {#2022.1.1} + +### Weird heading {#a#b} + +### Weird heading {#a b} + +### Weird heading {#a{b} + ## Pipe Code tag + double pipe: ||