|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import {ok as assert} from 'uvu/assert' |
|
import {toString} from 'mdast-util-to-string' |
|
import {parse} from 'micromark/lib/parse.js' |
|
import {preprocess} from 'micromark/lib/preprocess.js' |
|
import {postprocess} from 'micromark/lib/postprocess.js' |
|
import {decodeNumericCharacterReference} from 'micromark-util-decode-numeric-character-reference' |
|
import {decodeString} from 'micromark-util-decode-string' |
|
import {normalizeIdentifier} from 'micromark-util-normalize-identifier' |
|
import {codes} from 'micromark-util-symbol/codes.js' |
|
import {constants} from 'micromark-util-symbol/constants.js' |
|
import {types} from 'micromark-util-symbol/types.js' |
|
import {decodeNamedCharacterReference} from 'decode-named-character-reference' |
|
import {stringifyPosition} from 'unist-util-stringify-position' |
|
|
|
const own = {}.hasOwnProperty |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const fromMarkdown = |
|
|
|
|
|
|
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
|
|
|
|
|
|
function (value, encoding, options) { |
|
if (typeof encoding !== 'string') { |
|
options = encoding |
|
encoding = undefined |
|
} |
|
|
|
return compiler(options)( |
|
postprocess( |
|
parse(options).document().write(preprocess()(value, encoding, true)) |
|
) |
|
) |
|
} |
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
function compiler(options) { |
|
|
|
const config = { |
|
transforms: [], |
|
canContainEols: ['emphasis', 'fragment', 'heading', 'paragraph', 'strong'], |
|
enter: { |
|
autolink: opener(link), |
|
autolinkProtocol: onenterdata, |
|
autolinkEmail: onenterdata, |
|
atxHeading: opener(heading), |
|
blockQuote: opener(blockQuote), |
|
characterEscape: onenterdata, |
|
characterReference: onenterdata, |
|
codeFenced: opener(codeFlow), |
|
codeFencedFenceInfo: buffer, |
|
codeFencedFenceMeta: buffer, |
|
codeIndented: opener(codeFlow, buffer), |
|
codeText: opener(codeText, buffer), |
|
codeTextData: onenterdata, |
|
data: onenterdata, |
|
codeFlowValue: onenterdata, |
|
definition: opener(definition), |
|
definitionDestinationString: buffer, |
|
definitionLabelString: buffer, |
|
definitionTitleString: buffer, |
|
emphasis: opener(emphasis), |
|
hardBreakEscape: opener(hardBreak), |
|
hardBreakTrailing: opener(hardBreak), |
|
htmlFlow: opener(html, buffer), |
|
htmlFlowData: onenterdata, |
|
htmlText: opener(html, buffer), |
|
htmlTextData: onenterdata, |
|
image: opener(image), |
|
label: buffer, |
|
link: opener(link), |
|
listItem: opener(listItem), |
|
listItemValue: onenterlistitemvalue, |
|
listOrdered: opener(list, onenterlistordered), |
|
listUnordered: opener(list), |
|
paragraph: opener(paragraph), |
|
reference: onenterreference, |
|
referenceString: buffer, |
|
resourceDestinationString: buffer, |
|
resourceTitleString: buffer, |
|
setextHeading: opener(heading), |
|
strong: opener(strong), |
|
thematicBreak: opener(thematicBreak) |
|
}, |
|
exit: { |
|
atxHeading: closer(), |
|
atxHeadingSequence: onexitatxheadingsequence, |
|
autolink: closer(), |
|
autolinkEmail: onexitautolinkemail, |
|
autolinkProtocol: onexitautolinkprotocol, |
|
blockQuote: closer(), |
|
characterEscapeValue: onexitdata, |
|
characterReferenceMarkerHexadecimal: onexitcharacterreferencemarker, |
|
characterReferenceMarkerNumeric: onexitcharacterreferencemarker, |
|
characterReferenceValue: onexitcharacterreferencevalue, |
|
codeFenced: closer(onexitcodefenced), |
|
codeFencedFence: onexitcodefencedfence, |
|
codeFencedFenceInfo: onexitcodefencedfenceinfo, |
|
codeFencedFenceMeta: onexitcodefencedfencemeta, |
|
codeFlowValue: onexitdata, |
|
codeIndented: closer(onexitcodeindented), |
|
codeText: closer(onexitcodetext), |
|
codeTextData: onexitdata, |
|
data: onexitdata, |
|
definition: closer(), |
|
definitionDestinationString: onexitdefinitiondestinationstring, |
|
definitionLabelString: onexitdefinitionlabelstring, |
|
definitionTitleString: onexitdefinitiontitlestring, |
|
emphasis: closer(), |
|
hardBreakEscape: closer(onexithardbreak), |
|
hardBreakTrailing: closer(onexithardbreak), |
|
htmlFlow: closer(onexithtmlflow), |
|
htmlFlowData: onexitdata, |
|
htmlText: closer(onexithtmltext), |
|
htmlTextData: onexitdata, |
|
image: closer(onexitimage), |
|
label: onexitlabel, |
|
labelText: onexitlabeltext, |
|
lineEnding: onexitlineending, |
|
link: closer(onexitlink), |
|
listItem: closer(), |
|
listOrdered: closer(), |
|
listUnordered: closer(), |
|
paragraph: closer(), |
|
referenceString: onexitreferencestring, |
|
resourceDestinationString: onexitresourcedestinationstring, |
|
resourceTitleString: onexitresourcetitlestring, |
|
resource: onexitresource, |
|
setextHeading: closer(onexitsetextheading), |
|
setextHeadingLineSequence: onexitsetextheadinglinesequence, |
|
setextHeadingText: onexitsetextheadingtext, |
|
strong: closer(), |
|
thematicBreak: closer() |
|
} |
|
} |
|
|
|
configure(config, (options || {}).mdastExtensions || []) |
|
|
|
|
|
const data = {} |
|
|
|
return compile |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function compile(events) { |
|
|
|
let tree = {type: 'root', children: []} |
|
|
|
const context = { |
|
stack: [tree], |
|
tokenStack: [], |
|
config, |
|
enter, |
|
exit, |
|
buffer, |
|
resume, |
|
setData, |
|
getData |
|
} |
|
|
|
const listStack = [] |
|
let index = -1 |
|
|
|
while (++index < events.length) { |
|
|
|
|
|
if ( |
|
events[index][1].type === types.listOrdered || |
|
events[index][1].type === types.listUnordered |
|
) { |
|
if (events[index][0] === 'enter') { |
|
listStack.push(index) |
|
} else { |
|
const tail = listStack.pop() |
|
assert(typeof tail === 'number', 'expected list ot be open') |
|
index = prepareList(events, tail, index) |
|
} |
|
} |
|
} |
|
|
|
index = -1 |
|
|
|
while (++index < events.length) { |
|
const handler = config[events[index][0]] |
|
|
|
if (own.call(handler, events[index][1].type)) { |
|
handler[events[index][1].type].call( |
|
Object.assign( |
|
{sliceSerialize: events[index][2].sliceSerialize}, |
|
context |
|
), |
|
events[index][1] |
|
) |
|
} |
|
} |
|
|
|
|
|
if (context.tokenStack.length > 0) { |
|
const tail = context.tokenStack[context.tokenStack.length - 1] |
|
const handler = tail[1] || defaultOnError |
|
handler.call(context, undefined, tail[0]) |
|
} |
|
|
|
|
|
tree.position = { |
|
start: point( |
|
events.length > 0 ? events[0][1].start : {line: 1, column: 1, offset: 0} |
|
), |
|
end: point( |
|
events.length > 0 |
|
? events[events.length - 2][1].end |
|
: {line: 1, column: 1, offset: 0} |
|
) |
|
} |
|
|
|
|
|
index = -1 |
|
while (++index < config.transforms.length) { |
|
tree = config.transforms[index](tree) || tree |
|
} |
|
|
|
return tree |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function prepareList(events, start, length) { |
|
let index = start - 1 |
|
let containerBalance = -1 |
|
let listSpread = false |
|
|
|
let listItem |
|
|
|
let lineIndex |
|
|
|
let firstBlankLineIndex |
|
|
|
let atMarker |
|
|
|
while (++index <= length) { |
|
const event = events[index] |
|
|
|
if ( |
|
event[1].type === types.listUnordered || |
|
event[1].type === types.listOrdered || |
|
event[1].type === types.blockQuote |
|
) { |
|
if (event[0] === 'enter') { |
|
containerBalance++ |
|
} else { |
|
containerBalance-- |
|
} |
|
|
|
atMarker = undefined |
|
} else if (event[1].type === types.lineEndingBlank) { |
|
if (event[0] === 'enter') { |
|
if ( |
|
listItem && |
|
!atMarker && |
|
!containerBalance && |
|
!firstBlankLineIndex |
|
) { |
|
firstBlankLineIndex = index |
|
} |
|
|
|
atMarker = undefined |
|
} |
|
} else if ( |
|
event[1].type === types.linePrefix || |
|
event[1].type === types.listItemValue || |
|
event[1].type === types.listItemMarker || |
|
event[1].type === types.listItemPrefix || |
|
event[1].type === types.listItemPrefixWhitespace |
|
) { |
|
|
|
} else { |
|
atMarker = undefined |
|
} |
|
|
|
if ( |
|
(!containerBalance && |
|
event[0] === 'enter' && |
|
event[1].type === types.listItemPrefix) || |
|
(containerBalance === -1 && |
|
event[0] === 'exit' && |
|
(event[1].type === types.listUnordered || |
|
event[1].type === types.listOrdered)) |
|
) { |
|
if (listItem) { |
|
let tailIndex = index |
|
lineIndex = undefined |
|
|
|
while (tailIndex--) { |
|
const tailEvent = events[tailIndex] |
|
|
|
if ( |
|
tailEvent[1].type === types.lineEnding || |
|
tailEvent[1].type === types.lineEndingBlank |
|
) { |
|
if (tailEvent[0] === 'exit') continue |
|
|
|
if (lineIndex) { |
|
events[lineIndex][1].type = types.lineEndingBlank |
|
listSpread = true |
|
} |
|
|
|
tailEvent[1].type = types.lineEnding |
|
lineIndex = tailIndex |
|
} else if ( |
|
tailEvent[1].type === types.linePrefix || |
|
tailEvent[1].type === types.blockQuotePrefix || |
|
tailEvent[1].type === types.blockQuotePrefixWhitespace || |
|
tailEvent[1].type === types.blockQuoteMarker || |
|
tailEvent[1].type === types.listItemIndent |
|
) { |
|
|
|
} else { |
|
break |
|
} |
|
} |
|
|
|
if ( |
|
firstBlankLineIndex && |
|
(!lineIndex || firstBlankLineIndex < lineIndex) |
|
) { |
|
listItem._spread = true |
|
} |
|
|
|
|
|
listItem.end = Object.assign( |
|
{}, |
|
lineIndex ? events[lineIndex][1].start : event[1].end |
|
) |
|
|
|
events.splice(lineIndex || index, 0, ['exit', listItem, event[2]]) |
|
index++ |
|
length++ |
|
} |
|
|
|
|
|
if (event[1].type === types.listItemPrefix) { |
|
listItem = { |
|
type: 'listItem', |
|
_spread: false, |
|
start: Object.assign({}, event[1].start), |
|
|
|
end: undefined |
|
} |
|
|
|
events.splice(index, 0, ['enter', listItem, event[2]]) |
|
index++ |
|
length++ |
|
firstBlankLineIndex = undefined |
|
atMarker = true |
|
} |
|
} |
|
} |
|
|
|
events[start][1]._spread = listSpread |
|
return length |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function setData(key, value) { |
|
data[key] = value |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getData(key) { |
|
return data[key] |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function opener(create, and) { |
|
return open |
|
|
|
|
|
|
|
|
|
|
|
|
|
function open(token) { |
|
enter.call(this, create(token), token) |
|
if (and) and.call(this, token) |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function buffer() { |
|
this.stack.push({type: 'fragment', children: []}) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function enter(node, token, errorHandler) { |
|
const parent = this.stack[this.stack.length - 1] |
|
assert(parent, 'expected `parent`') |
|
assert('children' in parent, 'expected `parent`') |
|
|
|
parent.children.push(node) |
|
this.stack.push(node) |
|
this.tokenStack.push([token, errorHandler]) |
|
|
|
node.position = {start: point(token.start)} |
|
return node |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function closer(and) { |
|
return close |
|
|
|
|
|
|
|
|
|
|
|
|
|
function close(token) { |
|
if (and) and.call(this, token) |
|
exit.call(this, token) |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function exit(token, onExitError) { |
|
const node = this.stack.pop() |
|
assert(node, 'expected `node`') |
|
const open = this.tokenStack.pop() |
|
|
|
if (!open) { |
|
throw new Error( |
|
'Cannot close `' + |
|
token.type + |
|
'` (' + |
|
stringifyPosition({start: token.start, end: token.end}) + |
|
'): it’s not open' |
|
) |
|
} else if (open[0].type !== token.type) { |
|
if (onExitError) { |
|
onExitError.call(this, token, open[0]) |
|
} else { |
|
const handler = open[1] || defaultOnError |
|
handler.call(this, token, open[0]) |
|
} |
|
} |
|
|
|
assert(node.type !== 'fragment', 'unexpected fragment `exit`ed') |
|
assert(node.position, 'expected `position` to be defined') |
|
node.position.end = point(token.end) |
|
return node |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function resume() { |
|
return toString(this.stack.pop()) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function onenterlistordered() { |
|
setData('expectingFirstListItemValue', true) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onenterlistitemvalue(token) { |
|
if (getData('expectingFirstListItemValue')) { |
|
const ancestor = this.stack[this.stack.length - 2] |
|
assert(ancestor, 'expected nodes on stack') |
|
assert(ancestor.type === 'list', 'expected list on stack') |
|
ancestor.start = Number.parseInt( |
|
this.sliceSerialize(token), |
|
constants.numericBaseDecimal |
|
) |
|
setData('expectingFirstListItemValue') |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitcodefencedfenceinfo() { |
|
const data = this.resume() |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'code', 'expected code on stack') |
|
node.lang = data |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitcodefencedfencemeta() { |
|
const data = this.resume() |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'code', 'expected code on stack') |
|
node.meta = data |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitcodefencedfence() { |
|
|
|
if (getData('flowCodeInside')) return |
|
this.buffer() |
|
setData('flowCodeInside', true) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitcodefenced() { |
|
const data = this.resume() |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'code', 'expected code on stack') |
|
|
|
node.value = data.replace(/^(\r?\n|\r)|(\r?\n|\r)$/g, '') |
|
setData('flowCodeInside') |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitcodeindented() { |
|
const data = this.resume() |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'code', 'expected code on stack') |
|
|
|
node.value = data.replace(/(\r?\n|\r)$/g, '') |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitdefinitionlabelstring(token) { |
|
const label = this.resume() |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'definition', 'expected definition on stack') |
|
|
|
node.label = label |
|
node.identifier = normalizeIdentifier( |
|
this.sliceSerialize(token) |
|
).toLowerCase() |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitdefinitiontitlestring() { |
|
const data = this.resume() |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'definition', 'expected definition on stack') |
|
|
|
node.title = data |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitdefinitiondestinationstring() { |
|
const data = this.resume() |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'definition', 'expected definition on stack') |
|
|
|
node.url = data |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitatxheadingsequence(token) { |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'heading', 'expected heading on stack') |
|
|
|
if (!node.depth) { |
|
const depth = this.sliceSerialize(token).length |
|
|
|
assert( |
|
depth === 1 || |
|
depth === 2 || |
|
depth === 3 || |
|
depth === 4 || |
|
depth === 5 || |
|
depth === 6, |
|
'expected `depth` between `1` and `6`' |
|
) |
|
|
|
node.depth = depth |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitsetextheadingtext() { |
|
setData('setextHeadingSlurpLineEnding', true) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitsetextheadinglinesequence(token) { |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'heading', 'expected heading on stack') |
|
|
|
node.depth = |
|
this.sliceSerialize(token).charCodeAt(0) === codes.equalsTo ? 1 : 2 |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitsetextheading() { |
|
setData('setextHeadingSlurpLineEnding') |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onenterdata(token) { |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert('children' in node, 'expected parent on stack') |
|
|
|
let tail = node.children[node.children.length - 1] |
|
|
|
if (!tail || tail.type !== 'text') { |
|
|
|
tail = text() |
|
|
|
tail.position = {start: point(token.start)} |
|
|
|
node.children.push(tail) |
|
} |
|
|
|
this.stack.push(tail) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexitdata(token) { |
|
const tail = this.stack.pop() |
|
assert(tail, 'expected a `node` to be on the stack') |
|
assert('value' in tail, 'expected a `literal` to be on the stack') |
|
assert(tail.position, 'expected `node` to have an open position') |
|
tail.value += this.sliceSerialize(token) |
|
tail.position.end = point(token.end) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexitlineending(token) { |
|
const context = this.stack[this.stack.length - 1] |
|
assert(context, 'expected `node`') |
|
|
|
|
|
if (getData('atHardBreak')) { |
|
assert('children' in context, 'expected `parent`') |
|
const tail = context.children[context.children.length - 1] |
|
assert(tail.position, 'expected tail to have a starting position') |
|
tail.position.end = point(token.end) |
|
setData('atHardBreak') |
|
return |
|
} |
|
|
|
if ( |
|
!getData('setextHeadingSlurpLineEnding') && |
|
config.canContainEols.includes(context.type) |
|
) { |
|
onenterdata.call(this, token) |
|
onexitdata.call(this, token) |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexithardbreak() { |
|
setData('atHardBreak', true) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexithtmlflow() { |
|
const data = this.resume() |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'html', 'expected html on stack') |
|
|
|
node.value = data |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexithtmltext() { |
|
const data = this.resume() |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'html', 'expected html on stack') |
|
|
|
node.value = data |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexitcodetext() { |
|
const data = this.resume() |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'inlineCode', 'expected inline code on stack') |
|
|
|
node.value = data |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexitlink() { |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'link', 'expected link on stack') |
|
|
|
|
|
|
|
|
|
|
|
if (getData('inReference')) { |
|
|
|
const referenceType = getData('referenceType') || 'shortcut' |
|
|
|
node.type += 'Reference' |
|
|
|
node.referenceType = referenceType |
|
|
|
delete node.url |
|
delete node.title |
|
} else { |
|
|
|
delete node.identifier |
|
|
|
delete node.label |
|
} |
|
|
|
setData('referenceType') |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexitimage() { |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'image', 'expected image on stack') |
|
|
|
|
|
|
|
|
|
|
|
if (getData('inReference')) { |
|
|
|
const referenceType = getData('referenceType') || 'shortcut' |
|
|
|
node.type += 'Reference' |
|
|
|
node.referenceType = referenceType |
|
|
|
delete node.url |
|
delete node.title |
|
} else { |
|
|
|
delete node.identifier |
|
|
|
delete node.label |
|
} |
|
|
|
setData('referenceType') |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexitlabeltext(token) { |
|
const string = this.sliceSerialize(token) |
|
const ancestor = this.stack[this.stack.length - 2] |
|
assert(ancestor, 'expected ancestor on stack') |
|
assert( |
|
ancestor.type === 'image' || ancestor.type === 'link', |
|
'expected image or link on stack' |
|
) |
|
|
|
|
|
|
|
ancestor.label = decodeString(string) |
|
|
|
ancestor.identifier = normalizeIdentifier(string).toLowerCase() |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexitlabel() { |
|
const fragment = this.stack[this.stack.length - 1] |
|
assert(fragment, 'expected node on stack') |
|
assert(fragment.type === 'fragment', 'expected fragment on stack') |
|
const value = this.resume() |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert( |
|
node.type === 'image' || node.type === 'link', |
|
'expected image or link on stack' |
|
) |
|
|
|
|
|
setData('inReference', true) |
|
|
|
if (node.type === 'link') { |
|
|
|
|
|
const children = fragment.children |
|
|
|
node.children = children |
|
} else { |
|
node.alt = value |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexitresourcedestinationstring() { |
|
const data = this.resume() |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert( |
|
node.type === 'image' || node.type === 'link', |
|
'expected image or link on stack' |
|
) |
|
node.url = data |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexitresourcetitlestring() { |
|
const data = this.resume() |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert( |
|
node.type === 'image' || node.type === 'link', |
|
'expected image or link on stack' |
|
) |
|
node.title = data |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexitresource() { |
|
setData('inReference') |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onenterreference() { |
|
setData('referenceType', 'collapsed') |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexitreferencestring(token) { |
|
const label = this.resume() |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert( |
|
node.type === 'image' || node.type === 'link', |
|
'expected image reference or link reference on stack' |
|
) |
|
|
|
|
|
|
|
node.label = label |
|
|
|
node.identifier = normalizeIdentifier( |
|
this.sliceSerialize(token) |
|
).toLowerCase() |
|
setData('referenceType', 'full') |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onexitcharacterreferencemarker(token) { |
|
assert( |
|
token.type === 'characterReferenceMarkerNumeric' || |
|
token.type === 'characterReferenceMarkerHexadecimal' |
|
) |
|
setData('characterReferenceType', token.type) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitcharacterreferencevalue(token) { |
|
const data = this.sliceSerialize(token) |
|
const type = getData('characterReferenceType') |
|
|
|
let value |
|
|
|
if (type) { |
|
value = decodeNumericCharacterReference( |
|
data, |
|
type === types.characterReferenceMarkerNumeric |
|
? constants.numericBaseDecimal |
|
: constants.numericBaseHexadecimal |
|
) |
|
setData('characterReferenceType') |
|
} else { |
|
const result = decodeNamedCharacterReference(data) |
|
assert(result !== false, 'expected reference to decode') |
|
value = result |
|
} |
|
|
|
const tail = this.stack.pop() |
|
assert(tail, 'expected `node`') |
|
assert(tail.position, 'expected `node.position`') |
|
assert('value' in tail, 'expected `node.value`') |
|
tail.value += value |
|
tail.position.end = point(token.end) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitautolinkprotocol(token) { |
|
onexitdata.call(this, token) |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'link', 'expected link on stack') |
|
|
|
node.url = this.sliceSerialize(token) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onexitautolinkemail(token) { |
|
onexitdata.call(this, token) |
|
const node = this.stack[this.stack.length - 1] |
|
assert(node, 'expected node on stack') |
|
assert(node.type === 'link', 'expected link on stack') |
|
|
|
node.url = 'mailto:' + this.sliceSerialize(token) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function blockQuote() { |
|
return {type: 'blockquote', children: []} |
|
} |
|
|
|
|
|
function codeFlow() { |
|
return {type: 'code', lang: null, meta: null, value: ''} |
|
} |
|
|
|
|
|
function codeText() { |
|
return {type: 'inlineCode', value: ''} |
|
} |
|
|
|
|
|
function definition() { |
|
return { |
|
type: 'definition', |
|
identifier: '', |
|
label: null, |
|
title: null, |
|
url: '' |
|
} |
|
} |
|
|
|
|
|
function emphasis() { |
|
return {type: 'emphasis', children: []} |
|
} |
|
|
|
|
|
function heading() { |
|
|
|
return {type: 'heading', depth: undefined, children: []} |
|
} |
|
|
|
|
|
function hardBreak() { |
|
return {type: 'break'} |
|
} |
|
|
|
|
|
function html() { |
|
return {type: 'html', value: ''} |
|
} |
|
|
|
|
|
function image() { |
|
return {type: 'image', title: null, url: '', alt: null} |
|
} |
|
|
|
|
|
function link() { |
|
return {type: 'link', title: null, url: '', children: []} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function list(token) { |
|
return { |
|
type: 'list', |
|
ordered: token.type === 'listOrdered', |
|
start: null, |
|
spread: token._spread, |
|
children: [] |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function listItem(token) { |
|
return { |
|
type: 'listItem', |
|
spread: token._spread, |
|
checked: null, |
|
children: [] |
|
} |
|
} |
|
|
|
|
|
function paragraph() { |
|
return {type: 'paragraph', children: []} |
|
} |
|
|
|
|
|
function strong() { |
|
return {type: 'strong', children: []} |
|
} |
|
|
|
|
|
function text() { |
|
return {type: 'text', value: ''} |
|
} |
|
|
|
|
|
function thematicBreak() { |
|
return {type: 'thematicBreak'} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function point(d) { |
|
return {line: d.line, column: d.column, offset: d.offset} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function configure(combined, extensions) { |
|
let index = -1 |
|
|
|
while (++index < extensions.length) { |
|
const value = extensions[index] |
|
|
|
if (Array.isArray(value)) { |
|
configure(combined, value) |
|
} else { |
|
extension(combined, value) |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function extension(combined, extension) { |
|
|
|
let key |
|
|
|
for (key in extension) { |
|
if (own.call(extension, key)) { |
|
if (key === 'canContainEols') { |
|
const right = extension[key] |
|
if (right) { |
|
combined[key].push(...right) |
|
} |
|
} else if (key === 'transforms') { |
|
const right = extension[key] |
|
if (right) { |
|
combined[key].push(...right) |
|
} |
|
} else if (key === 'enter' || key === 'exit') { |
|
const right = extension[key] |
|
if (right) { |
|
Object.assign(combined[key], right) |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
function defaultOnError(left, right) { |
|
if (left) { |
|
throw new Error( |
|
'Cannot close `' + |
|
left.type + |
|
'` (' + |
|
stringifyPosition({start: left.start, end: left.end}) + |
|
'): a different token (`' + |
|
right.type + |
|
'`, ' + |
|
stringifyPosition({start: right.start, end: right.end}) + |
|
') is open' |
|
) |
|
} else { |
|
throw new Error( |
|
'Cannot close document, a token (`' + |
|
right.type + |
|
'`, ' + |
|
stringifyPosition({start: right.start, end: right.end}) + |
|
') is still open' |
|
) |
|
} |
|
} |
|
|