mdast-util-from-markdown
Advanced tools
Comparing version 0.8.0 to 0.8.1
773
index.js
'use strict' | ||
module.exports = fromMarkdown | ||
var decode = require('parse-entities/decode-entity') | ||
var toString = require('mdast-util-to-string') | ||
var codes = require('micromark/dist/character/codes') | ||
var constants = require('micromark/dist/constant/constants') | ||
var own = require('micromark/dist/constant/has-own-property') | ||
var types = require('micromark/dist/constant/types') | ||
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') | ||
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( | ||
{ | ||
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: closer(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 stack = [{type: 'root', children: []}] | ||
var index = -1 | ||
var listStack = [] | ||
var length | ||
var handler | ||
var listStart | ||
var event | ||
while (++index < events.length) { | ||
event = events[index] | ||
// We preprocess lists to add `listItem` tokens, and to infer whether | ||
// items the list itself are spread out. | ||
if ( | ||
event[1].type === types.listOrdered || | ||
event[1].type === types.listUnordered | ||
) { | ||
if (event[0] === 'enter') { | ||
listStack.push(index) | ||
} else { | ||
listStart = listStack.pop(index) | ||
index = prepareList(events, listStart, index) | ||
} | ||
} | ||
} | ||
index = -1 | ||
length = events.length | ||
while (++index < length) { | ||
handler = config[events[index][0]] | ||
if (own.call(handler, events[index][1].type)) { | ||
handler[events[index][1].type].call( | ||
{ | ||
stack: stack, | ||
config: config, | ||
enter: enter, | ||
exit: exit, | ||
buffer: buffer, | ||
resume: resume, | ||
sliceSerialize: events[index][2].sliceSerialize, | ||
setData: setData, | ||
getData: getData | ||
}, | ||
events[index][1] | ||
) | ||
} | ||
} | ||
// Figure out `root` position. | ||
stack[0].position = { | ||
start: point( | ||
length ? events[0][1].start : {line: 1, column: 1, offset: 0} | ||
), | ||
end: point( | ||
length | ||
? events[events.length - 2][1].end | ||
: {line: 1, column: 1, offset: 0} | ||
) | ||
} | ||
return stack[0] | ||
} | ||
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 === 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 | ||
) { | ||
// Empty. | ||
} 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) { | ||
tailIndex = index | ||
lineIndex = undefined | ||
while (tailIndex--) { | ||
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 | ||
) { | ||
// 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 === types.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) | ||
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() | ||
node.position.end = point(token.end) | ||
return node | ||
} | ||
function resume() { | ||
var value = toString(this.stack.pop()) | ||
return value | ||
} | ||
// | ||
// Handlers. | ||
// | ||
function onenterlistordered() { | ||
setData('expectingFirstListItemValue', true) | ||
} | ||
function onenterlistitemvalue(token) { | ||
if (getData('expectingFirstListItemValue')) { | ||
this.stack[this.stack.length - 2].start = parseInt( | ||
this.sliceSerialize(token), | ||
constants.numericBaseDecimal | ||
) | ||
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) === codes.equalsTo ? 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')) { | ||
return | ||
} | ||
if (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 | ||
if (type) { | ||
value = safeFromInt( | ||
data, | ||
type === types.characterReferenceMarkerNumeric | ||
? constants.numericBaseDecimal | ||
: constants.numericBaseHexadecimal | ||
) | ||
setData('characterReferenceType') | ||
} else { | ||
value = decode(data) | ||
} | ||
this.stack[this.stack.length - 1].value += value | ||
} | ||
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 length = extensions.length | ||
var index = -1 | ||
while (++index < length) { | ||
extension(config, extensions[index]) | ||
} | ||
return config | ||
} | ||
function extension(config, extension) { | ||
var key | ||
var left | ||
var right | ||
for (key in extension) { | ||
left = own.call(config, key) ? config[key] : (config[key] = {}) | ||
right = extension[key] | ||
if (key === 'canContainEols') { | ||
config[key] = [].concat(left, right) | ||
} else { | ||
Object.assign(left, right) | ||
} | ||
} | ||
} | ||
module.exports = require('./dist') |
{ | ||
"name": "mdast-util-from-markdown", | ||
"version": "0.8.0", | ||
"version": "0.8.1", | ||
"description": "mdast utility to parse markdown", | ||
@@ -30,2 +30,4 @@ "license": "MIT", | ||
"files": [ | ||
"dist/", | ||
"lib/", | ||
"index.js", | ||
@@ -42,2 +44,4 @@ "types/index.d.ts" | ||
"devDependencies": { | ||
"@babel/cli": "^7.0.0", | ||
"@babel/core": "^7.0.0", | ||
"browserify": "^17.0.0", | ||
@@ -53,13 +57,15 @@ "commonmark.json": "^0.29.0", | ||
"rehype-stringify": "^8.0.0", | ||
"remark-cli": "^8.0.0", | ||
"remark-preset-wooorm": "^7.0.0", | ||
"remark-cli": "^9.0.0", | ||
"remark-preset-wooorm": "^8.0.0", | ||
"tape": "^5.0.0", | ||
"tinyify": "^3.0.0", | ||
"unified": "^9.0.0", | ||
"xo": "^0.33.0" | ||
"xo": "^0.34.0" | ||
}, | ||
"scripts": { | ||
"prepare": "if [ -d \"micromark\" ] ; then\ncd \"micromark\" && git pull\nelse\ngit clone https://github.com/micromark/micromark.git\nfi", | ||
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", | ||
"generate-dist": "babel lib/ --out-dir dist/ --quiet --retain-lines --plugins ./micromark/script/babel-transform-constants.js; prettier dist/ --loglevel error --write", | ||
"generate-size": "browserify . -p tinyify -s mdast-util-from-markdown -o mdast-util-from-markdown.min.js; gzip-size mdast-util-from-markdown.min.js", | ||
"generate": "npm run generate-size", | ||
"generate": "npm run generate-dist && npm run generate-size", | ||
"test-api": "node test", | ||
@@ -95,3 +101,6 @@ "test-coverage": "nyc --reporter lcov tape test/index.js", | ||
"unicorn/prefer-set-has": "off" | ||
} | ||
}, | ||
"ignores": [ | ||
"types/" | ||
] | ||
}, | ||
@@ -98,0 +107,0 @@ "remarkConfig": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
50463
7
1350
18
1