mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-11 08:07:26 +02:00
fix(utils): better handling of code blocks in link replacement (#9046)
This commit is contained in:
parent
dcce8ff3cd
commit
76f920359b
3 changed files with 73 additions and 9 deletions
|
@ -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`] = `
|
exports[`replaceMarkdownLinks ignores links in HTML comments 1`] = `
|
||||||
{
|
{
|
||||||
"brokenMarkdownLinks": [
|
"brokenMarkdownLinks": [
|
||||||
|
|
|
@ -371,6 +371,32 @@ The following operations are defined for [URI]s:
|
||||||
[URL](./file.md?foo=bar#baz)
|
[URL](./file.md?foo=bar#baz)
|
||||||
[URL](./file.md#a)
|
[URL](./file.md#a)
|
||||||
[URL](./file.md?c)
|
[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();
|
).toMatchSnapshot();
|
||||||
|
|
|
@ -40,6 +40,24 @@ export type BrokenMarkdownLink<T extends ContentPaths> = {
|
||||||
link: string;
|
link: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type CodeFence = {
|
||||||
|
type: '`' | '~';
|
||||||
|
definitelyOpen: boolean;
|
||||||
|
count: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseCodeFence(line: string): CodeFence | null {
|
||||||
|
const match = line.trim().match(/^(?<fence>`{3,}|~{3,})(?<rest>.*)/);
|
||||||
|
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
|
* Takes a Markdown file and replaces relative file references with their URL
|
||||||
* counterparts, e.g. `[link](./intro.md)` => `[link](/docs/intro)`, preserving
|
* counterparts, e.g. `[link](./intro.md)` => `[link](/docs/intro)`, preserving
|
||||||
|
@ -82,19 +100,23 @@ export function replaceMarkdownLinks<T extends ContentPaths>({
|
||||||
const brokenMarkdownLinks: BrokenMarkdownLink<T>[] = [];
|
const brokenMarkdownLinks: BrokenMarkdownLink<T>[] = [];
|
||||||
|
|
||||||
// Replace internal markdown linking (except in fenced blocks).
|
// 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 lines = fileString.split('\n').map((line) => {
|
||||||
const codeFence = line.trimStart().match(/^`{3,}|^~{3,}/)?.[0];
|
const codeFence = parseCodeFence(line);
|
||||||
if (codeFence) {
|
if (codeFence) {
|
||||||
if (!lastCodeFence) {
|
if (!lastOpenCodeFence) {
|
||||||
lastCodeFence = codeFence;
|
lastOpenCodeFence = codeFence;
|
||||||
// If we are in a ````-fenced block, all ``` would be plain text instead
|
} else if (
|
||||||
// of fences
|
!codeFence.definitelyOpen &&
|
||||||
} else if (codeFence.startsWith(lastCodeFence)) {
|
lastOpenCodeFence.type === codeFence.type &&
|
||||||
lastCodeFence = null;
|
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;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue