This commit is contained in:
2024-11-03 17:16:20 +01:00
commit fd6412d6f2
8090 changed files with 778406 additions and 0 deletions

209
node_modules/markdown-it/lib/rules_block/blockquote.mjs generated vendored Normal file
View File

@@ -0,0 +1,209 @@
// Block quotes
import { isSpace } from '../common/utils.mjs'
export default function blockquote (state, startLine, endLine, silent) {
let pos = state.bMarks[startLine] + state.tShift[startLine]
let max = state.eMarks[startLine]
const oldLineMax = state.lineMax
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
// check the block quote marker
if (state.src.charCodeAt(pos) !== 0x3E/* > */) { return false }
// we know that it's going to be a valid blockquote,
// so no point trying to find the end of it in silent mode
if (silent) { return true }
const oldBMarks = []
const oldBSCount = []
const oldSCount = []
const oldTShift = []
const terminatorRules = state.md.block.ruler.getRules('blockquote')
const oldParentType = state.parentType
state.parentType = 'blockquote'
let lastLineEmpty = false
let nextLine
// Search the end of the block
//
// Block ends with either:
// 1. an empty line outside:
// ```
// > test
//
// ```
// 2. an empty line inside:
// ```
// >
// test
// ```
// 3. another tag:
// ```
// > test
// - - -
// ```
for (nextLine = startLine; nextLine < endLine; nextLine++) {
// check if it's outdented, i.e. it's inside list item and indented
// less than said list item:
//
// ```
// 1. anything
// > current blockquote
// 2. checking this line
// ```
const isOutdented = state.sCount[nextLine] < state.blkIndent
pos = state.bMarks[nextLine] + state.tShift[nextLine]
max = state.eMarks[nextLine]
if (pos >= max) {
// Case 1: line is not inside the blockquote, and this line is empty.
break
}
if (state.src.charCodeAt(pos++) === 0x3E/* > */ && !isOutdented) {
// This line is inside the blockquote.
// set offset past spaces and ">"
let initial = state.sCount[nextLine] + 1
let spaceAfterMarker
let adjustTab
// skip one optional space after '>'
if (state.src.charCodeAt(pos) === 0x20 /* space */) {
// ' > test '
// ^ -- position start of line here:
pos++
initial++
adjustTab = false
spaceAfterMarker = true
} else if (state.src.charCodeAt(pos) === 0x09 /* tab */) {
spaceAfterMarker = true
if ((state.bsCount[nextLine] + initial) % 4 === 3) {
// ' >\t test '
// ^ -- position start of line here (tab has width===1)
pos++
initial++
adjustTab = false
} else {
// ' >\t test '
// ^ -- position start of line here + shift bsCount slightly
// to make extra space appear
adjustTab = true
}
} else {
spaceAfterMarker = false
}
let offset = initial
oldBMarks.push(state.bMarks[nextLine])
state.bMarks[nextLine] = pos
while (pos < max) {
const ch = state.src.charCodeAt(pos)
if (isSpace(ch)) {
if (ch === 0x09) {
offset += 4 - (offset + state.bsCount[nextLine] + (adjustTab ? 1 : 0)) % 4
} else {
offset++
}
} else {
break
}
pos++
}
lastLineEmpty = pos >= max
oldBSCount.push(state.bsCount[nextLine])
state.bsCount[nextLine] = state.sCount[nextLine] + 1 + (spaceAfterMarker ? 1 : 0)
oldSCount.push(state.sCount[nextLine])
state.sCount[nextLine] = offset - initial
oldTShift.push(state.tShift[nextLine])
state.tShift[nextLine] = pos - state.bMarks[nextLine]
continue
}
// Case 2: line is not inside the blockquote, and the last line was empty.
if (lastLineEmpty) { break }
// Case 3: another tag found.
let terminate = false
for (let i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true
break
}
}
if (terminate) {
// Quirk to enforce "hard termination mode" for paragraphs;
// normally if you call `tokenize(state, startLine, nextLine)`,
// paragraphs will look below nextLine for paragraph continuation,
// but if blockquote is terminated by another tag, they shouldn't
state.lineMax = nextLine
if (state.blkIndent !== 0) {
// state.blkIndent was non-zero, we now set it to zero,
// so we need to re-calculate all offsets to appear as
// if indent wasn't changed
oldBMarks.push(state.bMarks[nextLine])
oldBSCount.push(state.bsCount[nextLine])
oldTShift.push(state.tShift[nextLine])
oldSCount.push(state.sCount[nextLine])
state.sCount[nextLine] -= state.blkIndent
}
break
}
oldBMarks.push(state.bMarks[nextLine])
oldBSCount.push(state.bsCount[nextLine])
oldTShift.push(state.tShift[nextLine])
oldSCount.push(state.sCount[nextLine])
// A negative indentation means that this is a paragraph continuation
//
state.sCount[nextLine] = -1
}
const oldIndent = state.blkIndent
state.blkIndent = 0
const token_o = state.push('blockquote_open', 'blockquote', 1)
token_o.markup = '>'
const lines = [startLine, 0]
token_o.map = lines
state.md.block.tokenize(state, startLine, nextLine)
const token_c = state.push('blockquote_close', 'blockquote', -1)
token_c.markup = '>'
state.lineMax = oldLineMax
state.parentType = oldParentType
lines[1] = state.line
// Restore original tShift; this might not be necessary since the parser
// has already been here, but just to make sure we can do that.
for (let i = 0; i < oldTShift.length; i++) {
state.bMarks[i + startLine] = oldBMarks[i]
state.tShift[i + startLine] = oldTShift[i]
state.sCount[i + startLine] = oldSCount[i]
state.bsCount[i + startLine] = oldBSCount[i]
}
state.blkIndent = oldIndent
return true
}

30
node_modules/markdown-it/lib/rules_block/code.mjs generated vendored Normal file
View File

@@ -0,0 +1,30 @@
// Code block (4 spaces padded)
export default function code (state, startLine, endLine/*, silent */) {
if (state.sCount[startLine] - state.blkIndent < 4) { return false }
let nextLine = startLine + 1
let last = nextLine
while (nextLine < endLine) {
if (state.isEmpty(nextLine)) {
nextLine++
continue
}
if (state.sCount[nextLine] - state.blkIndent >= 4) {
nextLine++
last = nextLine
continue
}
break
}
state.line = last
const token = state.push('code_block', 'code', 0)
token.content = state.getLines(startLine, last, 4 + state.blkIndent, false) + '\n'
token.map = [startLine, state.line]
return true
}

94
node_modules/markdown-it/lib/rules_block/fence.mjs generated vendored Normal file
View File

@@ -0,0 +1,94 @@
// fences (``` lang, ~~~ lang)
export default function fence (state, startLine, endLine, silent) {
let pos = state.bMarks[startLine] + state.tShift[startLine]
let max = state.eMarks[startLine]
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
if (pos + 3 > max) { return false }
const marker = state.src.charCodeAt(pos)
if (marker !== 0x7E/* ~ */ && marker !== 0x60 /* ` */) {
return false
}
// scan marker length
let mem = pos
pos = state.skipChars(pos, marker)
let len = pos - mem
if (len < 3) { return false }
const markup = state.src.slice(mem, pos)
const params = state.src.slice(pos, max)
if (marker === 0x60 /* ` */) {
if (params.indexOf(String.fromCharCode(marker)) >= 0) {
return false
}
}
// Since start is found, we can report success here in validation mode
if (silent) { return true }
// search end of block
let nextLine = startLine
let haveEndMarker = false
for (;;) {
nextLine++
if (nextLine >= endLine) {
// unclosed block should be autoclosed by end of document.
// also block seems to be autoclosed by end of parent
break
}
pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]
max = state.eMarks[nextLine]
if (pos < max && state.sCount[nextLine] < state.blkIndent) {
// non-empty line with negative indent should stop the list:
// - ```
// test
break
}
if (state.src.charCodeAt(pos) !== marker) { continue }
if (state.sCount[nextLine] - state.blkIndent >= 4) {
// closing fence should be indented less than 4 spaces
continue
}
pos = state.skipChars(pos, marker)
// closing code fence must be at least as long as the opening one
if (pos - mem < len) { continue }
// make sure tail has spaces only
pos = state.skipSpaces(pos)
if (pos < max) { continue }
haveEndMarker = true
// found!
break
}
// If a fence has heading spaces, they should be removed from its inner block
len = state.sCount[startLine]
state.line = nextLine + (haveEndMarker ? 1 : 0)
const token = state.push('fence', 'code', 0)
token.info = params
token.content = state.getLines(startLine + 1, nextLine, len, true)
token.markup = markup
token.map = [startLine, state.line]
return true
}

51
node_modules/markdown-it/lib/rules_block/heading.mjs generated vendored Normal file
View File

@@ -0,0 +1,51 @@
// heading (#, ##, ...)
import { isSpace } from '../common/utils.mjs'
export default function heading (state, startLine, endLine, silent) {
let pos = state.bMarks[startLine] + state.tShift[startLine]
let max = state.eMarks[startLine]
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
let ch = state.src.charCodeAt(pos)
if (ch !== 0x23/* # */ || pos >= max) { return false }
// count heading level
let level = 1
ch = state.src.charCodeAt(++pos)
while (ch === 0x23/* # */ && pos < max && level <= 6) {
level++
ch = state.src.charCodeAt(++pos)
}
if (level > 6 || (pos < max && !isSpace(ch))) { return false }
if (silent) { return true }
// Let's cut tails like ' ### ' from the end of string
max = state.skipSpacesBack(max, pos)
const tmp = state.skipCharsBack(max, 0x23, pos) // #
if (tmp > pos && isSpace(state.src.charCodeAt(tmp - 1))) {
max = tmp
}
state.line = startLine + 1
const token_o = state.push('heading_open', 'h' + String(level), 1)
token_o.markup = '########'.slice(0, level)
token_o.map = [startLine, state.line]
const token_i = state.push('inline', '', 0)
token_i.content = state.src.slice(pos, max).trim()
token_i.map = [startLine, state.line]
token_i.children = []
const token_c = state.push('heading_close', 'h' + String(level), -1)
token_c.markup = '########'.slice(0, level)
return true
}

40
node_modules/markdown-it/lib/rules_block/hr.mjs generated vendored Normal file
View File

@@ -0,0 +1,40 @@
// Horizontal rule
import { isSpace } from '../common/utils.mjs'
export default function hr (state, startLine, endLine, silent) {
const max = state.eMarks[startLine]
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
let pos = state.bMarks[startLine] + state.tShift[startLine]
const marker = state.src.charCodeAt(pos++)
// Check hr marker
if (marker !== 0x2A/* * */ &&
marker !== 0x2D/* - */ &&
marker !== 0x5F/* _ */) {
return false
}
// markers can be mixed with spaces, but there should be at least 3 of them
let cnt = 1
while (pos < max) {
const ch = state.src.charCodeAt(pos++)
if (ch !== marker && !isSpace(ch)) { return false }
if (ch === marker) { cnt++ }
}
if (cnt < 3) { return false }
if (silent) { return true }
state.line = startLine + 1
const token = state.push('hr', 'hr', 0)
token.map = [startLine, state.line]
token.markup = Array(cnt + 1).join(String.fromCharCode(marker))
return true
}

View File

@@ -0,0 +1,69 @@
// HTML block
import block_names from '../common/html_blocks.mjs'
import { HTML_OPEN_CLOSE_TAG_RE } from '../common/html_re.mjs'
// An array of opening and corresponding closing sequences for html tags,
// last argument defines whether it can terminate a paragraph or not
//
const HTML_SEQUENCES = [
[/^<(script|pre|style|textarea)(?=(\s|>|$))/i, /<\/(script|pre|style|textarea)>/i, true],
[/^<!--/, /-->/, true],
[/^<\?/, /\?>/, true],
[/^<![A-Z]/, />/, true],
[/^<!\[CDATA\[/, /\]\]>/, true],
[new RegExp('^</?(' + block_names.join('|') + ')(?=(\\s|/?>|$))', 'i'), /^$/, true],
[new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + '\\s*$'), /^$/, false]
]
export default function html_block (state, startLine, endLine, silent) {
let pos = state.bMarks[startLine] + state.tShift[startLine]
let max = state.eMarks[startLine]
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
if (!state.md.options.html) { return false }
if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false }
let lineText = state.src.slice(pos, max)
let i = 0
for (; i < HTML_SEQUENCES.length; i++) {
if (HTML_SEQUENCES[i][0].test(lineText)) { break }
}
if (i === HTML_SEQUENCES.length) { return false }
if (silent) {
// true if this sequence can be a terminator, false otherwise
return HTML_SEQUENCES[i][2]
}
let nextLine = startLine + 1
// If we are here - we detected HTML block.
// Let's roll down till block end.
if (!HTML_SEQUENCES[i][1].test(lineText)) {
for (; nextLine < endLine; nextLine++) {
if (state.sCount[nextLine] < state.blkIndent) { break }
pos = state.bMarks[nextLine] + state.tShift[nextLine]
max = state.eMarks[nextLine]
lineText = state.src.slice(pos, max)
if (HTML_SEQUENCES[i][1].test(lineText)) {
if (lineText.length !== 0) { nextLine++ }
break
}
}
}
state.line = nextLine
const token = state.push('html_block', '', 0)
token.map = [startLine, nextLine]
token.content = state.getLines(startLine, nextLine, state.blkIndent, true)
return true
}

82
node_modules/markdown-it/lib/rules_block/lheading.mjs generated vendored Normal file
View File

@@ -0,0 +1,82 @@
// lheading (---, ===)
export default function lheading (state, startLine, endLine/*, silent */) {
const terminatorRules = state.md.block.ruler.getRules('paragraph')
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
const oldParentType = state.parentType
state.parentType = 'paragraph' // use paragraph to match terminatorRules
// jump line-by-line until empty one or EOF
let level = 0
let marker
let nextLine = startLine + 1
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
// this would be a code block normally, but after paragraph
// it's considered a lazy continuation regardless of what's there
if (state.sCount[nextLine] - state.blkIndent > 3) { continue }
//
// Check for underline in setext header
//
if (state.sCount[nextLine] >= state.blkIndent) {
let pos = state.bMarks[nextLine] + state.tShift[nextLine]
const max = state.eMarks[nextLine]
if (pos < max) {
marker = state.src.charCodeAt(pos)
if (marker === 0x2D/* - */ || marker === 0x3D/* = */) {
pos = state.skipChars(pos, marker)
pos = state.skipSpaces(pos)
if (pos >= max) {
level = (marker === 0x3D/* = */ ? 1 : 2)
break
}
}
}
}
// quirk for blockquotes, this line should already be checked by that rule
if (state.sCount[nextLine] < 0) { continue }
// Some tags can terminate paragraph without empty line.
let terminate = false
for (let i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true
break
}
}
if (terminate) { break }
}
if (!level) {
// Didn't find valid underline
return false
}
const content = state.getLines(startLine, nextLine, state.blkIndent, false).trim()
state.line = nextLine + 1
const token_o = state.push('heading_open', 'h' + String(level), 1)
token_o.markup = String.fromCharCode(marker)
token_o.map = [startLine, state.line]
const token_i = state.push('inline', '', 0)
token_i.content = content
token_i.map = [startLine, state.line - 1]
token_i.children = []
const token_c = state.push('heading_close', 'h' + String(level), -1)
token_c.markup = String.fromCharCode(marker)
state.parentType = oldParentType
return true
}

331
node_modules/markdown-it/lib/rules_block/list.mjs generated vendored Normal file
View File

@@ -0,0 +1,331 @@
// Lists
import { isSpace } from '../common/utils.mjs'
// Search `[-+*][\n ]`, returns next pos after marker on success
// or -1 on fail.
function skipBulletListMarker (state, startLine) {
const max = state.eMarks[startLine]
let pos = state.bMarks[startLine] + state.tShift[startLine]
const marker = state.src.charCodeAt(pos++)
// Check bullet
if (marker !== 0x2A/* * */ &&
marker !== 0x2D/* - */ &&
marker !== 0x2B/* + */) {
return -1
}
if (pos < max) {
const ch = state.src.charCodeAt(pos)
if (!isSpace(ch)) {
// " -test " - is not a list item
return -1
}
}
return pos
}
// Search `\d+[.)][\n ]`, returns next pos after marker on success
// or -1 on fail.
function skipOrderedListMarker (state, startLine) {
const start = state.bMarks[startLine] + state.tShift[startLine]
const max = state.eMarks[startLine]
let pos = start
// List marker should have at least 2 chars (digit + dot)
if (pos + 1 >= max) { return -1 }
let ch = state.src.charCodeAt(pos++)
if (ch < 0x30/* 0 */ || ch > 0x39/* 9 */) { return -1 }
for (;;) {
// EOL -> fail
if (pos >= max) { return -1 }
ch = state.src.charCodeAt(pos++)
if (ch >= 0x30/* 0 */ && ch <= 0x39/* 9 */) {
// List marker should have no more than 9 digits
// (prevents integer overflow in browsers)
if (pos - start >= 10) { return -1 }
continue
}
// found valid marker
if (ch === 0x29/* ) */ || ch === 0x2e/* . */) {
break
}
return -1
}
if (pos < max) {
ch = state.src.charCodeAt(pos)
if (!isSpace(ch)) {
// " 1.test " - is not a list item
return -1
}
}
return pos
}
function markTightParagraphs (state, idx) {
const level = state.level + 2
for (let i = idx + 2, l = state.tokens.length - 2; i < l; i++) {
if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') {
state.tokens[i + 2].hidden = true
state.tokens[i].hidden = true
i += 2
}
}
}
export default function list (state, startLine, endLine, silent) {
let max, pos, start, token
let nextLine = startLine
let tight = true
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[nextLine] - state.blkIndent >= 4) { return false }
// Special case:
// - item 1
// - item 2
// - item 3
// - item 4
// - this one is a paragraph continuation
if (state.listIndent >= 0 &&
state.sCount[nextLine] - state.listIndent >= 4 &&
state.sCount[nextLine] < state.blkIndent) {
return false
}
let isTerminatingParagraph = false
// limit conditions when list can interrupt
// a paragraph (validation mode only)
if (silent && state.parentType === 'paragraph') {
// Next list item should still terminate previous list item;
//
// This code can fail if plugins use blkIndent as well as lists,
// but I hope the spec gets fixed long before that happens.
//
if (state.sCount[nextLine] >= state.blkIndent) {
isTerminatingParagraph = true
}
}
// Detect list type and position after marker
let isOrdered
let markerValue
let posAfterMarker
if ((posAfterMarker = skipOrderedListMarker(state, nextLine)) >= 0) {
isOrdered = true
start = state.bMarks[nextLine] + state.tShift[nextLine]
markerValue = Number(state.src.slice(start, posAfterMarker - 1))
// If we're starting a new ordered list right after
// a paragraph, it should start with 1.
if (isTerminatingParagraph && markerValue !== 1) return false
} else if ((posAfterMarker = skipBulletListMarker(state, nextLine)) >= 0) {
isOrdered = false
} else {
return false
}
// If we're starting a new unordered list right after
// a paragraph, first line should not be empty.
if (isTerminatingParagraph) {
if (state.skipSpaces(posAfterMarker) >= state.eMarks[nextLine]) return false
}
// For validation mode we can terminate immediately
if (silent) { return true }
// We should terminate list on style change. Remember first one to compare.
const markerCharCode = state.src.charCodeAt(posAfterMarker - 1)
// Start list
const listTokIdx = state.tokens.length
if (isOrdered) {
token = state.push('ordered_list_open', 'ol', 1)
if (markerValue !== 1) {
token.attrs = [['start', markerValue]]
}
} else {
token = state.push('bullet_list_open', 'ul', 1)
}
const listLines = [nextLine, 0]
token.map = listLines
token.markup = String.fromCharCode(markerCharCode)
//
// Iterate list items
//
let prevEmptyEnd = false
const terminatorRules = state.md.block.ruler.getRules('list')
const oldParentType = state.parentType
state.parentType = 'list'
while (nextLine < endLine) {
pos = posAfterMarker
max = state.eMarks[nextLine]
const initial = state.sCount[nextLine] + posAfterMarker - (state.bMarks[nextLine] + state.tShift[nextLine])
let offset = initial
while (pos < max) {
const ch = state.src.charCodeAt(pos)
if (ch === 0x09) {
offset += 4 - (offset + state.bsCount[nextLine]) % 4
} else if (ch === 0x20) {
offset++
} else {
break
}
pos++
}
const contentStart = pos
let indentAfterMarker
if (contentStart >= max) {
// trimming space in "- \n 3" case, indent is 1 here
indentAfterMarker = 1
} else {
indentAfterMarker = offset - initial
}
// If we have more than 4 spaces, the indent is 1
// (the rest is just indented code block)
if (indentAfterMarker > 4) { indentAfterMarker = 1 }
// " - test"
// ^^^^^ - calculating total length of this thing
const indent = initial + indentAfterMarker
// Run subparser & write tokens
token = state.push('list_item_open', 'li', 1)
token.markup = String.fromCharCode(markerCharCode)
const itemLines = [nextLine, 0]
token.map = itemLines
if (isOrdered) {
token.info = state.src.slice(start, posAfterMarker - 1)
}
// change current state, then restore it after parser subcall
const oldTight = state.tight
const oldTShift = state.tShift[nextLine]
const oldSCount = state.sCount[nextLine]
// - example list
// ^ listIndent position will be here
// ^ blkIndent position will be here
//
const oldListIndent = state.listIndent
state.listIndent = state.blkIndent
state.blkIndent = indent
state.tight = true
state.tShift[nextLine] = contentStart - state.bMarks[nextLine]
state.sCount[nextLine] = offset
if (contentStart >= max && state.isEmpty(nextLine + 1)) {
// workaround for this case
// (list item is empty, list terminates before "foo"):
// ~~~~~~~~
// -
//
// foo
// ~~~~~~~~
state.line = Math.min(state.line + 2, endLine)
} else {
state.md.block.tokenize(state, nextLine, endLine, true)
}
// If any of list item is tight, mark list as tight
if (!state.tight || prevEmptyEnd) {
tight = false
}
// Item become loose if finish with empty line,
// but we should filter last element, because it means list finish
prevEmptyEnd = (state.line - nextLine) > 1 && state.isEmpty(state.line - 1)
state.blkIndent = state.listIndent
state.listIndent = oldListIndent
state.tShift[nextLine] = oldTShift
state.sCount[nextLine] = oldSCount
state.tight = oldTight
token = state.push('list_item_close', 'li', -1)
token.markup = String.fromCharCode(markerCharCode)
nextLine = state.line
itemLines[1] = nextLine
if (nextLine >= endLine) { break }
//
// Try to check if list is terminated or continued.
//
if (state.sCount[nextLine] < state.blkIndent) { break }
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[nextLine] - state.blkIndent >= 4) { break }
// fail if terminating block found
let terminate = false
for (let i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true
break
}
}
if (terminate) { break }
// fail if list has another type
if (isOrdered) {
posAfterMarker = skipOrderedListMarker(state, nextLine)
if (posAfterMarker < 0) { break }
start = state.bMarks[nextLine] + state.tShift[nextLine]
} else {
posAfterMarker = skipBulletListMarker(state, nextLine)
if (posAfterMarker < 0) { break }
}
if (markerCharCode !== state.src.charCodeAt(posAfterMarker - 1)) { break }
}
// Finalize list
if (isOrdered) {
token = state.push('ordered_list_close', 'ol', -1)
} else {
token = state.push('bullet_list_close', 'ul', -1)
}
token.markup = String.fromCharCode(markerCharCode)
listLines[1] = nextLine
state.line = nextLine
state.parentType = oldParentType
// mark paragraphs tight if needed
if (tight) {
markTightParagraphs(state, listTokIdx)
}
return true
}

46
node_modules/markdown-it/lib/rules_block/paragraph.mjs generated vendored Normal file
View File

@@ -0,0 +1,46 @@
// Paragraph
export default function paragraph (state, startLine, endLine) {
const terminatorRules = state.md.block.ruler.getRules('paragraph')
const oldParentType = state.parentType
let nextLine = startLine + 1
state.parentType = 'paragraph'
// jump line-by-line until empty one or EOF
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
// this would be a code block normally, but after paragraph
// it's considered a lazy continuation regardless of what's there
if (state.sCount[nextLine] - state.blkIndent > 3) { continue }
// quirk for blockquotes, this line should already be checked by that rule
if (state.sCount[nextLine] < 0) { continue }
// Some tags can terminate paragraph without empty line.
let terminate = false
for (let i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true
break
}
}
if (terminate) { break }
}
const content = state.getLines(startLine, nextLine, state.blkIndent, false).trim()
state.line = nextLine
const token_o = state.push('paragraph_open', 'p', 1)
token_o.map = [startLine, state.line]
const token_i = state.push('inline', '', 0)
token_i.content = content
token_i.map = [startLine, state.line]
token_i.children = []
state.push('paragraph_close', 'p', -1)
state.parentType = oldParentType
return true
}

212
node_modules/markdown-it/lib/rules_block/reference.mjs generated vendored Normal file
View File

@@ -0,0 +1,212 @@
import { isSpace, normalizeReference } from '../common/utils.mjs'
export default function reference (state, startLine, _endLine, silent) {
let pos = state.bMarks[startLine] + state.tShift[startLine]
let max = state.eMarks[startLine]
let nextLine = startLine + 1
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
if (state.src.charCodeAt(pos) !== 0x5B/* [ */) { return false }
function getNextLine (nextLine) {
const endLine = state.lineMax
if (nextLine >= endLine || state.isEmpty(nextLine)) {
// empty line or end of input
return null
}
let isContinuation = false
// this would be a code block normally, but after paragraph
// it's considered a lazy continuation regardless of what's there
if (state.sCount[nextLine] - state.blkIndent > 3) { isContinuation = true }
// quirk for blockquotes, this line should already be checked by that rule
if (state.sCount[nextLine] < 0) { isContinuation = true }
if (!isContinuation) {
const terminatorRules = state.md.block.ruler.getRules('reference')
const oldParentType = state.parentType
state.parentType = 'reference'
// Some tags can terminate paragraph without empty line.
let terminate = false
for (let i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true
break
}
}
state.parentType = oldParentType
if (terminate) {
// terminated by another block
return null
}
}
const pos = state.bMarks[nextLine] + state.tShift[nextLine]
const max = state.eMarks[nextLine]
// max + 1 explicitly includes the newline
return state.src.slice(pos, max + 1)
}
let str = state.src.slice(pos, max + 1)
max = str.length
let labelEnd = -1
for (pos = 1; pos < max; pos++) {
const ch = str.charCodeAt(pos)
if (ch === 0x5B /* [ */) {
return false
} else if (ch === 0x5D /* ] */) {
labelEnd = pos
break
} else if (ch === 0x0A /* \n */) {
const lineContent = getNextLine(nextLine)
if (lineContent !== null) {
str += lineContent
max = str.length
nextLine++
}
} else if (ch === 0x5C /* \ */) {
pos++
if (pos < max && str.charCodeAt(pos) === 0x0A) {
const lineContent = getNextLine(nextLine)
if (lineContent !== null) {
str += lineContent
max = str.length
nextLine++
}
}
}
}
if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) !== 0x3A/* : */) { return false }
// [label]: destination 'title'
// ^^^ skip optional whitespace here
for (pos = labelEnd + 2; pos < max; pos++) {
const ch = str.charCodeAt(pos)
if (ch === 0x0A) {
const lineContent = getNextLine(nextLine)
if (lineContent !== null) {
str += lineContent
max = str.length
nextLine++
}
} else if (isSpace(ch)) {
/* eslint no-empty:0 */
} else {
break
}
}
// [label]: destination 'title'
// ^^^^^^^^^^^ parse this
const destRes = state.md.helpers.parseLinkDestination(str, pos, max)
if (!destRes.ok) { return false }
const href = state.md.normalizeLink(destRes.str)
if (!state.md.validateLink(href)) { return false }
pos = destRes.pos
// save cursor state, we could require to rollback later
const destEndPos = pos
const destEndLineNo = nextLine
// [label]: destination 'title'
// ^^^ skipping those spaces
const start = pos
for (; pos < max; pos++) {
const ch = str.charCodeAt(pos)
if (ch === 0x0A) {
const lineContent = getNextLine(nextLine)
if (lineContent !== null) {
str += lineContent
max = str.length
nextLine++
}
} else if (isSpace(ch)) {
/* eslint no-empty:0 */
} else {
break
}
}
// [label]: destination 'title'
// ^^^^^^^ parse this
let titleRes = state.md.helpers.parseLinkTitle(str, pos, max)
while (titleRes.can_continue) {
const lineContent = getNextLine(nextLine)
if (lineContent === null) break
str += lineContent
pos = max
max = str.length
nextLine++
titleRes = state.md.helpers.parseLinkTitle(str, pos, max, titleRes)
}
let title
if (pos < max && start !== pos && titleRes.ok) {
title = titleRes.str
pos = titleRes.pos
} else {
title = ''
pos = destEndPos
nextLine = destEndLineNo
}
// skip trailing spaces until the rest of the line
while (pos < max) {
const ch = str.charCodeAt(pos)
if (!isSpace(ch)) { break }
pos++
}
if (pos < max && str.charCodeAt(pos) !== 0x0A) {
if (title) {
// garbage at the end of the line after title,
// but it could still be a valid reference if we roll back
title = ''
pos = destEndPos
nextLine = destEndLineNo
while (pos < max) {
const ch = str.charCodeAt(pos)
if (!isSpace(ch)) { break }
pos++
}
}
}
if (pos < max && str.charCodeAt(pos) !== 0x0A) {
// garbage at the end of the line
return false
}
const label = normalizeReference(str.slice(1, labelEnd))
if (!label) {
// CommonMark 0.20 disallows empty labels
return false
}
// Reference can not terminate anything. This check is for safety only.
/* istanbul ignore if */
if (silent) { return true }
if (typeof state.env.references === 'undefined') {
state.env.references = {}
}
if (typeof state.env.references[label] === 'undefined') {
state.env.references[label] = { title, href }
}
state.line = nextLine
return true
}

View File

@@ -0,0 +1,220 @@
// Parser state class
import Token from '../token.mjs'
import { isSpace } from '../common/utils.mjs'
function StateBlock (src, md, env, tokens) {
this.src = src
// link to parser instance
this.md = md
this.env = env
//
// Internal state vartiables
//
this.tokens = tokens
this.bMarks = [] // line begin offsets for fast jumps
this.eMarks = [] // line end offsets for fast jumps
this.tShift = [] // offsets of the first non-space characters (tabs not expanded)
this.sCount = [] // indents for each line (tabs expanded)
// An amount of virtual spaces (tabs expanded) between beginning
// of each line (bMarks) and real beginning of that line.
//
// It exists only as a hack because blockquotes override bMarks
// losing information in the process.
//
// It's used only when expanding tabs, you can think about it as
// an initial tab length, e.g. bsCount=21 applied to string `\t123`
// means first tab should be expanded to 4-21%4 === 3 spaces.
//
this.bsCount = []
// block parser variables
// required block content indent (for example, if we are
// inside a list, it would be positioned after list marker)
this.blkIndent = 0
this.line = 0 // line index in src
this.lineMax = 0 // lines count
this.tight = false // loose/tight mode for lists
this.ddIndent = -1 // indent of the current dd block (-1 if there isn't any)
this.listIndent = -1 // indent of the current list block (-1 if there isn't any)
// can be 'blockquote', 'list', 'root', 'paragraph' or 'reference'
// used in lists to determine if they interrupt a paragraph
this.parentType = 'root'
this.level = 0
// Create caches
// Generate markers.
const s = this.src
for (let start = 0, pos = 0, indent = 0, offset = 0, len = s.length, indent_found = false; pos < len; pos++) {
const ch = s.charCodeAt(pos)
if (!indent_found) {
if (isSpace(ch)) {
indent++
if (ch === 0x09) {
offset += 4 - offset % 4
} else {
offset++
}
continue
} else {
indent_found = true
}
}
if (ch === 0x0A || pos === len - 1) {
if (ch !== 0x0A) { pos++ }
this.bMarks.push(start)
this.eMarks.push(pos)
this.tShift.push(indent)
this.sCount.push(offset)
this.bsCount.push(0)
indent_found = false
indent = 0
offset = 0
start = pos + 1
}
}
// Push fake entry to simplify cache bounds checks
this.bMarks.push(s.length)
this.eMarks.push(s.length)
this.tShift.push(0)
this.sCount.push(0)
this.bsCount.push(0)
this.lineMax = this.bMarks.length - 1 // don't count last fake line
}
// Push new token to "stream".
//
StateBlock.prototype.push = function (type, tag, nesting) {
const token = new Token(type, tag, nesting)
token.block = true
if (nesting < 0) this.level-- // closing tag
token.level = this.level
if (nesting > 0) this.level++ // opening tag
this.tokens.push(token)
return token
}
StateBlock.prototype.isEmpty = function isEmpty (line) {
return this.bMarks[line] + this.tShift[line] >= this.eMarks[line]
}
StateBlock.prototype.skipEmptyLines = function skipEmptyLines (from) {
for (let max = this.lineMax; from < max; from++) {
if (this.bMarks[from] + this.tShift[from] < this.eMarks[from]) {
break
}
}
return from
}
// Skip spaces from given position.
StateBlock.prototype.skipSpaces = function skipSpaces (pos) {
for (let max = this.src.length; pos < max; pos++) {
const ch = this.src.charCodeAt(pos)
if (!isSpace(ch)) { break }
}
return pos
}
// Skip spaces from given position in reverse.
StateBlock.prototype.skipSpacesBack = function skipSpacesBack (pos, min) {
if (pos <= min) { return pos }
while (pos > min) {
if (!isSpace(this.src.charCodeAt(--pos))) { return pos + 1 }
}
return pos
}
// Skip char codes from given position
StateBlock.prototype.skipChars = function skipChars (pos, code) {
for (let max = this.src.length; pos < max; pos++) {
if (this.src.charCodeAt(pos) !== code) { break }
}
return pos
}
// Skip char codes reverse from given position - 1
StateBlock.prototype.skipCharsBack = function skipCharsBack (pos, code, min) {
if (pos <= min) { return pos }
while (pos > min) {
if (code !== this.src.charCodeAt(--pos)) { return pos + 1 }
}
return pos
}
// cut lines range from source.
StateBlock.prototype.getLines = function getLines (begin, end, indent, keepLastLF) {
if (begin >= end) {
return ''
}
const queue = new Array(end - begin)
for (let i = 0, line = begin; line < end; line++, i++) {
let lineIndent = 0
const lineStart = this.bMarks[line]
let first = lineStart
let last
if (line + 1 < end || keepLastLF) {
// No need for bounds check because we have fake entry on tail.
last = this.eMarks[line] + 1
} else {
last = this.eMarks[line]
}
while (first < last && lineIndent < indent) {
const ch = this.src.charCodeAt(first)
if (isSpace(ch)) {
if (ch === 0x09) {
lineIndent += 4 - (lineIndent + this.bsCount[line]) % 4
} else {
lineIndent++
}
} else if (first - lineStart < this.tShift[line]) {
// patched tShift masked characters to look like spaces (blockquotes, list markers)
lineIndent++
} else {
break
}
first++
}
if (lineIndent > indent) {
// partially expanding tabs in code blocks, e.g '\t\tfoobar'
// with indent=2 becomes ' \tfoobar'
queue[i] = new Array(lineIndent - indent + 1).join(' ') + this.src.slice(first, last)
} else {
queue[i] = this.src.slice(first, last)
}
}
return queue.join('')
}
// re-export Token class to use in block rules
StateBlock.prototype.Token = Token
export default StateBlock

228
node_modules/markdown-it/lib/rules_block/table.mjs generated vendored Normal file
View File

@@ -0,0 +1,228 @@
// GFM table, https://github.github.com/gfm/#tables-extension-
import { isSpace } from '../common/utils.mjs'
// Limit the amount of empty autocompleted cells in a table,
// see https://github.com/markdown-it/markdown-it/issues/1000,
//
// Both pulldown-cmark and commonmark-hs limit the number of cells this way to ~200k.
// We set it to 65k, which can expand user input by a factor of x370
// (256x256 square is 1.8kB expanded into 650kB).
const MAX_AUTOCOMPLETED_CELLS = 0x10000
function getLine (state, line) {
const pos = state.bMarks[line] + state.tShift[line]
const max = state.eMarks[line]
return state.src.slice(pos, max)
}
function escapedSplit (str) {
const result = []
const max = str.length
let pos = 0
let ch = str.charCodeAt(pos)
let isEscaped = false
let lastPos = 0
let current = ''
while (pos < max) {
if (ch === 0x7c/* | */) {
if (!isEscaped) {
// pipe separating cells, '|'
result.push(current + str.substring(lastPos, pos))
current = ''
lastPos = pos + 1
} else {
// escaped pipe, '\|'
current += str.substring(lastPos, pos - 1)
lastPos = pos
}
}
isEscaped = (ch === 0x5c/* \ */)
pos++
ch = str.charCodeAt(pos)
}
result.push(current + str.substring(lastPos))
return result
}
export default function table (state, startLine, endLine, silent) {
// should have at least two lines
if (startLine + 2 > endLine) { return false }
let nextLine = startLine + 1
if (state.sCount[nextLine] < state.blkIndent) { return false }
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[nextLine] - state.blkIndent >= 4) { return false }
// first character of the second line should be '|', '-', ':',
// and no other characters are allowed but spaces;
// basically, this is the equivalent of /^[-:|][-:|\s]*$/ regexp
let pos = state.bMarks[nextLine] + state.tShift[nextLine]
if (pos >= state.eMarks[nextLine]) { return false }
const firstCh = state.src.charCodeAt(pos++)
if (firstCh !== 0x7C/* | */ && firstCh !== 0x2D/* - */ && firstCh !== 0x3A/* : */) { return false }
if (pos >= state.eMarks[nextLine]) { return false }
const secondCh = state.src.charCodeAt(pos++)
if (secondCh !== 0x7C/* | */ && secondCh !== 0x2D/* - */ && secondCh !== 0x3A/* : */ && !isSpace(secondCh)) {
return false
}
// if first character is '-', then second character must not be a space
// (due to parsing ambiguity with list)
if (firstCh === 0x2D/* - */ && isSpace(secondCh)) { return false }
while (pos < state.eMarks[nextLine]) {
const ch = state.src.charCodeAt(pos)
if (ch !== 0x7C/* | */ && ch !== 0x2D/* - */ && ch !== 0x3A/* : */ && !isSpace(ch)) { return false }
pos++
}
let lineText = getLine(state, startLine + 1)
let columns = lineText.split('|')
const aligns = []
for (let i = 0; i < columns.length; i++) {
const t = columns[i].trim()
if (!t) {
// allow empty columns before and after table, but not in between columns;
// e.g. allow ` |---| `, disallow ` ---||--- `
if (i === 0 || i === columns.length - 1) {
continue
} else {
return false
}
}
if (!/^:?-+:?$/.test(t)) { return false }
if (t.charCodeAt(t.length - 1) === 0x3A/* : */) {
aligns.push(t.charCodeAt(0) === 0x3A/* : */ ? 'center' : 'right')
} else if (t.charCodeAt(0) === 0x3A/* : */) {
aligns.push('left')
} else {
aligns.push('')
}
}
lineText = getLine(state, startLine).trim()
if (lineText.indexOf('|') === -1) { return false }
if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
columns = escapedSplit(lineText)
if (columns.length && columns[0] === '') columns.shift()
if (columns.length && columns[columns.length - 1] === '') columns.pop()
// header row will define an amount of columns in the entire table,
// and align row should be exactly the same (the rest of the rows can differ)
const columnCount = columns.length
if (columnCount === 0 || columnCount !== aligns.length) { return false }
if (silent) { return true }
const oldParentType = state.parentType
state.parentType = 'table'
// use 'blockquote' lists for termination because it's
// the most similar to tables
const terminatorRules = state.md.block.ruler.getRules('blockquote')
const token_to = state.push('table_open', 'table', 1)
const tableLines = [startLine, 0]
token_to.map = tableLines
const token_tho = state.push('thead_open', 'thead', 1)
token_tho.map = [startLine, startLine + 1]
const token_htro = state.push('tr_open', 'tr', 1)
token_htro.map = [startLine, startLine + 1]
for (let i = 0; i < columns.length; i++) {
const token_ho = state.push('th_open', 'th', 1)
if (aligns[i]) {
token_ho.attrs = [['style', 'text-align:' + aligns[i]]]
}
const token_il = state.push('inline', '', 0)
token_il.content = columns[i].trim()
token_il.children = []
state.push('th_close', 'th', -1)
}
state.push('tr_close', 'tr', -1)
state.push('thead_close', 'thead', -1)
let tbodyLines
let autocompletedCells = 0
for (nextLine = startLine + 2; nextLine < endLine; nextLine++) {
if (state.sCount[nextLine] < state.blkIndent) { break }
let terminate = false
for (let i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true
break
}
}
if (terminate) { break }
lineText = getLine(state, nextLine).trim()
if (!lineText) { break }
if (state.sCount[nextLine] - state.blkIndent >= 4) { break }
columns = escapedSplit(lineText)
if (columns.length && columns[0] === '') columns.shift()
if (columns.length && columns[columns.length - 1] === '') columns.pop()
// note: autocomplete count can be negative if user specifies more columns than header,
// but that does not affect intended use (which is limiting expansion)
autocompletedCells += columnCount - columns.length
if (autocompletedCells > MAX_AUTOCOMPLETED_CELLS) { break }
if (nextLine === startLine + 2) {
const token_tbo = state.push('tbody_open', 'tbody', 1)
token_tbo.map = tbodyLines = [startLine + 2, 0]
}
const token_tro = state.push('tr_open', 'tr', 1)
token_tro.map = [nextLine, nextLine + 1]
for (let i = 0; i < columnCount; i++) {
const token_tdo = state.push('td_open', 'td', 1)
if (aligns[i]) {
token_tdo.attrs = [['style', 'text-align:' + aligns[i]]]
}
const token_il = state.push('inline', '', 0)
token_il.content = columns[i] ? columns[i].trim() : ''
token_il.children = []
state.push('td_close', 'td', -1)
}
state.push('tr_close', 'tr', -1)
}
if (tbodyLines) {
state.push('tbody_close', 'tbody', -1)
tbodyLines[1] = nextLine
}
state.push('table_close', 'table', -1)
tableLines[1] = nextLine
state.parentType = oldParentType
state.line = nextLine
return true
}