Skip to content

Commit

Permalink
fix: Fixed an issue where inline footnotes were copied to title tag a…
Browse files Browse the repository at this point in the history
…nd section id when heading was defined
  • Loading branch information
akabekobeko committed May 15, 2021
1 parent 146abea commit 59f8db0
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 156 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
},
"dependencies": {
"debug": "^4.3.1",
"github-slugger": "^1.3.0",
"hast-util-find-and-replace": "^3.2.0",
"hast-util-is-element": "^1.1.0",
"hast-util-select": "^4.0.2",
Expand All @@ -78,7 +79,6 @@
"remark-parse": "^8.0.2",
"remark-rehype": "^8.0.0",
"remark-shortcodes": "^0.3.1",
"remark-slug": "^6.0.0",
"to-vfile": "^6.1.0",
"unified": "^9.2.0",
"unist-builder": "^2.0.3",
Expand All @@ -94,6 +94,7 @@
"@release-it/conventional-changelog": "^2.0.0",
"@types/common-tags": "^1.8.0",
"@types/debug": "^4.1.5",
"@types/github-slugger": "^1.3.0",
"@types/hast": "^2.3.1",
"@types/jest": "^26.0.20",
"@types/js-yaml": "^4.0.0",
Expand Down
28 changes: 25 additions & 3 deletions src/plugins/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,37 @@ const setBodyClass = (node: Element, value: string) => {
}
};

/**
* Create the title text without footnotes.
* @param tree Tree of Markdown AST.
* @returns Title text or `undefined`.
*/
const createTitle = (tree: Node) => {
const heading = select('heading', tree) as Element | undefined;
if (!heading) {
return;
}

// Create title string with footnotes removed
const children = [...heading.children];
heading.children = heading.children.filter(
(child: Node) => child.type !== 'footnote',
);
const text = toString(heading);
heading.children = children;

return text;
};

/**
* Parse Markdown's Frontmatter to metadate (`VFile.data`).
* @returns Handler.
* @see https://github.com/Symbitic/remark-plugins/blob/master/packages/remark-meta/src/index.js
*/
export const mdast = () => (tree: Node, file: MetadataVFile) => {
const heading = select('heading', tree);
if (heading) {
file.data.title = toString(heading);
const title = createTitle(tree);
if (title) {
file.data.title = title;
}

visit<FrontmatterContent>(tree, ['yaml'], (node) => {
Expand Down
71 changes: 71 additions & 0 deletions src/plugins/slug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* derived from `remark-slug`.
* original: Copyright (c) 2015 Titus Wormer <tituswormer@gmail.com>
* modified: 2021 and later is Akabeko
* @license MIT
* @see https://github.com/remarkjs/remark-slug
*/

import GithubSlugger from 'github-slugger';
import toString from 'mdast-util-to-string';
import { Node } from 'unist';
import { selectAll } from 'unist-util-select';

/**
* Heading in Markdown AST.
*/
interface Heading extends Node {
children: Node[];
data?: {
id?: string;
hProperties?: {
id?: string;
};
};
}

/**
* Create slug from `id` or heading children.
* @param heading Heading.
* @param slugs Slugger.
* @returns
*/
const createSlug = (heading: Heading, slugger: GithubSlugger) => {
if (heading.data && heading.data.hProperties && heading.data.hProperties.id) {
return slugger.slug(heading.data.hProperties.id, true);
}

// Create slug string with footnotes removed
const children = [...heading.children];
heading.children = heading.children.filter(
(child: Node) => child.type !== 'footnote',
);
const text = slugger.slug(toString(heading));
heading.children = children;

return text;
};

/**
* Process Markdown AST.
* @returns Transformer.
*/
export const mdast = () => (tree: any) => {
const slugger = new GithubSlugger();
slugger.reset();

const headings = selectAll('heading', tree) as Heading[];
for (const heading of headings) {
const id = createSlug(heading, slugger);
if (!heading.data) {
heading.data = {};
}

if (!heading.data.hProperties) {
heading.data.hProperties = {};
}

heading.data.id = id;
heading.data.hProperties.id = id;
}
};
2 changes: 1 addition & 1 deletion src/revive-parse.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import breaks from 'remark-breaks';
import frontmatter from 'remark-frontmatter';
import markdown from 'remark-parse';
import slug from 'remark-slug';
import unified from 'unified';
import { mdast as attr } from './plugins/attr';
import { mdast as code } from './plugins/code';
Expand All @@ -10,6 +9,7 @@ import { mdast as math } from './plugins/math';
import { mdast as metadata } from './plugins/metadata';
import { mdast as ruby } from './plugins/ruby';
import { mdast as section } from './plugins/section';
import { mdast as slug } from './plugins/slug';
import { mdast as toc } from './plugins/toc';
import { inspect } from './utils';

Expand Down
26 changes: 26 additions & 0 deletions tests/footnotes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,29 @@ Footnotes can also be written inline ^[This part is a footnote.].
`;
expect(received).toBe(expected);
});

it('Heading title and section id without inline footnotes text', () => {
const md = '# Test^[Test]';
const received = stringify(md);
const expected = `<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<section id="test" class="level1">
<h1>Test<a id="fnref1" href="#fn1" class="footnote-ref" role="doc-noteref"><sup>1</sup></a></h1>
</section>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn1" role="doc-endnote">Test<a href="#fnref1" class="footnote-back" role="doc-backlink">↩</a></li>
</ol>
</section>
</body>
</html>
`;
expect(received).toBe(expected);
});
Loading

0 comments on commit 59f8db0

Please sign in to comment.