fix(content-blog): make footnote reference DOM ID unique on post listing page (#7212)

Co-authored-by: Joshua Chen <sidachen2003@gmail.com>
This commit is contained in:
AkiraVoid 2022-04-21 22:51:24 +08:00 committed by GitHub
parent 3b32a41f22
commit 71ba449a28
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 197 additions and 1 deletions

View file

@ -31,6 +31,7 @@
"reading-time": "^1.5.0",
"remark-admonitions": "^1.2.1",
"tslib": "^2.3.1",
"unist-util-visit": "^2.0.3",
"utility-types": "^3.10.0",
"webpack": "^5.72.0"
},

View file

@ -7,6 +7,7 @@
import path from 'path';
import admonitions from 'remark-admonitions';
import footnoteIDFixer from './remark/footnoteIDFixer';
import {
normalizeUrl,
docuHash,
@ -435,7 +436,10 @@ export default async function pluginContentBlog(
options: {
remarkPlugins,
rehypePlugins,
beforeDefaultRemarkPlugins,
beforeDefaultRemarkPlugins: [
footnoteIDFixer,
...beforeDefaultRemarkPlugins,
],
beforeDefaultRehypePlugins,
staticDirs: siteConfig.staticDirectories.map((dir) =>
path.resolve(siteDir, dir),

View file

@ -0,0 +1,9 @@
foo[^1]
bar[^2]
baz[^3]
[^1]: foo
[^2]: foo
[^3]: foo

View file

@ -0,0 +1,9 @@
foo[^1]
bar[^2]
baz[^3]
[^1]: foo
[^2]: foo
[^3]: foo

View file

@ -0,0 +1,66 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`footnoteIDFixer remark plugin appends a hash to each footnote def/ref 1`] = `
"/* @jsxRuntime classic */
/* @jsx mdx */
const layoutProps = {
};
const MDXLayout = "wrapper"
export default function MDXContent({
components,
...props
}) {
return <MDXLayout {...layoutProps} {...props} components={components} mdxType="MDXLayout">
<p>{\`foo\`}<sup parentName="p" {...{
"id": "fnref-1-[HASH]"
}}><a parentName="sup" {...{
"href": "#fn-1-[HASH]",
"className": "footnote-ref"
}}>{\`1\`}</a></sup></p>
<p>{\`bar\`}<sup parentName="p" {...{
"id": "fnref-2-[HASH]"
}}><a parentName="sup" {...{
"href": "#fn-2-[HASH]",
"className": "footnote-ref"
}}>{\`2\`}</a></sup></p>
<p>{\`baz\`}<sup parentName="p" {...{
"id": "fnref-3-[HASH]"
}}><a parentName="sup" {...{
"href": "#fn-3-[HASH]",
"className": "footnote-ref"
}}>{\`3\`}</a></sup></p>
<div {...{
"className": "footnotes"
}}>
<hr parentName="div"></hr>
<ol parentName="div">
<li parentName="ol" {...{
"id": "fn-1-[HASH]"
}}>{\`foo\`}<a parentName="li" {...{
"href": "#fnref-1-[HASH]",
"className": "footnote-backref"
}}>{\`↩\`}</a></li>
<li parentName="ol" {...{
"id": "fn-2-[HASH]"
}}>{\`foo\`}<a parentName="li" {...{
"href": "#fnref-2-[HASH]",
"className": "footnote-backref"
}}>{\`↩\`}</a></li>
<li parentName="ol" {...{
"id": "fn-3-[HASH]"
}}>{\`foo\`}<a parentName="li" {...{
"href": "#fnref-3-[HASH]",
"className": "footnote-backref"
}}>{\`↩\`}</a></li>
</ol>
</div>
</MDXLayout>;
}
;
MDXContent.isMDXComponent = true;"
`;

View file

@ -0,0 +1,39 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import fs from 'fs-extra';
import path from 'path';
import {simpleHash} from '@docusaurus/utils';
import mdx from '@mdx-js/mdx';
import footnoteIDFixer from '../footnoteIDFixer';
const processFixture = async (name: string) => {
const filepath = path.join(__dirname, `__fixtures__/${name}.md`);
const result = await mdx(await fs.readFile(filepath), {
filepath,
remarkPlugins: [footnoteIDFixer],
});
return result.toString();
};
describe('footnoteIDFixer remark plugin', () => {
it('appends a hash to each footnote def/ref', async () => {
const hash = simpleHash(path.join(__dirname, `__fixtures__/post.md`), 6);
expect(
(await processFixture('post')).replace(new RegExp(hash, 'g'), '[HASH]'),
).toMatchSnapshot();
});
it('produces different hashes for different posts but same hash for the same path', async () => {
const file1 = await processFixture('post');
const file1again = await processFixture('post');
const file2 = await processFixture('post2');
expect(file1).toBe(file1again);
expect(file1).not.toBe(file2);
});
});

View file

@ -0,0 +1,29 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import visit from 'unist-util-visit';
import {simpleHash} from '@docusaurus/utils';
import type {Transformer} from 'unified';
import type {FootnoteReference, FootnoteDefinition} from 'mdast';
/**
* In the blog list view, each post will be compiled separately. However, they
* may use the same footnote IDs. This leads to duplicated DOM IDs and inability
* to navigate to footnote references. This plugin fixes it by appending a
* unique hash to each reference/definition.
*/
export default function plugin(): Transformer {
return (root, vfile) => {
const suffix = `-${simpleHash(vfile.path!, 6)}`;
visit(root, 'footnoteReference', (node: FootnoteReference) => {
node.identifier += suffix;
});
visit(root, 'footnoteDefinition', (node: FootnoteDefinition) => {
node.identifier += suffix;
});
};
}

View file

@ -0,0 +1,13 @@
---
title: Third post with footnote to test posts with same footnote reference.
---
foo[^1]
bar[^2]
baz[^3]
[^1]: foo
[^2]: bar
[^3]: baz

View file

@ -0,0 +1,13 @@
---
title: Second post with footnote to test posts with same footnote reference.
---
foo[^1]
bar[^2]
baz[^3]
[^1]: foo
[^2]: bar
[^3]: baz

View file

@ -0,0 +1,13 @@
---
title: First post with footnote to test posts with same footnote reference.
---
foo[^1]
bar[^2]
baz[^3]
[^1]: foo
[^2]: bar
[^3]: baz