Skip to content

Commit d286b3a

Browse files
committed
ensure transformed link tags don't remove chlidren
1 parent 42d39f2 commit d286b3a

File tree

2 files changed

+47
-28
lines changed

2 files changed

+47
-28
lines changed

src/format/links.test.ts

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,67 @@ import * as assert from "uvu/assert";
44
import { Root, Paragraph } from "mdast";
55
import unified from "unified";
66
import markdown from "remark-parse";
7+
import rehype from "remark-rehype";
8+
import stringify from "rehype-stringify";
79

810
import { set_link_attributes } from "./links";
911

10-
const processor = unified().use(markdown).use(set_link_attributes);
12+
const { process } = unified()
13+
.use(markdown)
14+
.use(rehype)
15+
.use(set_link_attributes)
16+
.use(stringify);
1117

1218
const _links = suite("links");
1319

1420
_links("renders link", async () => {
1521
const src = `[a link](/local/path/to/file)`;
16-
const output = (await processor.run(processor.parse(src))) as Root;
22+
const output = await process(src);
1723

18-
assert.is((output.children[0] as Paragraph).children[0].type, "html");
1924
assert.is(
20-
(output.children[0] as Paragraph).children[0].value,
21-
'<a href="http://github.com/local/path/to/file" rel="noopener noreferrer">a link</a>'
25+
output.contents,
26+
'<p><a href="http://github.com/local/path/to/file" rel="noopener noreferrer">a link</a></p>'
2227
);
2328
});
2429

2530
_links("renders link with title attribute", async () => {
2631
const src = `[a link](/local/path/to/file "about my link")`;
27-
const output = (await processor.run(processor.parse(src))) as Root;
32+
const output = await process(src);
2833

2934
assert.is(
30-
(output.children[0] as Paragraph).children[0].value,
31-
'<a href="http://github.com/local/path/to/file" title="about my link" rel="noopener noreferrer">a link</a>'
35+
output.contents,
36+
'<p><a href="http://github.com/local/path/to/file" title="about my link" rel="noopener noreferrer">a link</a></p>'
3237
);
3338
});
3439

3540
_links("renders external links with target _blank: no title", async () => {
3641
const src = `[a link](https://google.com)`;
37-
const output = (await processor.run(processor.parse(src))) as Root;
42+
const output = await process(src);
3843

3944
assert.is(
40-
(output.children[0] as Paragraph).children[0].value,
41-
'<a href="https://google.com/" target="_blank" rel="noopener noreferrer">a link</a>'
45+
output.contents,
46+
'<p><a href="https://google.com/" target="_blank" rel="noopener noreferrer">a link</a></p>'
4247
);
4348
});
4449

45-
_links("renders external links with target _blank: no title", async () => {
50+
_links("renders external links with target _blank: title", async () => {
4651
const src = `[a link](https://google.com "a search engine")`;
47-
const output = (await processor.run(processor.parse(src))) as Root;
52+
const output = await process(src);
53+
54+
assert.is(
55+
output.contents,
56+
'<p><a href="https://google.com" title="a search engine" target="_blank" rel="noopener noreferrer">a link</a></p>'
57+
);
58+
});
59+
60+
_links("renders links containing multiple child nodes", async () => {
61+
const src =
62+
"[CommonJS packages in `noExternal`](https://github.com/vitejs/vite/issues/2579)";
63+
const output = await process(src);
4864

4965
assert.is(
50-
(output.children[0] as Paragraph).children[0].value,
51-
'<a href="https://google.com" target="_blank" title="a search engine" rel="noopener noreferrer">a link</a>'
66+
output.contents,
67+
'<p><a href="https://github.com/vitejs/vite/issues/2579" target="_blank" rel="noopener noreferrer">CommonJS packages in <code>noExternal</code></a></p>'
5268
);
5369
});
5470

src/format/links.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
11
import type { Transformer } from "unified";
2-
import type { Link, HTML } from "mdast";
2+
import type { Node } from "unist";
3+
import type { Link, HTML, Parent } from "mdast";
34

45
import visit from "unist-util-visit";
56

7+
interface Element extends Node {
8+
properties: {
9+
href?: string;
10+
target?: string;
11+
rel?: string;
12+
};
13+
}
614
/**
715
* All links must have `rel="noopener noreferrer"` and external attributes
816
* must open in a new window.
917
*/
1018
export function set_link_attributes(): Transformer {
1119
return function transformer(tree) {
12-
visit(tree, "link", (node: Link) => {
13-
let target_attr = "";
14-
let title_attr = "";
20+
visit<Element>(tree, "element", (node) => {
21+
// console.log(node);
22+
if (node.tagName === "a") {
23+
if (node.properties.href && node.properties.href.startsWith("http")) {
24+
node.properties.target = "_blank";
25+
}
1526

16-
if (node.url.startsWith("http")) {
17-
target_attr = ' target="_blank"';
27+
node.properties.rel = "noopener noreferrer";
1828
}
19-
20-
if (node.title !== null) {
21-
title_attr = ` title="${node.title}"`;
22-
}
23-
24-
((node as unknown) as HTML).type = "html";
25-
((node as unknown) as HTML).value = `<a href="${node.url}"${target_attr}${title_attr} rel="noopener noreferrer">${node.children[0].value}</a>`;
2629
});
2730
};
2831
}

0 commit comments

Comments
 (0)