From 131b77381c2b607a65d83c7d71fbf348f85cb2f4 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Wed, 7 Jun 2023 18:28:59 +0800 Subject: [PATCH] fix(utils): better handling of code blocks in link replacement --- .../__snapshots__/markdownLinks.test.ts.snap | 16 ++++++++ .../src/__tests__/markdownLinks.test.ts | 26 ++++++++++++ .../docusaurus-utils/src/markdownLinks.ts | 40 ++++++++++++++----- 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/packages/docusaurus-utils/src/__tests__/__snapshots__/markdownLinks.test.ts.snap b/packages/docusaurus-utils/src/__tests__/__snapshots__/markdownLinks.test.ts.snap index 17102ffd25bb..7c5fc874287c 100644 --- a/packages/docusaurus-utils/src/__tests__/__snapshots__/markdownLinks.test.ts.snap +++ b/packages/docusaurus-utils/src/__tests__/__snapshots__/markdownLinks.test.ts.snap @@ -47,6 +47,22 @@ exports[`replaceMarkdownLinks handles stray spaces 1`] = ` } `; +exports[`replaceMarkdownLinks handles unpaired fences 1`] = ` +{ + "brokenMarkdownLinks": [], + "newContent": " +\`\`\`foo +hello + +\`\`\`foo +hello +\`\`\` + +A [link](/docs/file) +", +} +`; + exports[`replaceMarkdownLinks ignores links in HTML comments 1`] = ` { "brokenMarkdownLinks": [ diff --git a/packages/docusaurus-utils/src/__tests__/markdownLinks.test.ts b/packages/docusaurus-utils/src/__tests__/markdownLinks.test.ts index 691b9599aa4f..b7d7abd556b5 100644 --- a/packages/docusaurus-utils/src/__tests__/markdownLinks.test.ts +++ b/packages/docusaurus-utils/src/__tests__/markdownLinks.test.ts @@ -371,6 +371,32 @@ The following operations are defined for [URI]s: [URL](./file.md?foo=bar#baz) [URL](./file.md#a) [URL](./file.md?c) +`, + }), + ).toMatchSnapshot(); + }); + + it('handles unpaired fences', () => { + expect( + replaceMarkdownLinks({ + siteDir: '.', + filePath: 'docs/file.md', + contentPaths: { + contentPath: 'docs', + contentPathLocalized: 'i18n/docs-localized', + }, + sourceToPermalink: { + '@site/docs/file.md': '/docs/file', + }, + fileString: ` +\`\`\`foo +hello + +\`\`\`foo +hello +\`\`\` + +A [link](./file.md) `, }), ).toMatchSnapshot(); diff --git a/packages/docusaurus-utils/src/markdownLinks.ts b/packages/docusaurus-utils/src/markdownLinks.ts index 24752519fd07..88825e502f14 100644 --- a/packages/docusaurus-utils/src/markdownLinks.ts +++ b/packages/docusaurus-utils/src/markdownLinks.ts @@ -40,6 +40,24 @@ export type BrokenMarkdownLink = { link: string; }; +type CodeFence = { + type: '`' | '~'; + definitelyOpen: boolean; + count: number; +}; + +function parseCodeFence(line: string): CodeFence | null { + const match = line.trim().match(/^(?`{3,}|~{3,})(?.*)/); + if (!match) { + return null; + } + return { + type: match.groups!.fence![0]! as '`' | '~', + definitelyOpen: !!match.groups!.rest!, + count: match.groups!.fence!.length, + }; +} + /** * Takes a Markdown file and replaces relative file references with their URL * counterparts, e.g. `[link](./intro.md)` => `[link](/docs/intro)`, preserving @@ -82,19 +100,23 @@ export function replaceMarkdownLinks({ const brokenMarkdownLinks: BrokenMarkdownLink[] = []; // Replace internal markdown linking (except in fenced blocks). - let lastCodeFence: string | null = null; + let lastOpenCodeFence: CodeFence | null = null; const lines = fileString.split('\n').map((line) => { - const codeFence = line.trimStart().match(/^`{3,}|^~{3,}/)?.[0]; + const codeFence = parseCodeFence(line); if (codeFence) { - if (!lastCodeFence) { - lastCodeFence = codeFence; - // If we are in a ````-fenced block, all ``` would be plain text instead - // of fences - } else if (codeFence.startsWith(lastCodeFence)) { - lastCodeFence = null; + if (!lastOpenCodeFence) { + lastOpenCodeFence = codeFence; + } else if ( + !codeFence.definitelyOpen && + lastOpenCodeFence.type === codeFence.type && + lastOpenCodeFence.count <= codeFence.count + ) { + // All three conditions must be met in order for this to be considered + // a closing fence. + lastOpenCodeFence = null; } } - if (lastCodeFence) { + if (lastOpenCodeFence) { return line; }