Skip to content

Commit 752dc22

Browse files
committed
Add support for error handler to token parsing
1 parent 901a104 commit 752dc22

File tree

2 files changed

+39
-26
lines changed

2 files changed

+39
-26
lines changed

dev/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* @typedef {import('./lib/index.js').Transform} Transform
88
* @typedef {import('./lib/index.js').Token} Token
99
* @typedef {import('./lib/index.js').CompileContext} CompileContext
10+
* @typedef {import('./lib/index.js').OnError} OnError
1011
*/
1112

1213
export {fromMarkdown} from './lib/index.js'

dev/lib/index.js

+38-26
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,12 @@
5454
* @typedef {Partial<NormalizedExtension>} Extension
5555
* An mdast extension changes how markdown tokens are turned into mdast.
5656
*
57+
* @typedef {(this: Omit<CompileContext, 'sliceSerialize'>, left: Token|undefined, right: Token) => void} OnError
58+
*
5759
* @typedef CompileContext
5860
* mdast compiler context
5961
* @property {Array<Node | Fragment>} stack
60-
* @property {Array<Token>} tokenStack
62+
* @property {Array<[Token, OnError|undefined]>} tokenStack
6163
* @property {(key: string, value?: unknown) => void} setData
6264
* Set data into the key-value store.
6365
* @property {<K extends string>(key: K) => CompileData[K]} getData
@@ -66,7 +68,7 @@
6668
* Capture some of the output data.
6769
* @property {(this: CompileContext) => string} resume
6870
* Stop capturing and access the output data.
69-
* @property {<N extends Node>(this: CompileContext, node: N, token: Token) => N} enter
71+
* @property {<N extends Node>(this: CompileContext, node: N, token: Token, onError?: OnError) => N} enter
7072
* Enter a token.
7173
* @property {(this: CompileContext, token: Token) => Node} exit
7274
* Exit a token.
@@ -309,16 +311,9 @@ function compiler(options = {}) {
309311
}
310312

311313
if (tokenStack.length > 0) {
312-
throw new Error(
313-
'Cannot close document, a token (`' +
314-
tokenStack[tokenStack.length - 1].type +
315-
'`, ' +
316-
stringifyPosition({
317-
start: tokenStack[tokenStack.length - 1].start,
318-
end: tokenStack[tokenStack.length - 1].end
319-
}) +
320-
') is still open'
321-
)
314+
const tail = tokenStack[tokenStack.length - 1]
315+
const handler = tail[1] || defaultOnError
316+
handler.call(context, undefined, tail[0])
322317
}
323318

324319
// Figure out `root` position.
@@ -540,16 +535,17 @@ function compiler(options = {}) {
540535
* @this {CompileContext}
541536
* @param {N} node
542537
* @param {Token} token
538+
* @param {OnError} [errorHandler]
543539
* @returns {N}
544540
*/
545-
function enter(node, token) {
541+
function enter(node, token, errorHandler) {
546542
const parent = this.stack[this.stack.length - 1]
547543
assert(parent, 'expected `parent`')
548544
assert('children' in parent, 'expected `parent`')
549545
// @ts-expect-error: Assume `Node` can exist as a child of `parent`.
550546
parent.children.push(node)
551547
this.stack.push(node)
552-
this.tokenStack.push(token)
548+
this.tokenStack.push([token, errorHandler])
553549
// @ts-expect-error: `end` will be patched later.
554550
node.position = {start: point(token.start)}
555551
return node
@@ -587,18 +583,9 @@ function compiler(options = {}) {
587583
stringifyPosition({start: token.start, end: token.end}) +
588584
'): it’s not open'
589585
)
590-
} else if (open.type !== token.type) {
591-
throw new Error(
592-
'Cannot close `' +
593-
token.type +
594-
'` (' +
595-
stringifyPosition({start: token.start, end: token.end}) +
596-
'): a different token (`' +
597-
open.type +
598-
'`, ' +
599-
stringifyPosition({start: open.start, end: open.end}) +
600-
') is open'
601-
)
586+
} else if (open[0].type !== token.type) {
587+
const handler = open[1] || defaultOnError
588+
handler.call(this, token, open[0])
602589
}
603590

604591
assert(node.type !== 'fragment', 'unexpected fragment `exit`ed')
@@ -1142,3 +1129,28 @@ function extension(combined, extension) {
11421129
}
11431130
}
11441131
}
1132+
1133+
/** @type {OnError} */
1134+
function defaultOnError(left, right) {
1135+
if (left) {
1136+
throw new Error(
1137+
'Cannot close `' +
1138+
left.type +
1139+
'` (' +
1140+
stringifyPosition({start: left.start, end: left.end}) +
1141+
'): a different token (`' +
1142+
right.type +
1143+
'`, ' +
1144+
stringifyPosition({start: right.start, end: right.end}) +
1145+
') is open'
1146+
)
1147+
} else {
1148+
throw new Error(
1149+
'Cannot close document, a token (`' +
1150+
right.type +
1151+
'`, ' +
1152+
stringifyPosition({start: right.start, end: right.end}) +
1153+
') is still open'
1154+
)
1155+
}
1156+
}

0 commit comments

Comments
 (0)