Skip to content

Commit ea5cf44

Browse files
committed
more hack
1 parent 8bdf1c0 commit ea5cf44

8 files changed

Lines changed: 168 additions & 147 deletions

File tree

src/language-markdown/clean.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ function clean(original, cloned, parent) {
8484
) {
8585
return null;
8686
}
87+
88+
if (original.type === "newLineHack") {
89+
return null;
90+
}
8791
}
8892

8993
clean.ignoredProperties = ignoredProperties;

src/language-markdown/parse/micromark/html-flow-hack.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ To enforce all html tags parse like `<div>`
44
https://github.com/remarkjs/remark-gfm/issues/81
55
*/
66
import { fromMarkdown as originalFromMarkdown } from "mdast-util-from-markdown";
7+
import { htmlFlow } from "micromark-core-commonmark";
78
import { htmlBlockNames, htmlRawNames } from "micromark-util-html-tag-name";
9+
import { codes } from "micromark-util-symbol";
810
import * as assert from "#universal/assert";
911

1012
const isProduction = process.env.NODE_ENV !== "production";
@@ -48,4 +50,16 @@ if (isProduction) {
4850
};
4951
}
5052

51-
export { fromMarkdown };
53+
// interrupts html-text by html-flow
54+
function htmlFlowHackSyntax() {
55+
return {
56+
text: {
57+
[codes.lessThan]: {
58+
...htmlFlow,
59+
add: "before",
60+
},
61+
},
62+
};
63+
}
64+
65+
export { fromMarkdown, htmlFlowHackSyntax };

src/language-markdown/parse/parse-markdown.js

Lines changed: 130 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ import { fromMarkdown as wikiLinkFromMarkdown } from "mdast-util-wiki-link";
33
import { gfm as gfmSyntax } from "micromark-extension-gfm";
44
import { math as mathSyntax } from "micromark-extension-math";
55
import { syntax as wikiLinkSyntax } from "micromark-extension-wiki-link";
6+
import { htmlBlockNames, htmlRawNames } from "micromark-util-html-tag-name";
67
import parseFrontMatter from "../../main/front-matter/parse.js";
7-
import { fromMarkdown } from "./micromark/html-flow-hack.js";
8+
import { mapAst } from "../utilities.js";
9+
import {
10+
fromMarkdown,
11+
htmlFlowHackSyntax,
12+
} from "./micromark/html-flow-hack.js";
813
import { gfmFromMarkdown } from "./micromark/mdast-util-gfm.js";
914
import {
1015
liquidFromMarkdown,
@@ -14,7 +19,13 @@ import {
1419
let markdownParseOptions;
1520
function getMarkdownParseOptions() {
1621
return (markdownParseOptions ??= {
17-
extensions: [gfmSyntax(), mathSyntax(), wikiLinkSyntax(), liquidSyntax()],
22+
extensions: [
23+
gfmSyntax(),
24+
mathSyntax(),
25+
wikiLinkSyntax(),
26+
liquidSyntax(),
27+
htmlFlowHackSyntax(),
28+
],
1829
mdastExtensions: [
1930
gfmFromMarkdown(),
2031
mathFromMarkdown(),
@@ -26,7 +37,7 @@ function getMarkdownParseOptions() {
2637

2738
function parseMarkdown(text) {
2839
const { frontMatter, content } = parseFrontMatter(text);
29-
const ast = fromMarkdown(content, getMarkdownParseOptions());
40+
let ast = fromMarkdown(content, getMarkdownParseOptions());
3041

3142
if (frontMatter) {
3243
const [start, end] = [frontMatter.start, frontMatter.end].map(
@@ -45,7 +56,123 @@ function parseMarkdown(text) {
4556
});
4657
}
4758

59+
ast = transformInlineHtml(ast);
60+
4861
return ast;
4962
}
5063

64+
// with html-flow hack, inline HTML can interrupt paragraphs against commonmark spec
65+
// this function merges such inline HTML back into paragraphs
66+
function transformInlineHtml(ast) {
67+
return mapAst(ast, (node) => {
68+
if (!node.children) {
69+
return node;
70+
}
71+
72+
const { children } = node;
73+
for (let i = 0; i < children.length; i++) {
74+
const child = children[i];
75+
if (child.type !== "html") {
76+
continue;
77+
}
78+
const tagName = child.value
79+
.match(/^<\/?([a-z0-9-]+)/iu)?.[1]
80+
.toLowerCase();
81+
if (!tagName) {
82+
continue;
83+
}
84+
if (
85+
[...htmlBlockNames].includes(tagName) ||
86+
htmlRawNames.includes(tagName)
87+
) {
88+
continue;
89+
}
90+
const prev = children[i - 1];
91+
const next = children[i + 1];
92+
93+
const previousLineDifference =
94+
prev?.type !== "paragraph"
95+
? null
96+
: child.position.start.line - prev.position.end.line;
97+
/** @type {"immediate" | "with-new-line" | "none"} */
98+
const mergePrevious =
99+
previousLineDifference === null
100+
? "none"
101+
: previousLineDifference === 0
102+
? "immediate"
103+
: previousLineDifference === 1
104+
? "with-new-line"
105+
: "none";
106+
107+
const nextLineDifference =
108+
next?.type !== "paragraph"
109+
? null
110+
: next.position.start.line - child.position.end.line;
111+
/** @type {"immediate" | "with-new-line" | "none"} */
112+
const mergeNext =
113+
previousLineDifference === null
114+
? "none"
115+
: nextLineDifference === 0
116+
? "immediate"
117+
: nextLineDifference === 1
118+
? "with-new-line"
119+
: "none";
120+
121+
if (mergePrevious === "none" && mergeNext === "none") {
122+
continue;
123+
}
124+
125+
if (mergePrevious !== "none") {
126+
if (mergePrevious === "with-new-line") {
127+
prev.children.push(newlineTextAfter(prev));
128+
}
129+
prev.children.push(child);
130+
prev.position.end = child.position.end;
131+
children.splice(i, 1);
132+
i--;
133+
134+
if (mergeNext === "none") {
135+
continue;
136+
}
137+
if (mergeNext === "with-new-line") {
138+
prev.children.push(newlineTextAfter(child));
139+
}
140+
prev.children.push(...next.children);
141+
prev.position.end = next.position.end;
142+
children.splice(i + 1, 1);
143+
continue;
144+
}
145+
146+
// mergeNext must be not "none" here
147+
if (mergeNext === "with-new-line") {
148+
next.children.unshift(newlineTextAfter(child));
149+
}
150+
next.children.unshift(child);
151+
next.position.start = child.position.start;
152+
children.splice(i, 1);
153+
}
154+
return node;
155+
});
156+
157+
function newlineTextAfter(node) {
158+
return {
159+
type: "newLineHack",
160+
value: "\n",
161+
raw: "\n",
162+
position: {
163+
start: {
164+
line: node.position.end.line,
165+
column: node.position.end.column,
166+
offset: node.position.end.offset,
167+
},
168+
end: {
169+
line: node.position.end.line + 1,
170+
column: 1,
171+
offset: node.position.end.offset + 1,
172+
},
173+
},
174+
};
175+
}
176+
}
177+
51178
export { parseMarkdown };

src/language-markdown/print/preprocess.js

Lines changed: 7 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { htmlBlockNames, htmlRawNames } from "micromark-util-html-tag-name";
21
import htmlWhitespace from "../../utilities/html-whitespace.js";
32
import { getOrderedListItemInfo, mapAst, splitText } from "../utilities.js";
43

@@ -26,9 +25,6 @@ function preprocess(ast, options) {
2625
} else {
2726
ast = markAlignedList(ast, options);
2827
}
29-
if (options.parser !== "mdx") {
30-
ast = transformInlineHtml(ast);
31-
}
3228
if (options.parser === "mdx") {
3329
ast = splitTextIntoSentencesLegacy(ast);
3430
} else {
@@ -176,6 +172,13 @@ function splitTextIntoSentences(ast) {
176172
});
177173

178174
return mapAst(ast, (node, index, parentStack) => {
175+
if (node.type === "newLineHack") {
176+
return {
177+
type: "sentence",
178+
position: node.position,
179+
children: splitText(node.raw),
180+
};
181+
}
179182
if (node.type !== "text") {
180183
return node;
181184
}
@@ -603,116 +606,4 @@ function markAlignedListLegacy(ast, options) {
603606
}
604607
}
605608

606-
function transformInlineHtml(ast) {
607-
return mapAst(ast, (node) => {
608-
if (!node.children) {
609-
return node;
610-
}
611-
612-
const { children } = node;
613-
for (let i = 0; i < children.length; i++) {
614-
const child = children[i];
615-
if (child.type !== "html") {
616-
continue;
617-
}
618-
const tagName = child.value
619-
.match(/^<\/?([a-z0-9-]+)/iu)?.[1]
620-
.toLowerCase();
621-
if (!tagName) {
622-
continue;
623-
}
624-
if (
625-
[...htmlBlockNames].includes(tagName) ||
626-
htmlRawNames.includes(tagName)
627-
) {
628-
continue;
629-
}
630-
const prev = children[i - 1];
631-
const next = children[i + 1];
632-
633-
const previousLineDifference =
634-
prev?.type !== "paragraph"
635-
? null
636-
: child.position.start.line - prev.position.end.line;
637-
/** @type {"immediate" | "with-new-line" | "none"} */
638-
const mergePrevious =
639-
previousLineDifference === null
640-
? "none"
641-
: previousLineDifference === 0
642-
? "immediate"
643-
: previousLineDifference === 1
644-
? "with-new-line"
645-
: "none";
646-
647-
const nextLineDifference =
648-
next?.type !== "paragraph"
649-
? null
650-
: next.position.start.line - child.position.end.line;
651-
/** @type {"immediate" | "with-new-line" | "none"} */
652-
const mergeNext =
653-
previousLineDifference === null
654-
? "none"
655-
: nextLineDifference === 0
656-
? "immediate"
657-
: nextLineDifference === 1
658-
? "with-new-line"
659-
: "none";
660-
661-
if (mergePrevious === "none" && mergeNext === "none") {
662-
continue;
663-
}
664-
665-
if (mergePrevious !== "none") {
666-
if (mergePrevious === "with-new-line") {
667-
prev.children.push(newlineTextAfter(prev));
668-
}
669-
prev.children.push(child);
670-
prev.position.end = child.position.end;
671-
children.splice(i, 1);
672-
i--;
673-
674-
if (mergeNext === "none") {
675-
continue;
676-
}
677-
if (mergeNext === "with-new-line") {
678-
prev.children.push(newlineTextAfter(child));
679-
}
680-
prev.children.push(...next.children);
681-
prev.position.end = next.position.end;
682-
children.splice(i + 1, 1);
683-
continue;
684-
}
685-
686-
// mergeNext must be not "none" here
687-
if (mergeNext === "with-new-line") {
688-
next.children.unshift(newlineTextAfter(child));
689-
}
690-
next.children.unshift(child);
691-
next.position.start = child.position.start;
692-
children.splice(i, 1);
693-
}
694-
return node;
695-
});
696-
697-
function newlineTextAfter(node) {
698-
return {
699-
type: "text",
700-
value: "\n",
701-
raw: "\n",
702-
position: {
703-
start: {
704-
line: node.position.end.line,
705-
column: node.position.end.column,
706-
offset: node.position.end.offset,
707-
},
708-
end: {
709-
line: node.position.end.line + 1,
710-
column: 1,
711-
offset: node.position.end.offset + 1,
712-
},
713-
},
714-
};
715-
}
716-
}
717-
718609
export default preprocess;

src/language-markdown/traverse/visitor-keys.evaluate.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const visitorKeys = generateReferenceSharedVisitorKeys({
3535
esComment: [],
3636
jsx: [],
3737
math: [],
38+
newLineHack: [],
3839
inlineMath: [],
3940
tableRow: ["children"],
4041
listItem: ["children"],

tests/format/markdown/commonmark-test-suite/__snapshots__/format.test.js.snap

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3476,9 +3476,7 @@ Foo
34763476
baz
34773477
34783478
=====================================output=====================================
3479-
Foo
3480-
3481-
<a href="bar">
3479+
Foo <a href="bar">
34823480
baz
34833481
34843482
================================================================================
@@ -3669,9 +3667,7 @@ proseWrap: "always"
36693667
[Foo bar]
36703668
36713669
=====================================output=====================================
3672-
[Foo bar]:
3673-
3674-
<my url>
3670+
[Foo bar]: <my url>
36753671
'title'
36763672
36773673
[Foo bar]
@@ -11001,9 +10997,7 @@ foo><bar/ >
1100110997
bim!bop />
1100210998
1100310999
=====================================output=====================================
11004-
< a>< foo><bar/ >
11005-
11006-
<foo bar=baz
11000+
< a>< foo><bar/ > <foo bar=baz
1100711001
bim!bop />
1100811002
1100911003
================================================================================

0 commit comments

Comments
 (0)