All checks were successful
Publish To Prod / deploy_and_publish (push) Successful in 35s
577 lines
13 KiB
JavaScript
577 lines
13 KiB
JavaScript
exports.flow = {
|
||
null: {tokenize: tokenizeTable, resolve: resolveTable, interruptible: true}
|
||
}
|
||
|
||
var createSpace = require('micromark/dist/tokenize/factory-space')
|
||
|
||
var setextUnderlineMini = {tokenize: tokenizeSetextUnderlineMini, partial: true}
|
||
var nextPrefixedOrBlank = {tokenize: tokenizeNextPrefixedOrBlank, partial: true}
|
||
|
||
function resolveTable(events, context) {
|
||
var length = events.length
|
||
var index = -1
|
||
var token
|
||
var inHead
|
||
var inDelimiterRow
|
||
var inRow
|
||
var cell
|
||
var content
|
||
var text
|
||
var contentStart
|
||
var contentEnd
|
||
var cellStart
|
||
|
||
while (++index < length) {
|
||
token = events[index][1]
|
||
|
||
if (inRow) {
|
||
if (token.type === 'temporaryTableCellContent') {
|
||
contentStart = contentStart || index
|
||
contentEnd = index
|
||
}
|
||
|
||
if (
|
||
// Combine separate content parts into one.
|
||
(token.type === 'tableCellDivider' || token.type === 'tableRow') &&
|
||
contentEnd
|
||
) {
|
||
content = {
|
||
type: 'tableContent',
|
||
start: events[contentStart][1].start,
|
||
end: events[contentEnd][1].end
|
||
}
|
||
text = {
|
||
type: 'chunkText',
|
||
start: content.start,
|
||
end: content.end,
|
||
contentType: 'text'
|
||
}
|
||
|
||
events.splice(
|
||
contentStart,
|
||
contentEnd - contentStart + 1,
|
||
['enter', content, context],
|
||
['enter', text, context],
|
||
['exit', text, context],
|
||
['exit', content, context]
|
||
)
|
||
index -= contentEnd - contentStart - 3
|
||
length = events.length
|
||
contentStart = undefined
|
||
contentEnd = undefined
|
||
}
|
||
}
|
||
|
||
if (
|
||
events[index][0] === 'exit' &&
|
||
cellStart &&
|
||
cellStart + 1 < index &&
|
||
(token.type === 'tableCellDivider' ||
|
||
(token.type === 'tableRow' &&
|
||
(cellStart + 3 < index ||
|
||
events[cellStart][1].type !== 'whitespace')))
|
||
) {
|
||
cell = {
|
||
type: inDelimiterRow
|
||
? 'tableDelimiter'
|
||
: inHead
|
||
? 'tableHeader'
|
||
: 'tableData',
|
||
start: events[cellStart][1].start,
|
||
end: events[index][1].end
|
||
}
|
||
events.splice(index + (token.type === 'tableCellDivider' ? 1 : 0), 0, [
|
||
'exit',
|
||
cell,
|
||
context
|
||
])
|
||
events.splice(cellStart, 0, ['enter', cell, context])
|
||
index += 2
|
||
length = events.length
|
||
cellStart = index + 1
|
||
}
|
||
|
||
if (token.type === 'tableRow') {
|
||
inRow = events[index][0] === 'enter'
|
||
|
||
if (inRow) {
|
||
cellStart = index + 1
|
||
}
|
||
}
|
||
|
||
if (token.type === 'tableDelimiterRow') {
|
||
inDelimiterRow = events[index][0] === 'enter'
|
||
|
||
if (inDelimiterRow) {
|
||
cellStart = index + 1
|
||
}
|
||
}
|
||
|
||
if (token.type === 'tableHead') {
|
||
inHead = events[index][0] === 'enter'
|
||
}
|
||
}
|
||
|
||
return events
|
||
}
|
||
|
||
function tokenizeTable(effects, ok, nok) {
|
||
var align = []
|
||
var tableHeaderCount = 0
|
||
var seenDelimiter
|
||
var hasDash
|
||
|
||
return start
|
||
|
||
function start(code) {
|
||
/* istanbul ignore if - used to be passed in beta micromark versions. */
|
||
if (code === null || code === -5 || code === -4 || code === -3) {
|
||
return nok(code)
|
||
}
|
||
|
||
effects.enter('table')._align = align
|
||
effects.enter('tableHead')
|
||
effects.enter('tableRow')
|
||
|
||
// If we start with a pipe, we open a cell marker.
|
||
if (code === 124) {
|
||
return cellDividerHead(code)
|
||
}
|
||
|
||
tableHeaderCount++
|
||
effects.enter('temporaryTableCellContent')
|
||
// Can’t be space or eols at the start of a construct, so we’re in a cell.
|
||
return inCellContentHead(code)
|
||
}
|
||
|
||
function cellDividerHead(code) {
|
||
// Always a pipe.
|
||
effects.enter('tableCellDivider')
|
||
effects.consume(code)
|
||
effects.exit('tableCellDivider')
|
||
seenDelimiter = true
|
||
return cellBreakHead
|
||
}
|
||
|
||
function cellBreakHead(code) {
|
||
// EOF, CR, LF, CRLF.
|
||
if (code === null || code === -5 || code === -4 || code === -3) {
|
||
return atRowEndHead(code)
|
||
}
|
||
|
||
// HT, VS, SP.
|
||
if (code === -2 || code === -1 || code === 32) {
|
||
effects.enter('whitespace')
|
||
effects.consume(code)
|
||
return inWhitespaceHead
|
||
}
|
||
|
||
if (seenDelimiter) {
|
||
seenDelimiter = undefined
|
||
tableHeaderCount++
|
||
}
|
||
|
||
// `|`
|
||
if (code === 124) {
|
||
return cellDividerHead(code)
|
||
}
|
||
|
||
// Anything else is cell content.
|
||
effects.enter('temporaryTableCellContent')
|
||
return inCellContentHead(code)
|
||
}
|
||
|
||
function inWhitespaceHead(code) {
|
||
// HT, VS, SP.
|
||
if (code === -2 || code === -1 || code === 32) {
|
||
effects.consume(code)
|
||
return inWhitespaceHead
|
||
}
|
||
|
||
effects.exit('whitespace')
|
||
return cellBreakHead(code)
|
||
}
|
||
|
||
function inCellContentHead(code) {
|
||
// EOF, whitespace, pipe
|
||
if (code === null || code < 0 || code === 32 || code === 124) {
|
||
effects.exit('temporaryTableCellContent')
|
||
return cellBreakHead(code)
|
||
}
|
||
|
||
effects.consume(code)
|
||
// `\`
|
||
return code === 92 ? inCellContentEscapeHead : inCellContentHead
|
||
}
|
||
|
||
function inCellContentEscapeHead(code) {
|
||
// `\` or `|`
|
||
if (code === 92 || code === 124) {
|
||
effects.consume(code)
|
||
return inCellContentHead
|
||
}
|
||
|
||
// Anything else.
|
||
return inCellContentHead(code)
|
||
}
|
||
|
||
function atRowEndHead(code) {
|
||
if (code === null) {
|
||
return nok(code)
|
||
}
|
||
|
||
effects.exit('tableRow')
|
||
effects.exit('tableHead')
|
||
|
||
// Always a line ending.
|
||
effects.enter('lineEnding')
|
||
effects.consume(code)
|
||
effects.exit('lineEnding')
|
||
|
||
// If a setext heading, exit.
|
||
return effects.check(
|
||
setextUnderlineMini,
|
||
nok,
|
||
// Support an indent before the delimiter row.
|
||
createSpace(effects, rowStartDelimiter, 'linePrefix', 4)
|
||
)
|
||
}
|
||
|
||
function rowStartDelimiter(code) {
|
||
// If there’s another space, or we’re at the EOL/EOF, exit.
|
||
if (code === null || code < 0 || code === 32) {
|
||
return nok(code)
|
||
}
|
||
|
||
effects.enter('tableDelimiterRow')
|
||
return atDelimiterRowBreak(code)
|
||
}
|
||
|
||
function atDelimiterRowBreak(code) {
|
||
// EOF, CR, LF, CRLF.
|
||
if (code === null || code === -5 || code === -4 || code === -3) {
|
||
return rowEndDelimiter(code)
|
||
}
|
||
|
||
// HT, VS, SP.
|
||
if (code === -2 || code === -1 || code === 32) {
|
||
effects.enter('whitespace')
|
||
effects.consume(code)
|
||
return inWhitespaceDelimiter
|
||
}
|
||
|
||
// `-`
|
||
if (code === 45) {
|
||
effects.enter('tableDelimiterFiller')
|
||
effects.consume(code)
|
||
hasDash = true
|
||
align.push(null)
|
||
return inFillerDelimiter
|
||
}
|
||
|
||
// `:`
|
||
if (code === 58) {
|
||
effects.enter('tableDelimiterAlignment')
|
||
effects.consume(code)
|
||
effects.exit('tableDelimiterAlignment')
|
||
align.push('left')
|
||
return afterLeftAlignment
|
||
}
|
||
|
||
// If we start with a pipe, we open a cell marker.
|
||
if (code === 124) {
|
||
effects.enter('tableCellDivider')
|
||
effects.consume(code)
|
||
effects.exit('tableCellDivider')
|
||
return atDelimiterRowBreak
|
||
}
|
||
|
||
return nok(code)
|
||
}
|
||
|
||
function inWhitespaceDelimiter(code) {
|
||
// HT, VS, SP.
|
||
if (code === -2 || code === -1 || code === 32) {
|
||
effects.consume(code)
|
||
return inWhitespaceDelimiter
|
||
}
|
||
|
||
effects.exit('whitespace')
|
||
return atDelimiterRowBreak(code)
|
||
}
|
||
|
||
function inFillerDelimiter(code) {
|
||
// `-`
|
||
if (code === 45) {
|
||
effects.consume(code)
|
||
return inFillerDelimiter
|
||
}
|
||
|
||
effects.exit('tableDelimiterFiller')
|
||
|
||
// `:`
|
||
if (code === 58) {
|
||
effects.enter('tableDelimiterAlignment')
|
||
effects.consume(code)
|
||
effects.exit('tableDelimiterAlignment')
|
||
|
||
align[align.length - 1] =
|
||
align[align.length - 1] === 'left' ? 'center' : 'right'
|
||
|
||
return afterRightAlignment
|
||
}
|
||
|
||
return atDelimiterRowBreak(code)
|
||
}
|
||
|
||
function afterLeftAlignment(code) {
|
||
// `-`
|
||
if (code === 45) {
|
||
effects.enter('tableDelimiterFiller')
|
||
effects.consume(code)
|
||
hasDash = true
|
||
return inFillerDelimiter
|
||
}
|
||
|
||
// Anything else is not ok.
|
||
return nok(code)
|
||
}
|
||
|
||
function afterRightAlignment(code) {
|
||
// EOF, CR, LF, CRLF.
|
||
if (code === null || code === -5 || code === -4 || code === -3) {
|
||
return rowEndDelimiter(code)
|
||
}
|
||
|
||
// HT, VS, SP.
|
||
if (code === -2 || code === -1 || code === 32) {
|
||
effects.enter('whitespace')
|
||
effects.consume(code)
|
||
return inWhitespaceDelimiter
|
||
}
|
||
|
||
// `|`
|
||
if (code === 124) {
|
||
effects.enter('tableCellDivider')
|
||
effects.consume(code)
|
||
effects.exit('tableCellDivider')
|
||
return atDelimiterRowBreak
|
||
}
|
||
|
||
return nok(code)
|
||
}
|
||
|
||
function rowEndDelimiter(code) {
|
||
effects.exit('tableDelimiterRow')
|
||
|
||
// Exit if there was no dash at all, or if the header cell count is not the
|
||
// delimiter cell count.
|
||
if (!hasDash || tableHeaderCount !== align.length) {
|
||
return nok(code)
|
||
}
|
||
|
||
if (code === null) {
|
||
return tableClose(code)
|
||
}
|
||
|
||
return effects.check(nextPrefixedOrBlank, tableClose, tableContinue)(code)
|
||
}
|
||
|
||
function tableClose(code) {
|
||
effects.exit('table')
|
||
return ok(code)
|
||
}
|
||
|
||
function tableContinue(code) {
|
||
// Always a line ending.
|
||
effects.enter('lineEnding')
|
||
effects.consume(code)
|
||
effects.exit('lineEnding')
|
||
// We checked that it’s not a prefixed or blank line, so we’re certain a
|
||
// body is coming, though it may be indented.
|
||
return createSpace(effects, bodyStart, 'linePrefix', 4)
|
||
}
|
||
|
||
function bodyStart(code) {
|
||
effects.enter('tableBody')
|
||
return rowStartBody(code)
|
||
}
|
||
|
||
function rowStartBody(code) {
|
||
effects.enter('tableRow')
|
||
|
||
// If we start with a pipe, we open a cell marker.
|
||
if (code === 124) {
|
||
return cellDividerBody(code)
|
||
}
|
||
|
||
effects.enter('temporaryTableCellContent')
|
||
// Can’t be space or eols at the start of a construct, so we’re in a cell.
|
||
return inCellContentBody(code)
|
||
}
|
||
|
||
function cellDividerBody(code) {
|
||
// Always a pipe.
|
||
effects.enter('tableCellDivider')
|
||
effects.consume(code)
|
||
effects.exit('tableCellDivider')
|
||
return cellBreakBody
|
||
}
|
||
|
||
function cellBreakBody(code) {
|
||
// EOF, CR, LF, CRLF.
|
||
if (code === null || code === -5 || code === -4 || code === -3) {
|
||
return atRowEndBody(code)
|
||
}
|
||
|
||
// HT, VS, SP.
|
||
if (code === -2 || code === -1 || code === 32) {
|
||
effects.enter('whitespace')
|
||
effects.consume(code)
|
||
return inWhitespaceBody
|
||
}
|
||
|
||
// `|`
|
||
if (code === 124) {
|
||
return cellDividerBody(code)
|
||
}
|
||
|
||
// Anything else is cell content.
|
||
effects.enter('temporaryTableCellContent')
|
||
return inCellContentBody(code)
|
||
}
|
||
|
||
function inWhitespaceBody(code) {
|
||
// HT, VS, SP.
|
||
if (code === -2 || code === -1 || code === 32) {
|
||
effects.consume(code)
|
||
return inWhitespaceBody
|
||
}
|
||
|
||
effects.exit('whitespace')
|
||
return cellBreakBody(code)
|
||
}
|
||
|
||
function inCellContentBody(code) {
|
||
// EOF, whitespace, pipe
|
||
if (code === null || code < 0 || code === 32 || code === 124) {
|
||
effects.exit('temporaryTableCellContent')
|
||
return cellBreakBody(code)
|
||
}
|
||
|
||
effects.consume(code)
|
||
// `\`
|
||
return code === 92 ? inCellContentEscapeBody : inCellContentBody
|
||
}
|
||
|
||
function inCellContentEscapeBody(code) {
|
||
// `\` or `|`
|
||
if (code === 92 || code === 124) {
|
||
effects.consume(code)
|
||
return inCellContentBody
|
||
}
|
||
|
||
// Anything else.
|
||
return inCellContentBody(code)
|
||
}
|
||
|
||
function atRowEndBody(code) {
|
||
effects.exit('tableRow')
|
||
|
||
if (code === null) {
|
||
return tableBodyClose(code)
|
||
}
|
||
|
||
return effects.check(
|
||
nextPrefixedOrBlank,
|
||
tableBodyClose,
|
||
tableBodyContinue
|
||
)(code)
|
||
}
|
||
|
||
function tableBodyClose(code) {
|
||
effects.exit('tableBody')
|
||
return tableClose(code)
|
||
}
|
||
|
||
function tableBodyContinue(code) {
|
||
// Always a line ending.
|
||
effects.enter('lineEnding')
|
||
effects.consume(code)
|
||
effects.exit('lineEnding')
|
||
// Support an optional prefix, then start a body row.
|
||
return createSpace(effects, rowStartBody, 'linePrefix', 4)
|
||
}
|
||
}
|
||
|
||
// Based on micromark, but that won’t work as we’re in a table, and that expects
|
||
// content.
|
||
// <https://github.com/micromark/micromark/blob/main/lib/tokenize/setext-underline.js>
|
||
function tokenizeSetextUnderlineMini(effects, ok, nok) {
|
||
return start
|
||
|
||
function start(code) {
|
||
// `-`
|
||
if (code !== 45) {
|
||
return nok(code)
|
||
}
|
||
|
||
effects.enter('setextUnderline')
|
||
return sequence(code)
|
||
}
|
||
|
||
function sequence(code) {
|
||
if (code === 45) {
|
||
effects.consume(code)
|
||
return sequence
|
||
}
|
||
|
||
return whitespace(code)
|
||
}
|
||
|
||
function whitespace(code) {
|
||
if (code === -2 || code === -1 || code === 32) {
|
||
effects.consume(code)
|
||
return whitespace
|
||
}
|
||
|
||
if (code === null || code === -5 || code === -4 || code === -3) {
|
||
return ok(code)
|
||
}
|
||
|
||
return nok(code)
|
||
}
|
||
}
|
||
|
||
function tokenizeNextPrefixedOrBlank(effects, ok, nok) {
|
||
var size = 0
|
||
|
||
return start
|
||
|
||
function start(code) {
|
||
// This is a check, so we don’t care about tokens, but we open a bogus one
|
||
// so we’re valid.
|
||
effects.enter('check')
|
||
// EOL.
|
||
effects.consume(code)
|
||
return whitespace
|
||
}
|
||
|
||
function whitespace(code) {
|
||
// VS or SP.
|
||
if (code === -1 || code === 32) {
|
||
effects.consume(code)
|
||
size++
|
||
return size === 4 ? ok : whitespace
|
||
}
|
||
|
||
// EOF or whitespace
|
||
if (code === null || code < 0) {
|
||
return ok(code)
|
||
}
|
||
|
||
// Anything else.
|
||
return nok(code)
|
||
}
|
||
}
|