|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823 |
- 'use strict'
-
- module.exports = fromMarkdown
-
- // These three are compiled away in the `dist/`
-
- var toString = require('mdast-util-to-string')
- var assign = require('micromark/dist/constant/assign')
- var own = require('micromark/dist/constant/has-own-property')
- var normalizeIdentifier = require('micromark/dist/util/normalize-identifier')
- var safeFromInt = require('micromark/dist/util/safe-from-int')
- var parser = require('micromark/dist/parse')
- var preprocessor = require('micromark/dist/preprocess')
- var postprocess = require('micromark/dist/postprocess')
- var decode = require('parse-entities/decode-entity')
- var stringifyPosition = require('unist-util-stringify-position')
-
- function fromMarkdown(value, encoding, options) {
- if (typeof encoding !== 'string') {
- options = encoding
- encoding = undefined
- }
-
- return compiler(options)(
- postprocess(
- parser(options).document().write(preprocessor()(value, encoding, true))
- )
- )
- }
-
- // Note this compiler only understand complete buffering, not streaming.
- function compiler(options) {
- var settings = options || {}
- var config = configure(
- {
- 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()
- }
- },
-
- settings.mdastExtensions || []
- )
-
- var data = {}
-
- return compile
-
- function compile(events) {
- var tree = {type: 'root', children: []}
- var stack = [tree]
- var tokenStack = []
- var listStack = []
- var index = -1
- var handler
- var listStart
-
- var context = {
- stack: stack,
- tokenStack: tokenStack,
- config: config,
- enter: enter,
- exit: exit,
- buffer: buffer,
- resume: resume,
- setData: setData,
- getData: getData
- }
-
- while (++index < events.length) {
- // We preprocess lists to add `listItem` tokens, and to infer whether
- // items the list itself are spread out.
- if (
- events[index][1].type === 'listOrdered' ||
- events[index][1].type === 'listUnordered'
- ) {
- if (events[index][0] === 'enter') {
- listStack.push(index)
- } else {
- listStart = listStack.pop(index)
- index = prepareList(events, listStart, index)
- }
- }
- }
-
- index = -1
-
- while (++index < events.length) {
- handler = config[events[index][0]]
-
- if (own.call(handler, events[index][1].type)) {
- handler[events[index][1].type].call(
- assign({sliceSerialize: events[index][2].sliceSerialize}, context),
- events[index][1]
- )
- }
- }
-
- if (tokenStack.length) {
- throw new Error(
- 'Cannot close document, a token (`' +
- tokenStack[tokenStack.length - 1].type +
- '`, ' +
- stringifyPosition({
- start: tokenStack[tokenStack.length - 1].start,
- end: tokenStack[tokenStack.length - 1].end
- }) +
- ') is still open'
- )
- }
-
- // Figure out `root` position.
- tree.position = {
- start: point(
- events.length ? events[0][1].start : {line: 1, column: 1, offset: 0}
- ),
-
- end: point(
- events.length
- ? 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) {
- var index = start - 1
- var containerBalance = -1
- var listSpread = false
- var listItem
- var tailIndex
- var lineIndex
- var tailEvent
- var event
- var firstBlankLineIndex
- var atMarker
-
- while (++index <= length) {
- event = events[index]
-
- if (
- event[1].type === 'listUnordered' ||
- event[1].type === 'listOrdered' ||
- event[1].type === 'blockQuote'
- ) {
- if (event[0] === 'enter') {
- containerBalance++
- } else {
- containerBalance--
- }
-
- atMarker = undefined
- } else if (event[1].type === 'lineEndingBlank') {
- if (event[0] === 'enter') {
- if (
- listItem &&
- !atMarker &&
- !containerBalance &&
- !firstBlankLineIndex
- ) {
- firstBlankLineIndex = index
- }
-
- atMarker = undefined
- }
- } else if (
- event[1].type === 'linePrefix' ||
- event[1].type === 'listItemValue' ||
- event[1].type === 'listItemMarker' ||
- event[1].type === 'listItemPrefix' ||
- event[1].type === 'listItemPrefixWhitespace'
- ) {
- // Empty.
- } else {
- atMarker = undefined
- }
-
- if (
- (!containerBalance &&
- event[0] === 'enter' &&
- event[1].type === 'listItemPrefix') ||
- (containerBalance === -1 &&
- event[0] === 'exit' &&
- (event[1].type === 'listUnordered' ||
- event[1].type === 'listOrdered'))
- ) {
- if (listItem) {
- tailIndex = index
- lineIndex = undefined
-
- while (tailIndex--) {
- tailEvent = events[tailIndex]
-
- if (
- tailEvent[1].type === 'lineEnding' ||
- tailEvent[1].type === 'lineEndingBlank'
- ) {
- if (tailEvent[0] === 'exit') continue
-
- if (lineIndex) {
- events[lineIndex][1].type = 'lineEndingBlank'
- listSpread = true
- }
-
- tailEvent[1].type = 'lineEnding'
- lineIndex = tailIndex
- } else if (
- tailEvent[1].type === 'linePrefix' ||
- tailEvent[1].type === 'blockQuotePrefix' ||
- tailEvent[1].type === 'blockQuotePrefixWhitespace' ||
- tailEvent[1].type === 'blockQuoteMarker' ||
- tailEvent[1].type === 'listItemIndent'
- ) {
- // Empty
- } else {
- break
- }
- }
-
- if (
- firstBlankLineIndex &&
- (!lineIndex || firstBlankLineIndex < lineIndex)
- ) {
- listItem._spread = true
- }
-
- // Fix position.
- listItem.end = point(
- lineIndex ? events[lineIndex][1].start : event[1].end
- )
-
- events.splice(lineIndex || index, 0, ['exit', listItem, event[2]])
- index++
- length++
- }
-
- // Create a new list item.
- if (event[1].type === 'listItemPrefix') {
- listItem = {
- type: 'listItem',
- _spread: false,
- start: point(event[1].start)
- }
-
- 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 point(d) {
- return {line: d.line, column: d.column, offset: d.offset}
- }
-
- 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) {
- this.stack[this.stack.length - 1].children.push(node)
- this.stack.push(node)
- this.tokenStack.push(token)
- 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) {
- var node = this.stack.pop()
- var 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.type !== token.type) {
- throw new Error(
- 'Cannot close `' +
- token.type +
- '` (' +
- stringifyPosition({start: token.start, end: token.end}) +
- '): a different token (`' +
- open.type +
- '`, ' +
- stringifyPosition({start: open.start, end: open.end}) +
- ') is open'
- )
- }
-
- node.position.end = point(token.end)
- return node
- }
-
- function resume() {
- return toString(this.stack.pop())
- }
-
- //
- // Handlers.
- //
-
- function onenterlistordered() {
- setData('expectingFirstListItemValue', true)
- }
-
- function onenterlistitemvalue(token) {
- if (getData('expectingFirstListItemValue')) {
- this.stack[this.stack.length - 2].start = parseInt(
- this.sliceSerialize(token),
- 10
- )
-
- setData('expectingFirstListItemValue')
- }
- }
-
- function onexitcodefencedfenceinfo() {
- var data = this.resume()
- this.stack[this.stack.length - 1].lang = data
- }
-
- function onexitcodefencedfencemeta() {
- var data = this.resume()
- this.stack[this.stack.length - 1].meta = data
- }
-
- function onexitcodefencedfence() {
- // Exit if this is the closing fence.
- if (getData('flowCodeInside')) return
- this.buffer()
- setData('flowCodeInside', true)
- }
-
- function onexitcodefenced() {
- var data = this.resume()
- this.stack[this.stack.length - 1].value = data.replace(
- /^(\r?\n|\r)|(\r?\n|\r)$/g,
- ''
- )
-
- setData('flowCodeInside')
- }
-
- function onexitcodeindented() {
- var data = this.resume()
- this.stack[this.stack.length - 1].value = data
- }
-
- function onexitdefinitionlabelstring(token) {
- // Discard label, use the source content instead.
- var label = this.resume()
- this.stack[this.stack.length - 1].label = label
- this.stack[this.stack.length - 1].identifier = normalizeIdentifier(
- this.sliceSerialize(token)
- ).toLowerCase()
- }
-
- function onexitdefinitiontitlestring() {
- var data = this.resume()
- this.stack[this.stack.length - 1].title = data
- }
-
- function onexitdefinitiondestinationstring() {
- var data = this.resume()
- this.stack[this.stack.length - 1].url = data
- }
-
- function onexitatxheadingsequence(token) {
- if (!this.stack[this.stack.length - 1].depth) {
- this.stack[this.stack.length - 1].depth = this.sliceSerialize(
- token
- ).length
- }
- }
-
- function onexitsetextheadingtext() {
- setData('setextHeadingSlurpLineEnding', true)
- }
-
- function onexitsetextheadinglinesequence(token) {
- this.stack[this.stack.length - 1].depth =
- this.sliceSerialize(token).charCodeAt(0) === 61 ? 1 : 2
- }
-
- function onexitsetextheading() {
- setData('setextHeadingSlurpLineEnding')
- }
-
- function onenterdata(token) {
- var siblings = this.stack[this.stack.length - 1].children
- var tail = siblings[siblings.length - 1]
-
- if (!tail || tail.type !== 'text') {
- // Add a new text node.
- tail = text()
- tail.position = {start: point(token.start)}
- this.stack[this.stack.length - 1].children.push(tail)
- }
-
- this.stack.push(tail)
- }
-
- function onexitdata(token) {
- var tail = this.stack.pop()
- tail.value += this.sliceSerialize(token)
- tail.position.end = point(token.end)
- }
-
- function onexitlineending(token) {
- var context = this.stack[this.stack.length - 1]
-
- // If we’re at a hard break, include the line ending in there.
- if (getData('atHardBreak')) {
- context.children[context.children.length - 1].position.end = point(
- token.end
- )
-
- setData('atHardBreak')
- return
- }
-
- if (
- !getData('setextHeadingSlurpLineEnding') &&
- config.canContainEols.indexOf(context.type) > -1
- ) {
- onenterdata.call(this, token)
- onexitdata.call(this, token)
- }
- }
-
- function onexithardbreak() {
- setData('atHardBreak', true)
- }
-
- function onexithtmlflow() {
- var data = this.resume()
- this.stack[this.stack.length - 1].value = data
- }
-
- function onexithtmltext() {
- var data = this.resume()
- this.stack[this.stack.length - 1].value = data
- }
-
- function onexitcodetext() {
- var data = this.resume()
- this.stack[this.stack.length - 1].value = data
- }
-
- function onexitlink() {
- var context = this.stack[this.stack.length - 1]
-
- // To do: clean.
- if (getData('inReference')) {
- context.type += 'Reference'
- context.referenceType = getData('referenceType') || 'shortcut'
- delete context.url
- delete context.title
- } else {
- delete context.identifier
- delete context.label
- delete context.referenceType
- }
-
- setData('referenceType')
- }
-
- function onexitimage() {
- var context = this.stack[this.stack.length - 1]
-
- // To do: clean.
- if (getData('inReference')) {
- context.type += 'Reference'
- context.referenceType = getData('referenceType') || 'shortcut'
- delete context.url
- delete context.title
- } else {
- delete context.identifier
- delete context.label
- delete context.referenceType
- }
-
- setData('referenceType')
- }
-
- function onexitlabeltext(token) {
- this.stack[this.stack.length - 2].identifier = normalizeIdentifier(
- this.sliceSerialize(token)
- ).toLowerCase()
- }
-
- function onexitlabel() {
- var fragment = this.stack[this.stack.length - 1]
- var value = this.resume()
-
- this.stack[this.stack.length - 1].label = value
-
- // Assume a reference.
- setData('inReference', true)
-
- if (this.stack[this.stack.length - 1].type === 'link') {
- this.stack[this.stack.length - 1].children = fragment.children
- } else {
- this.stack[this.stack.length - 1].alt = value
- }
- }
-
- function onexitresourcedestinationstring() {
- var data = this.resume()
- this.stack[this.stack.length - 1].url = data
- }
-
- function onexitresourcetitlestring() {
- var data = this.resume()
- this.stack[this.stack.length - 1].title = data
- }
-
- function onexitresource() {
- setData('inReference')
- }
-
- function onenterreference() {
- setData('referenceType', 'collapsed')
- }
-
- function onexitreferencestring(token) {
- var label = this.resume()
- this.stack[this.stack.length - 1].label = label
- this.stack[this.stack.length - 1].identifier = normalizeIdentifier(
- this.sliceSerialize(token)
- ).toLowerCase()
- setData('referenceType', 'full')
- }
-
- function onexitcharacterreferencemarker(token) {
- setData('characterReferenceType', token.type)
- }
-
- function onexitcharacterreferencevalue(token) {
- var data = this.sliceSerialize(token)
- var type = getData('characterReferenceType')
- var value
- var tail
-
- if (type) {
- value = safeFromInt(
- data,
- type === 'characterReferenceMarkerNumeric' ? 10 : 16
- )
-
- setData('characterReferenceType')
- } else {
- value = decode(data)
- }
-
- tail = this.stack.pop()
- tail.value += value
- tail.position.end = point(token.end)
- }
-
- function onexitautolinkprotocol(token) {
- onexitdata.call(this, token)
- this.stack[this.stack.length - 1].url = this.sliceSerialize(token)
- }
-
- function onexitautolinkemail(token) {
- onexitdata.call(this, token)
- this.stack[this.stack.length - 1].url =
- 'mailto:' + this.sliceSerialize(token)
- }
-
- //
- // Creaters.
- //
-
- 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 configure(config, extensions) {
- var index = -1
-
- while (++index < extensions.length) {
- extension(config, extensions[index])
- }
-
- return config
- }
-
- function extension(config, extension) {
- var key
- var left
-
- for (key in extension) {
- left = own.call(config, key) ? config[key] : (config[key] = {})
-
- if (key === 'canContainEols' || key === 'transforms') {
- config[key] = [].concat(left, extension[key])
- } else {
- Object.assign(left, extension[key])
- }
- }
- }
|