Skip to content

Commit 4995154

Browse files
committed
Add errors when extensions mess up
1 parent 01b3448 commit 4995154

File tree

2 files changed

+101
-12
lines changed

2 files changed

+101
-12
lines changed

Diff for: lib/index.js

+55-12
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ var codes = require('micromark/lib/character/codes')
77
var constants = require('micromark/lib/constant/constants')
88
var types = require('micromark/lib/constant/types')
99

10-
var decode = require('parse-entities/decode-entity')
1110
var toString = require('mdast-util-to-string')
11+
var assign = require('micromark/dist/constant/assign')
1212
var own = require('micromark/dist/constant/has-own-property')
1313
var normalizeIdentifier = require('micromark/dist/util/normalize-identifier')
1414
var safeFromInt = require('micromark/dist/util/safe-from-int')
1515
var parser = require('micromark/dist/parse')
1616
var preprocessor = require('micromark/dist/preprocess')
1717
var postprocess = require('micromark/dist/postprocess')
18+
var decode = require('parse-entities/decode-entity')
19+
var stringifyPosition = require('unist-util-stringify-position')
1820

1921
function fromMarkdown(value, encoding, options) {
2022
if (typeof encoding !== 'string') {
@@ -144,13 +146,26 @@ function compiler(options) {
144146

145147
function compile(events) {
146148
var stack = [{type: 'root', children: []}]
149+
var tokenStack = []
147150
var index = -1
148151
var listStack = []
149152
var length
150153
var handler
151154
var listStart
152155
var event
153156

157+
var context = {
158+
stack: stack,
159+
tokenStack: tokenStack,
160+
config: config,
161+
enter: enter,
162+
exit: exit,
163+
buffer: buffer,
164+
resume: resume,
165+
setData: setData,
166+
getData: getData
167+
}
168+
154169
while (++index < events.length) {
155170
event = events[index]
156171

@@ -177,22 +192,25 @@ function compiler(options) {
177192

178193
if (own.call(handler, events[index][1].type)) {
179194
handler[events[index][1].type].call(
180-
{
181-
stack: stack,
182-
config: config,
183-
enter: enter,
184-
exit: exit,
185-
buffer: buffer,
186-
resume: resume,
187-
sliceSerialize: events[index][2].sliceSerialize,
188-
setData: setData,
189-
getData: getData
190-
},
195+
assign({sliceSerialize: events[index][2].sliceSerialize}, context),
191196
events[index][1]
192197
)
193198
}
194199
}
195200

201+
if (tokenStack.length > 0) {
202+
throw new Error(
203+
'Cannot close document, a token (`' +
204+
tokenStack[tokenStack.length - 1].type +
205+
'`, ' +
206+
stringifyPosition({
207+
start: tokenStack[tokenStack.length - 1].start,
208+
end: tokenStack[tokenStack.length - 1].end
209+
}) +
210+
') is still open'
211+
)
212+
}
213+
196214
// Figure out `root` position.
197215
stack[0].position = {
198216
start: point(
@@ -367,6 +385,7 @@ function compiler(options) {
367385
function enter(node, token) {
368386
this.stack[this.stack.length - 1].children.push(node)
369387
this.stack.push(node)
388+
this.tokenStack.push(token)
370389
node.position = {start: point(token.start)}
371390
return node
372391
}
@@ -382,6 +401,30 @@ function compiler(options) {
382401

383402
function exit(token) {
384403
var node = this.stack.pop()
404+
var open = this.tokenStack.pop()
405+
406+
if (!open) {
407+
throw new Error(
408+
'Cannot close `' +
409+
token.type +
410+
'` (' +
411+
stringifyPosition({start: token.start, end: token.end}) +
412+
'): it’s not open'
413+
)
414+
} else if (open.type !== token.type) {
415+
throw new Error(
416+
'Cannot close `' +
417+
token.type +
418+
'` (' +
419+
stringifyPosition({start: token.start, end: token.end}) +
420+
'): a different token (`' +
421+
open.type +
422+
'`, ' +
423+
stringifyPosition({start: open.start, end: open.end}) +
424+
') is open'
425+
)
426+
}
427+
385428
node.position.end = point(token.end)
386429
return node
387430
}

Diff for: test/index.js

+46
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,52 @@ test('mdast-util-from-markdown', function (t) {
123123
this.exit(token)
124124
}
125125

126+
t.throws(
127+
function () {
128+
fromMarkdown('a', {
129+
mdastExtensions: [
130+
{enter: {paragraph: brokenParagraph}, exit: {paragraph: noop}}
131+
]
132+
})
133+
134+
function brokenParagraph(token) {
135+
this.enter({type: 'paragraph', children: []}, token)
136+
}
137+
138+
function noop() {}
139+
},
140+
/Cannot close document, a token \(`paragraph`, 1:1-1:2\) is still open/,
141+
'should crash if a token is opened but not closed'
142+
)
143+
144+
t.throws(
145+
function () {
146+
fromMarkdown('a', {
147+
mdastExtensions: [{enter: {paragraph: brokenParagraph}}]
148+
})
149+
150+
function brokenParagraph(token) {
151+
this.exit(token)
152+
}
153+
},
154+
/Cannot close `paragraph` \(1:1-1:2\): its not open/,
155+
'should crash when closing a token that isn’t open'
156+
)
157+
158+
t.throws(
159+
function () {
160+
fromMarkdown('a', {
161+
mdastExtensions: [{exit: {paragraph: brokenParagraph}}]
162+
})
163+
164+
function brokenParagraph(token) {
165+
this.exit(Object.assign({}, token, {type: 'lol'}))
166+
}
167+
},
168+
/Cannot close `lol` \(1:1-1:2\): a different token \(`paragraph`, 1:1-1:2\) is open/,
169+
'should crash when closing a token when a different one is open'
170+
)
171+
126172
t.deepEqual(
127173
fromMarkdown('<tel:123>').children[0],
128174
{

0 commit comments

Comments
 (0)