fix(utils): avoid replacing Markdown links missing the directly next link (#7458)

This commit is contained in:
Joshua Chen 2022-05-21 13:07:31 +08:00 committed by GitHub
parent 0f8f918f2c
commit ad888b7fae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 2 deletions

View file

@ -135,6 +135,13 @@ The following operations are defined for [URI]s:
} }
`; `;
exports[`replaceMarkdownLinks replaces two links on the same line 1`] = `
{
"brokenMarkdownLinks": [],
"newContent": "[TypeScript](/programming-languages/typescript/) and [Go](/programming-languages/go/)",
}
`;
exports[`replaceMarkdownLinks resolves absolute and relative links differently 1`] = ` exports[`replaceMarkdownLinks resolves absolute and relative links differently 1`] = `
{ {
"brokenMarkdownLinks": [ "brokenMarkdownLinks": [

View file

@ -38,6 +38,35 @@ describe('replaceMarkdownLinks', () => {
).toMatchSnapshot(); ).toMatchSnapshot();
}); });
it('replaces two links on the same line', () => {
// cSpell:ignore Goooooooooo
// This is a very arcane bug: if we continue matching using the previous
// matching index (as is the behavior of RegExp#exec), it will go right over
// the next Markdown link and fail to match the "Go" link. This only happens
// when: (1) the replaced link is much shorter than the Markdown path, (2)
// the next link is very close to the current one (e.g. here if it's not
// "Go" but "Goooooooooo", or if every link has the /docs/ prefix, the bug
// will not trigger because it won't overshoot)
expect(
replaceMarkdownLinks({
siteDir: '.',
filePath: 'docs/intro.md',
contentPaths: {
contentPath: 'docs',
contentPathLocalized: 'i18n/docs-localized',
},
sourceToPermalink: {
'@site/docs/intro.md': '/',
'@site/docs/programming-languages/typescript/typescript.md':
'/programming-languages/typescript/',
'@site/docs/programming-languages/go/go.md':
'/programming-languages/go/',
},
fileString: `[TypeScript](programming-languages/typescript/typescript.md) and [Go](programming-languages/go/go.md)`,
}),
).toMatchSnapshot();
});
it('replaces reference style Markdown links', () => { it('replaces reference style Markdown links', () => {
expect( expect(
replaceMarkdownLinks({ replaceMarkdownLinks({
@ -155,7 +184,7 @@ The following operations are defined for [URI]s:
).toMatchSnapshot(); ).toMatchSnapshot();
}); });
// TODO bad // FIXME
it('ignores links in inline code', () => { it('ignores links in inline code', () => {
expect( expect(
replaceMarkdownLinks({ replaceMarkdownLinks({
@ -175,7 +204,7 @@ The following operations are defined for [URI]s:
).toMatchSnapshot(); ).toMatchSnapshot();
}); });
// TODO bad // FIXME
it('replaces links with same title as URL', () => { it('replaces links with same title as URL', () => {
expect( expect(
replaceMarkdownLinks({ replaceMarkdownLinks({

View file

@ -138,6 +138,9 @@ export function replaceMarkdownLinks<T extends ContentPaths>({
.map((part) => part.replace(/\s/g, '%20')) .map((part) => part.replace(/\s/g, '%20'))
.join('/'); .join('/');
modifiedLine = modifiedLine.replace(mdLink, encodedPermalink); modifiedLine = modifiedLine.replace(mdLink, encodedPermalink);
// Adjust the lastIndex to avoid passing over the next link if the
// newly replaced URL is shorter.
mdRegex.lastIndex += encodedPermalink.length - mdLink.length;
} else { } else {
const brokenMarkdownLink: BrokenMarkdownLink<T> = { const brokenMarkdownLink: BrokenMarkdownLink<T> = {
contentPaths, contentPaths,