Skip to content

Commit 5fada56

Browse files
authored
Better handle JSX block parsing in remark-mdx (#415)
* Add failing test for inline jsx issue * Add repro test * Add and modify remark html block parser * Skip failing test that shouldn't pass yet
1 parent 3913c39 commit 5fada56

File tree

5 files changed

+135
-4
lines changed

5 files changed

+135
-4
lines changed

packages/mdx/test/index.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,16 @@ test('Should process filepath and pass it to the plugins', async () => {
356356
expect(result).toMatch(/HELLO, WORLD!/)
357357
})
358358

359+
test.skip('Should handle inline JSX', async () => {
360+
const result = await mdx(
361+
'Hello, <span style={{ color: "tomato" }}>world</span>'
362+
)
363+
364+
expect(result).toContain(
365+
'<MDXTag name="p" components={components}>Hello, <span style={{ color: "tomato" }}>world</span></MDXTag>'
366+
)
367+
})
368+
359369
test('Should parse and render footnotes', async () => {
360370
const result = await mdx(
361371
'This is a paragraph with a [^footnote]\n\n[^footnote]: Here is the footnote'

packages/remark-mdx/block.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Source copied and then modified from
2+
// https://github.com/remarkjs/remark/blob/master/packages/remark-parse/lib/tokenize/html-block.js
3+
//
4+
// MIT License https://github.com/remarkjs/remark/blob/master/license
5+
6+
const {openCloseTag} = require('./tag')
7+
8+
module.exports = blockHtml
9+
10+
const tab = '\t'
11+
const space = ' '
12+
const lineFeed = '\n'
13+
const lessThan = '<'
14+
15+
const rawOpenExpression = /^<(script|pre|style)(?=(\s|>|$))/i
16+
const rawCloseExpression = /<\/(script|pre|style)>/i
17+
const commentOpenExpression = /^<!--/
18+
const commentCloseExpression = /-->/
19+
const instructionOpenExpression = /^<\?/
20+
const instructionCloseExpression = /\?>/
21+
const directiveOpenExpression = /^<![A-Za-z]/
22+
const directiveCloseExpression = />/
23+
const cdataOpenExpression = /^<!\[CDATA\[/
24+
const cdataCloseExpression = /\]\]>/
25+
const elementCloseExpression = /^$/
26+
const otherElementOpenExpression = new RegExp(openCloseTag.source + '\\s*$')
27+
28+
function blockHtml(eat, value, silent) {
29+
const blocks = '[a-z\\.]+(\\.){0,1}[a-z\\.]'
30+
const elementOpenExpression = new RegExp(
31+
'^</?(' + blocks + ')(?=(\\s|/?>|$))',
32+
'i'
33+
)
34+
const length = value.length
35+
let index = 0
36+
let next
37+
let line
38+
let offset
39+
let character
40+
let count
41+
let sequence
42+
let subvalue
43+
44+
const sequences = [
45+
[rawOpenExpression, rawCloseExpression, true],
46+
[commentOpenExpression, commentCloseExpression, true],
47+
[instructionOpenExpression, instructionCloseExpression, true],
48+
[directiveOpenExpression, directiveCloseExpression, true],
49+
[cdataOpenExpression, cdataCloseExpression, true],
50+
[elementOpenExpression, elementCloseExpression, true],
51+
[otherElementOpenExpression, elementCloseExpression, false]
52+
]
53+
54+
// Eat initial spacing.
55+
while (index < length) {
56+
character = value.charAt(index)
57+
58+
if (character !== tab && character !== space) {
59+
break
60+
}
61+
62+
index++
63+
}
64+
65+
if (value.charAt(index) !== lessThan) {
66+
return
67+
}
68+
69+
next = value.indexOf(lineFeed, index + 1)
70+
next = next === -1 ? length : next
71+
line = value.slice(index, next)
72+
offset = -1
73+
count = sequences.length
74+
75+
while (++offset < count) {
76+
if (sequences[offset][0].test(line)) {
77+
sequence = sequences[offset]
78+
break
79+
}
80+
}
81+
82+
if (!sequence) {
83+
return
84+
}
85+
86+
if (silent) {
87+
return sequence[2]
88+
}
89+
90+
index = next
91+
92+
if (!sequence[1].test(line)) {
93+
while (index < length) {
94+
next = value.indexOf(lineFeed, index + 1)
95+
next = next === -1 ? length : next
96+
line = value.slice(index + 1, next)
97+
98+
if (sequence[1].test(line)) {
99+
if (line) {
100+
index = next
101+
}
102+
103+
break
104+
}
105+
106+
index = next
107+
}
108+
}
109+
110+
subvalue = value.slice(0, index)
111+
112+
return eat(subvalue)({type: 'html', value: subvalue})
113+
}

packages/remark-mdx/index.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const isAlphabetical = require('is-alphabetical')
22
const extractImportsAndExports = require('./extract-imports-and-exports')
3+
const block = require('./block')
34
const {tag} = require('./tag')
45

56
const IMPORT_REGEX = /^import/
@@ -37,7 +38,7 @@ function attachParser(parser) {
3738
const methods = parser.prototype.blockMethods
3839

3940
blocks.esSyntax = tokenizeEsSyntax
40-
blocks.html = wrap(blocks.html)
41+
blocks.html = wrap(block)
4142
inlines.html = wrap(inlines.html, inlineJsx)
4243

4344
methods.splice(methods.indexOf('paragraph'), 0, 'esSyntax')
@@ -89,7 +90,7 @@ function attachCompiler(compiler) {
8990
proto.visitors = Object.assign({}, proto.visitors, {
9091
import: stringifyEsSyntax,
9192
export: stringifyEsSyntax,
92-
jsx: proto.visitors.html
93+
jsx: stringifyEsSyntax
9394
})
9495
}
9596

packages/remark-mdx/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"files": [
2525
"index.js",
2626
"tag.js",
27+
"block.js",
2728
"extract-imports-and-exports.js"
2829
],
2930
"dependencies": {

packages/remark-mdx/test/fixtures/inline-parsing.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,16 @@ module.exports = [
6565
description: 'Handles links',
6666
mdx: 'Hello, <Component>{props.world}</Component> and a moustache! }: <https://johno.com>'
6767
},
68+
{
69+
description: 'Ignores links inside JSX blocks',
70+
mdx: [
71+
'# Hello, world!',
72+
'<Component>from https://johno.com</Component>'
73+
].join('\n\n')
74+
},
6875
{
6976
description: 'Handles multiline JSX blocks',
70-
mdx: `
71-
<Image
77+
mdx: `<Image
7278
src={asset(\`\${SOME_CONSTANT}/some.png\`)}
7379
width="123"
7480
height="456"

0 commit comments

Comments
 (0)