summaryrefslogtreecommitdiff
path: root/packages/markdown-it-14.1.0/lib/rules_inline
diff options
context:
space:
mode:
Diffstat (limited to 'packages/markdown-it-14.1.0/lib/rules_inline')
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/autolink.mjs72
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/backticks.mjs60
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/balance_pairs.mjs124
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/emphasis.mjs123
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/entity.mjs51
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/escape.mjs69
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/fragments_join.mjs38
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/html_inline.mjs50
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/image.mjs138
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/link.mjs139
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/linkify.mjs56
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/newline.mjs42
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/state_inline.mjs123
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/strikethrough.mjs127
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_inline/text.mjs86
15 files changed, 1298 insertions, 0 deletions
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/autolink.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/autolink.mjs
new file mode 100644
index 0000000..e71e66f
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/autolink.mjs
@@ -0,0 +1,72 @@
+// Process autolinks '<protocol:...>'
+
+/* eslint max-len:0 */
+const EMAIL_RE = /^([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)$/
+/* eslint-disable-next-line no-control-regex */
+const AUTOLINK_RE = /^([a-zA-Z][a-zA-Z0-9+.-]{1,31}):([^<>\x00-\x20]*)$/
+
+export default function autolink (state, silent) {
+ let pos = state.pos
+
+ if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false }
+
+ const start = state.pos
+ const max = state.posMax
+
+ for (;;) {
+ if (++pos >= max) return false
+
+ const ch = state.src.charCodeAt(pos)
+
+ if (ch === 0x3C /* < */) return false
+ if (ch === 0x3E /* > */) break
+ }
+
+ const url = state.src.slice(start + 1, pos)
+
+ if (AUTOLINK_RE.test(url)) {
+ const fullUrl = state.md.normalizeLink(url)
+ if (!state.md.validateLink(fullUrl)) { return false }
+
+ if (!silent) {
+ const token_o = state.push('link_open', 'a', 1)
+ token_o.attrs = [['href', fullUrl]]
+ token_o.markup = 'autolink'
+ token_o.info = 'auto'
+
+ const token_t = state.push('text', '', 0)
+ token_t.content = state.md.normalizeLinkText(url)
+
+ const token_c = state.push('link_close', 'a', -1)
+ token_c.markup = 'autolink'
+ token_c.info = 'auto'
+ }
+
+ state.pos += url.length + 2
+ return true
+ }
+
+ if (EMAIL_RE.test(url)) {
+ const fullUrl = state.md.normalizeLink('mailto:' + url)
+ if (!state.md.validateLink(fullUrl)) { return false }
+
+ if (!silent) {
+ const token_o = state.push('link_open', 'a', 1)
+ token_o.attrs = [['href', fullUrl]]
+ token_o.markup = 'autolink'
+ token_o.info = 'auto'
+
+ const token_t = state.push('text', '', 0)
+ token_t.content = state.md.normalizeLinkText(url)
+
+ const token_c = state.push('link_close', 'a', -1)
+ token_c.markup = 'autolink'
+ token_c.info = 'auto'
+ }
+
+ state.pos += url.length + 2
+ return true
+ }
+
+ return false
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/backticks.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/backticks.mjs
new file mode 100644
index 0000000..5b926d0
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/backticks.mjs
@@ -0,0 +1,60 @@
+// Parse backticks
+
+export default function backtick (state, silent) {
+ let pos = state.pos
+ const ch = state.src.charCodeAt(pos)
+
+ if (ch !== 0x60/* ` */) { return false }
+
+ const start = pos
+ pos++
+ const max = state.posMax
+
+ // scan marker length
+ while (pos < max && state.src.charCodeAt(pos) === 0x60/* ` */) { pos++ }
+
+ const marker = state.src.slice(start, pos)
+ const openerLength = marker.length
+
+ if (state.backticksScanned && (state.backticks[openerLength] || 0) <= start) {
+ if (!silent) state.pending += marker
+ state.pos += openerLength
+ return true
+ }
+
+ let matchEnd = pos
+ let matchStart
+
+ // Nothing found in the cache, scan until the end of the line (or until marker is found)
+ while ((matchStart = state.src.indexOf('`', matchEnd)) !== -1) {
+ matchEnd = matchStart + 1
+
+ // scan marker length
+ while (matchEnd < max && state.src.charCodeAt(matchEnd) === 0x60/* ` */) { matchEnd++ }
+
+ const closerLength = matchEnd - matchStart
+
+ if (closerLength === openerLength) {
+ // Found matching closer length.
+ if (!silent) {
+ const token = state.push('code_inline', 'code', 0)
+ token.markup = marker
+ token.content = state.src.slice(pos, matchStart)
+ .replace(/\n/g, ' ')
+ .replace(/^ (.+) $/, '$1')
+ }
+ state.pos = matchEnd
+ return true
+ }
+
+ // Some different length found, put it in cache as upper limit of where closer can be found
+ state.backticks[closerLength] = matchStart
+ }
+
+ // Scanned through the end, didn't find anything
+ state.backticksScanned = true
+
+ if (!silent) state.pending += marker
+ state.pos += openerLength
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/balance_pairs.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/balance_pairs.mjs
new file mode 100644
index 0000000..8638d21
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/balance_pairs.mjs
@@ -0,0 +1,124 @@
+// For each opening emphasis-like marker find a matching closing one
+//
+
+function processDelimiters (delimiters) {
+ const openersBottom = {}
+ const max = delimiters.length
+
+ if (!max) return
+
+ // headerIdx is the first delimiter of the current (where closer is) delimiter run
+ let headerIdx = 0
+ let lastTokenIdx = -2 // needs any value lower than -1
+ const jumps = []
+
+ for (let closerIdx = 0; closerIdx < max; closerIdx++) {
+ const closer = delimiters[closerIdx]
+
+ jumps.push(0)
+
+ // markers belong to same delimiter run if:
+ // - they have adjacent tokens
+ // - AND markers are the same
+ //
+ if (delimiters[headerIdx].marker !== closer.marker || lastTokenIdx !== closer.token - 1) {
+ headerIdx = closerIdx
+ }
+
+ lastTokenIdx = closer.token
+
+ // Length is only used for emphasis-specific "rule of 3",
+ // if it's not defined (in strikethrough or 3rd party plugins),
+ // we can default it to 0 to disable those checks.
+ //
+ closer.length = closer.length || 0
+
+ if (!closer.close) continue
+
+ // Previously calculated lower bounds (previous fails)
+ // for each marker, each delimiter length modulo 3,
+ // and for whether this closer can be an opener;
+ // https://github.com/commonmark/cmark/commit/34250e12ccebdc6372b8b49c44fab57c72443460
+ /* eslint-disable-next-line no-prototype-builtins */
+ if (!openersBottom.hasOwnProperty(closer.marker)) {
+ openersBottom[closer.marker] = [-1, -1, -1, -1, -1, -1]
+ }
+
+ const minOpenerIdx = openersBottom[closer.marker][(closer.open ? 3 : 0) + (closer.length % 3)]
+
+ let openerIdx = headerIdx - jumps[headerIdx] - 1
+
+ let newMinOpenerIdx = openerIdx
+
+ for (; openerIdx > minOpenerIdx; openerIdx -= jumps[openerIdx] + 1) {
+ const opener = delimiters[openerIdx]
+
+ if (opener.marker !== closer.marker) continue
+
+ if (opener.open && opener.end < 0) {
+ let isOddMatch = false
+
+ // from spec:
+ //
+ // If one of the delimiters can both open and close emphasis, then the
+ // sum of the lengths of the delimiter runs containing the opening and
+ // closing delimiters must not be a multiple of 3 unless both lengths
+ // are multiples of 3.
+ //
+ if (opener.close || closer.open) {
+ if ((opener.length + closer.length) % 3 === 0) {
+ if (opener.length % 3 !== 0 || closer.length % 3 !== 0) {
+ isOddMatch = true
+ }
+ }
+ }
+
+ if (!isOddMatch) {
+ // If previous delimiter cannot be an opener, we can safely skip
+ // the entire sequence in future checks. This is required to make
+ // sure algorithm has linear complexity (see *_*_*_*_*_... case).
+ //
+ const lastJump = openerIdx > 0 && !delimiters[openerIdx - 1].open
+ ? jumps[openerIdx - 1] + 1
+ : 0
+
+ jumps[closerIdx] = closerIdx - openerIdx + lastJump
+ jumps[openerIdx] = lastJump
+
+ closer.open = false
+ opener.end = closerIdx
+ opener.close = false
+ newMinOpenerIdx = -1
+ // treat next token as start of run,
+ // it optimizes skips in **<...>**a**<...>** pathological case
+ lastTokenIdx = -2
+ break
+ }
+ }
+ }
+
+ if (newMinOpenerIdx !== -1) {
+ // If match for this delimiter run failed, we want to set lower bound for
+ // future lookups. This is required to make sure algorithm has linear
+ // complexity.
+ //
+ // See details here:
+ // https://github.com/commonmark/cmark/issues/178#issuecomment-270417442
+ //
+ openersBottom[closer.marker][(closer.open ? 3 : 0) + ((closer.length || 0) % 3)] = newMinOpenerIdx
+ }
+ }
+}
+
+export default function link_pairs (state) {
+ const tokens_meta = state.tokens_meta
+ const max = state.tokens_meta.length
+
+ processDelimiters(state.delimiters)
+
+ for (let curr = 0; curr < max; curr++) {
+ if (tokens_meta[curr] && tokens_meta[curr].delimiters) {
+ processDelimiters(tokens_meta[curr].delimiters)
+ }
+ }
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/emphasis.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/emphasis.mjs
new file mode 100644
index 0000000..4e3b273
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/emphasis.mjs
@@ -0,0 +1,123 @@
+// Process *this* and _that_
+//
+
+// Insert each marker as a separate text token, and add it to delimiter list
+//
+function emphasis_tokenize (state, silent) {
+ const start = state.pos
+ const marker = state.src.charCodeAt(start)
+
+ if (silent) { return false }
+
+ if (marker !== 0x5F /* _ */ && marker !== 0x2A /* * */) { return false }
+
+ const scanned = state.scanDelims(state.pos, marker === 0x2A)
+
+ for (let i = 0; i < scanned.length; i++) {
+ const token = state.push('text', '', 0)
+ token.content = String.fromCharCode(marker)
+
+ state.delimiters.push({
+ // Char code of the starting marker (number).
+ //
+ marker,
+
+ // Total length of these series of delimiters.
+ //
+ length: scanned.length,
+
+ // A position of the token this delimiter corresponds to.
+ //
+ token: state.tokens.length - 1,
+
+ // If this delimiter is matched as a valid opener, `end` will be
+ // equal to its position, otherwise it's `-1`.
+ //
+ end: -1,
+
+ // Boolean flags that determine if this delimiter could open or close
+ // an emphasis.
+ //
+ open: scanned.can_open,
+ close: scanned.can_close
+ })
+ }
+
+ state.pos += scanned.length
+
+ return true
+}
+
+function postProcess (state, delimiters) {
+ const max = delimiters.length
+
+ for (let i = max - 1; i >= 0; i--) {
+ const startDelim = delimiters[i]
+
+ if (startDelim.marker !== 0x5F/* _ */ && startDelim.marker !== 0x2A/* * */) {
+ continue
+ }
+
+ // Process only opening markers
+ if (startDelim.end === -1) {
+ continue
+ }
+
+ const endDelim = delimiters[startDelim.end]
+
+ // If the previous delimiter has the same marker and is adjacent to this one,
+ // merge those into one strong delimiter.
+ //
+ // `<em><em>whatever</em></em>` -> `<strong>whatever</strong>`
+ //
+ const isStrong = i > 0 &&
+ delimiters[i - 1].end === startDelim.end + 1 &&
+ // check that first two markers match and adjacent
+ delimiters[i - 1].marker === startDelim.marker &&
+ delimiters[i - 1].token === startDelim.token - 1 &&
+ // check that last two markers are adjacent (we can safely assume they match)
+ delimiters[startDelim.end + 1].token === endDelim.token + 1
+
+ const ch = String.fromCharCode(startDelim.marker)
+
+ const token_o = state.tokens[startDelim.token]
+ token_o.type = isStrong ? 'strong_open' : 'em_open'
+ token_o.tag = isStrong ? 'strong' : 'em'
+ token_o.nesting = 1
+ token_o.markup = isStrong ? ch + ch : ch
+ token_o.content = ''
+
+ const token_c = state.tokens[endDelim.token]
+ token_c.type = isStrong ? 'strong_close' : 'em_close'
+ token_c.tag = isStrong ? 'strong' : 'em'
+ token_c.nesting = -1
+ token_c.markup = isStrong ? ch + ch : ch
+ token_c.content = ''
+
+ if (isStrong) {
+ state.tokens[delimiters[i - 1].token].content = ''
+ state.tokens[delimiters[startDelim.end + 1].token].content = ''
+ i--
+ }
+ }
+}
+
+// Walk through delimiter list and replace text tokens with tags
+//
+function emphasis_post_process (state) {
+ const tokens_meta = state.tokens_meta
+ const max = state.tokens_meta.length
+
+ postProcess(state, state.delimiters)
+
+ for (let curr = 0; curr < max; curr++) {
+ if (tokens_meta[curr] && tokens_meta[curr].delimiters) {
+ postProcess(state, tokens_meta[curr].delimiters)
+ }
+ }
+}
+
+export default {
+ tokenize: emphasis_tokenize,
+ postProcess: emphasis_post_process
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/entity.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/entity.mjs
new file mode 100644
index 0000000..e65191d
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/entity.mjs
@@ -0,0 +1,51 @@
+// Process html entity - &#123;, &#xAF;, &quot;, ...
+
+import { decodeHTML } from 'entities'
+import { isValidEntityCode, fromCodePoint } from '../common/utils.mjs'
+
+const DIGITAL_RE = /^&#((?:x[a-f0-9]{1,6}|[0-9]{1,7}));/i
+const NAMED_RE = /^&([a-z][a-z0-9]{1,31});/i
+
+export default function entity (state, silent) {
+ const pos = state.pos
+ const max = state.posMax
+
+ if (state.src.charCodeAt(pos) !== 0x26/* & */) return false
+
+ if (pos + 1 >= max) return false
+
+ const ch = state.src.charCodeAt(pos + 1)
+
+ if (ch === 0x23 /* # */) {
+ const match = state.src.slice(pos).match(DIGITAL_RE)
+ if (match) {
+ if (!silent) {
+ const code = match[1][0].toLowerCase() === 'x' ? parseInt(match[1].slice(1), 16) : parseInt(match[1], 10)
+
+ const token = state.push('text_special', '', 0)
+ token.content = isValidEntityCode(code) ? fromCodePoint(code) : fromCodePoint(0xFFFD)
+ token.markup = match[0]
+ token.info = 'entity'
+ }
+ state.pos += match[0].length
+ return true
+ }
+ } else {
+ const match = state.src.slice(pos).match(NAMED_RE)
+ if (match) {
+ const decoded = decodeHTML(match[0])
+ if (decoded !== match[0]) {
+ if (!silent) {
+ const token = state.push('text_special', '', 0)
+ token.content = decoded
+ token.markup = match[0]
+ token.info = 'entity'
+ }
+ state.pos += match[0].length
+ return true
+ }
+ }
+ }
+
+ return false
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/escape.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/escape.mjs
new file mode 100644
index 0000000..aa3728e
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/escape.mjs
@@ -0,0 +1,69 @@
+// Process escaped chars and hardbreaks
+
+import { isSpace } from '../common/utils.mjs'
+
+const ESCAPED = []
+
+for (let i = 0; i < 256; i++) { ESCAPED.push(0) }
+
+'\\!"#$%&\'()*+,./:;<=>?@[]^_`{|}~-'
+ .split('').forEach(function (ch) { ESCAPED[ch.charCodeAt(0)] = 1 })
+
+export default function escape (state, silent) {
+ let pos = state.pos
+ const max = state.posMax
+
+ if (state.src.charCodeAt(pos) !== 0x5C/* \ */) return false
+ pos++
+
+ // '\' at the end of the inline block
+ if (pos >= max) return false
+
+ let ch1 = state.src.charCodeAt(pos)
+
+ if (ch1 === 0x0A) {
+ if (!silent) {
+ state.push('hardbreak', 'br', 0)
+ }
+
+ pos++
+ // skip leading whitespaces from next line
+ while (pos < max) {
+ ch1 = state.src.charCodeAt(pos)
+ if (!isSpace(ch1)) break
+ pos++
+ }
+
+ state.pos = pos
+ return true
+ }
+
+ let escapedStr = state.src[pos]
+
+ if (ch1 >= 0xD800 && ch1 <= 0xDBFF && pos + 1 < max) {
+ const ch2 = state.src.charCodeAt(pos + 1)
+
+ if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) {
+ escapedStr += state.src[pos + 1]
+ pos++
+ }
+ }
+
+ const origStr = '\\' + escapedStr
+
+ if (!silent) {
+ const token = state.push('text_special', '', 0)
+
+ if (ch1 < 256 && ESCAPED[ch1] !== 0) {
+ token.content = escapedStr
+ } else {
+ token.content = origStr
+ }
+
+ token.markup = origStr
+ token.info = 'escape'
+ }
+
+ state.pos = pos + 1
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/fragments_join.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/fragments_join.mjs
new file mode 100644
index 0000000..e89ef9e
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/fragments_join.mjs
@@ -0,0 +1,38 @@
+// Clean up tokens after emphasis and strikethrough postprocessing:
+// merge adjacent text nodes into one and re-calculate all token levels
+//
+// This is necessary because initially emphasis delimiter markers (*, _, ~)
+// are treated as their own separate text tokens. Then emphasis rule either
+// leaves them as text (needed to merge with adjacent text) or turns them
+// into opening/closing tags (which messes up levels inside).
+//
+
+export default function fragments_join (state) {
+ let curr, last
+ let level = 0
+ const tokens = state.tokens
+ const max = state.tokens.length
+
+ for (curr = last = 0; curr < max; curr++) {
+ // re-calculate levels after emphasis/strikethrough turns some text nodes
+ // into opening/closing tags
+ if (tokens[curr].nesting < 0) level-- // closing tag
+ tokens[curr].level = level
+ if (tokens[curr].nesting > 0) level++ // opening tag
+
+ if (tokens[curr].type === 'text' &&
+ curr + 1 < max &&
+ tokens[curr + 1].type === 'text') {
+ // collapse two adjacent text nodes
+ tokens[curr + 1].content = tokens[curr].content + tokens[curr + 1].content
+ } else {
+ if (curr !== last) { tokens[last] = tokens[curr] }
+
+ last++
+ }
+ }
+
+ if (curr !== last) {
+ tokens.length = last
+ }
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/html_inline.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/html_inline.mjs
new file mode 100644
index 0000000..256efad
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/html_inline.mjs
@@ -0,0 +1,50 @@
+// Process html tags
+
+import { HTML_TAG_RE } from '../common/html_re.mjs'
+
+function isLinkOpen (str) {
+ return /^<a[>\s]/i.test(str)
+}
+function isLinkClose (str) {
+ return /^<\/a\s*>/i.test(str)
+}
+
+function isLetter (ch) {
+ /* eslint no-bitwise:0 */
+ const lc = ch | 0x20 // to lower case
+ return (lc >= 0x61/* a */) && (lc <= 0x7a/* z */)
+}
+
+export default function html_inline (state, silent) {
+ if (!state.md.options.html) { return false }
+
+ // Check start
+ const max = state.posMax
+ const pos = state.pos
+ if (state.src.charCodeAt(pos) !== 0x3C/* < */ ||
+ pos + 2 >= max) {
+ return false
+ }
+
+ // Quick fail on second char
+ const ch = state.src.charCodeAt(pos + 1)
+ if (ch !== 0x21/* ! */ &&
+ ch !== 0x3F/* ? */ &&
+ ch !== 0x2F/* / */ &&
+ !isLetter(ch)) {
+ return false
+ }
+
+ const match = state.src.slice(pos).match(HTML_TAG_RE)
+ if (!match) { return false }
+
+ if (!silent) {
+ const token = state.push('html_inline', '', 0)
+ token.content = match[0]
+
+ if (isLinkOpen(token.content)) state.linkLevel++
+ if (isLinkClose(token.content)) state.linkLevel--
+ }
+ state.pos += match[0].length
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/image.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/image.mjs
new file mode 100644
index 0000000..32cbb31
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/image.mjs
@@ -0,0 +1,138 @@
+// Process ![image](<src> "title")
+
+import { normalizeReference, isSpace } from '../common/utils.mjs'
+
+export default function image (state, silent) {
+ let code, content, label, pos, ref, res, title, start
+ let href = ''
+ const oldPos = state.pos
+ const max = state.posMax
+
+ if (state.src.charCodeAt(state.pos) !== 0x21/* ! */) { return false }
+ if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) { return false }
+
+ const labelStart = state.pos + 2
+ const labelEnd = state.md.helpers.parseLinkLabel(state, state.pos + 1, false)
+
+ // parser failed to find ']', so it's not a valid link
+ if (labelEnd < 0) { return false }
+
+ pos = labelEnd + 1
+ if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) {
+ //
+ // Inline link
+ //
+
+ // [link]( <href> "title" )
+ // ^^ skipping these spaces
+ pos++
+ for (; pos < max; pos++) {
+ code = state.src.charCodeAt(pos)
+ if (!isSpace(code) && code !== 0x0A) { break }
+ }
+ if (pos >= max) { return false }
+
+ // [link]( <href> "title" )
+ // ^^^^^^ parsing link destination
+ start = pos
+ res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax)
+ if (res.ok) {
+ href = state.md.normalizeLink(res.str)
+ if (state.md.validateLink(href)) {
+ pos = res.pos
+ } else {
+ href = ''
+ }
+ }
+
+ // [link]( <href> "title" )
+ // ^^ skipping these spaces
+ start = pos
+ for (; pos < max; pos++) {
+ code = state.src.charCodeAt(pos)
+ if (!isSpace(code) && code !== 0x0A) { break }
+ }
+
+ // [link]( <href> "title" )
+ // ^^^^^^^ parsing link title
+ res = state.md.helpers.parseLinkTitle(state.src, pos, state.posMax)
+ if (pos < max && start !== pos && res.ok) {
+ title = res.str
+ pos = res.pos
+
+ // [link]( <href> "title" )
+ // ^^ skipping these spaces
+ for (; pos < max; pos++) {
+ code = state.src.charCodeAt(pos)
+ if (!isSpace(code) && code !== 0x0A) { break }
+ }
+ } else {
+ title = ''
+ }
+
+ if (pos >= max || state.src.charCodeAt(pos) !== 0x29/* ) */) {
+ state.pos = oldPos
+ return false
+ }
+ pos++
+ } else {
+ //
+ // Link reference
+ //
+ if (typeof state.env.references === 'undefined') { return false }
+
+ if (pos < max && state.src.charCodeAt(pos) === 0x5B/* [ */) {
+ start = pos + 1
+ pos = state.md.helpers.parseLinkLabel(state, pos)
+ if (pos >= 0) {
+ label = state.src.slice(start, pos++)
+ } else {
+ pos = labelEnd + 1
+ }
+ } else {
+ pos = labelEnd + 1
+ }
+
+ // covers label === '' and label === undefined
+ // (collapsed reference link and shortcut reference link respectively)
+ if (!label) { label = state.src.slice(labelStart, labelEnd) }
+
+ ref = state.env.references[normalizeReference(label)]
+ if (!ref) {
+ state.pos = oldPos
+ return false
+ }
+ href = ref.href
+ title = ref.title
+ }
+
+ //
+ // We found the end of the link, and know for a fact it's a valid link;
+ // so all that's left to do is to call tokenizer.
+ //
+ if (!silent) {
+ content = state.src.slice(labelStart, labelEnd)
+
+ const tokens = []
+ state.md.inline.parse(
+ content,
+ state.md,
+ state.env,
+ tokens
+ )
+
+ const token = state.push('image', 'img', 0)
+ const attrs = [['src', href], ['alt', '']]
+ token.attrs = attrs
+ token.children = tokens
+ token.content = content
+
+ if (title) {
+ attrs.push(['title', title])
+ }
+ }
+
+ state.pos = pos
+ state.posMax = max
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/link.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/link.mjs
new file mode 100644
index 0000000..b145173
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/link.mjs
@@ -0,0 +1,139 @@
+// Process [link](<to> "stuff")
+
+import { normalizeReference, isSpace } from '../common/utils.mjs'
+
+export default function link (state, silent) {
+ let code, label, res, ref
+ let href = ''
+ let title = ''
+ let start = state.pos
+ let parseReference = true
+
+ if (state.src.charCodeAt(state.pos) !== 0x5B/* [ */) { return false }
+
+ const oldPos = state.pos
+ const max = state.posMax
+ const labelStart = state.pos + 1
+ const labelEnd = state.md.helpers.parseLinkLabel(state, state.pos, true)
+
+ // parser failed to find ']', so it's not a valid link
+ if (labelEnd < 0) { return false }
+
+ let pos = labelEnd + 1
+ if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) {
+ //
+ // Inline link
+ //
+
+ // might have found a valid shortcut link, disable reference parsing
+ parseReference = false
+
+ // [link]( <href> "title" )
+ // ^^ skipping these spaces
+ pos++
+ for (; pos < max; pos++) {
+ code = state.src.charCodeAt(pos)
+ if (!isSpace(code) && code !== 0x0A) { break }
+ }
+ if (pos >= max) { return false }
+
+ // [link]( <href> "title" )
+ // ^^^^^^ parsing link destination
+ start = pos
+ res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax)
+ if (res.ok) {
+ href = state.md.normalizeLink(res.str)
+ if (state.md.validateLink(href)) {
+ pos = res.pos
+ } else {
+ href = ''
+ }
+
+ // [link]( <href> "title" )
+ // ^^ skipping these spaces
+ start = pos
+ for (; pos < max; pos++) {
+ code = state.src.charCodeAt(pos)
+ if (!isSpace(code) && code !== 0x0A) { break }
+ }
+
+ // [link]( <href> "title" )
+ // ^^^^^^^ parsing link title
+ res = state.md.helpers.parseLinkTitle(state.src, pos, state.posMax)
+ if (pos < max && start !== pos && res.ok) {
+ title = res.str
+ pos = res.pos
+
+ // [link]( <href> "title" )
+ // ^^ skipping these spaces
+ for (; pos < max; pos++) {
+ code = state.src.charCodeAt(pos)
+ if (!isSpace(code) && code !== 0x0A) { break }
+ }
+ }
+ }
+
+ if (pos >= max || state.src.charCodeAt(pos) !== 0x29/* ) */) {
+ // parsing a valid shortcut link failed, fallback to reference
+ parseReference = true
+ }
+ pos++
+ }
+
+ if (parseReference) {
+ //
+ // Link reference
+ //
+ if (typeof state.env.references === 'undefined') { return false }
+
+ if (pos < max && state.src.charCodeAt(pos) === 0x5B/* [ */) {
+ start = pos + 1
+ pos = state.md.helpers.parseLinkLabel(state, pos)
+ if (pos >= 0) {
+ label = state.src.slice(start, pos++)
+ } else {
+ pos = labelEnd + 1
+ }
+ } else {
+ pos = labelEnd + 1
+ }
+
+ // covers label === '' and label === undefined
+ // (collapsed reference link and shortcut reference link respectively)
+ if (!label) { label = state.src.slice(labelStart, labelEnd) }
+
+ ref = state.env.references[normalizeReference(label)]
+ if (!ref) {
+ state.pos = oldPos
+ return false
+ }
+ href = ref.href
+ title = ref.title
+ }
+
+ //
+ // We found the end of the link, and know for a fact it's a valid link;
+ // so all that's left to do is to call tokenizer.
+ //
+ if (!silent) {
+ state.pos = labelStart
+ state.posMax = labelEnd
+
+ const token_o = state.push('link_open', 'a', 1)
+ const attrs = [['href', href]]
+ token_o.attrs = attrs
+ if (title) {
+ attrs.push(['title', title])
+ }
+
+ state.linkLevel++
+ state.md.inline.tokenize(state)
+ state.linkLevel--
+
+ state.push('link_close', 'a', -1)
+ }
+
+ state.pos = pos
+ state.posMax = max
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/linkify.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/linkify.mjs
new file mode 100644
index 0000000..fdc817a
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/linkify.mjs
@@ -0,0 +1,56 @@
+// Process links like https://example.org/
+
+// RFC3986: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+const SCHEME_RE = /(?:^|[^a-z0-9.+-])([a-z][a-z0-9.+-]*)$/i
+
+export default function linkify (state, silent) {
+ if (!state.md.options.linkify) return false
+ if (state.linkLevel > 0) return false
+
+ const pos = state.pos
+ const max = state.posMax
+
+ if (pos + 3 > max) return false
+ if (state.src.charCodeAt(pos) !== 0x3A/* : */) return false
+ if (state.src.charCodeAt(pos + 1) !== 0x2F/* / */) return false
+ if (state.src.charCodeAt(pos + 2) !== 0x2F/* / */) return false
+
+ const match = state.pending.match(SCHEME_RE)
+ if (!match) return false
+
+ const proto = match[1]
+
+ const link = state.md.linkify.matchAtStart(state.src.slice(pos - proto.length))
+ if (!link) return false
+
+ let url = link.url
+
+ // invalid link, but still detected by linkify somehow;
+ // need to check to prevent infinite loop below
+ if (url.length <= proto.length) return false
+
+ // disallow '*' at the end of the link (conflicts with emphasis)
+ url = url.replace(/\*+$/, '')
+
+ const fullUrl = state.md.normalizeLink(url)
+ if (!state.md.validateLink(fullUrl)) return false
+
+ if (!silent) {
+ state.pending = state.pending.slice(0, -proto.length)
+
+ const token_o = state.push('link_open', 'a', 1)
+ token_o.attrs = [['href', fullUrl]]
+ token_o.markup = 'linkify'
+ token_o.info = 'auto'
+
+ const token_t = state.push('text', '', 0)
+ token_t.content = state.md.normalizeLinkText(url)
+
+ const token_c = state.push('link_close', 'a', -1)
+ token_c.markup = 'linkify'
+ token_c.info = 'auto'
+ }
+
+ state.pos += url.length - proto.length
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/newline.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/newline.mjs
new file mode 100644
index 0000000..2c0e3ae
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/newline.mjs
@@ -0,0 +1,42 @@
+// Proceess '\n'
+
+import { isSpace } from '../common/utils.mjs'
+
+export default function newline (state, silent) {
+ let pos = state.pos
+
+ if (state.src.charCodeAt(pos) !== 0x0A/* \n */) { return false }
+
+ const pmax = state.pending.length - 1
+ const max = state.posMax
+
+ // ' \n' -> hardbreak
+ // Lookup in pending chars is bad practice! Don't copy to other rules!
+ // Pending string is stored in concat mode, indexed lookups will cause
+ // convertion to flat mode.
+ if (!silent) {
+ if (pmax >= 0 && state.pending.charCodeAt(pmax) === 0x20) {
+ if (pmax >= 1 && state.pending.charCodeAt(pmax - 1) === 0x20) {
+ // Find whitespaces tail of pending chars.
+ let ws = pmax - 1
+ while (ws >= 1 && state.pending.charCodeAt(ws - 1) === 0x20) ws--
+
+ state.pending = state.pending.slice(0, ws)
+ state.push('hardbreak', 'br', 0)
+ } else {
+ state.pending = state.pending.slice(0, -1)
+ state.push('softbreak', 'br', 0)
+ }
+ } else {
+ state.push('softbreak', 'br', 0)
+ }
+ }
+
+ pos++
+
+ // skip heading spaces for next line
+ while (pos < max && isSpace(state.src.charCodeAt(pos))) { pos++ }
+
+ state.pos = pos
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/state_inline.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/state_inline.mjs
new file mode 100644
index 0000000..80cb3c5
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/state_inline.mjs
@@ -0,0 +1,123 @@
+// Inline parser state
+
+import Token from '../token.mjs'
+import { isWhiteSpace, isPunctChar, isMdAsciiPunct } from '../common/utils.mjs'
+
+function StateInline (src, md, env, outTokens) {
+ this.src = src
+ this.env = env
+ this.md = md
+ this.tokens = outTokens
+ this.tokens_meta = Array(outTokens.length)
+
+ this.pos = 0
+ this.posMax = this.src.length
+ this.level = 0
+ this.pending = ''
+ this.pendingLevel = 0
+
+ // Stores { start: end } pairs. Useful for backtrack
+ // optimization of pairs parse (emphasis, strikes).
+ this.cache = {}
+
+ // List of emphasis-like delimiters for current tag
+ this.delimiters = []
+
+ // Stack of delimiter lists for upper level tags
+ this._prev_delimiters = []
+
+ // backtick length => last seen position
+ this.backticks = {}
+ this.backticksScanned = false
+
+ // Counter used to disable inline linkify-it execution
+ // inside <a> and markdown links
+ this.linkLevel = 0
+}
+
+// Flush pending text
+//
+StateInline.prototype.pushPending = function () {
+ const token = new Token('text', '', 0)
+ token.content = this.pending
+ token.level = this.pendingLevel
+ this.tokens.push(token)
+ this.pending = ''
+ return token
+}
+
+// Push new token to "stream".
+// If pending text exists - flush it as text token
+//
+StateInline.prototype.push = function (type, tag, nesting) {
+ if (this.pending) {
+ this.pushPending()
+ }
+
+ const token = new Token(type, tag, nesting)
+ let token_meta = null
+
+ if (nesting < 0) {
+ // closing tag
+ this.level--
+ this.delimiters = this._prev_delimiters.pop()
+ }
+
+ token.level = this.level
+
+ if (nesting > 0) {
+ // opening tag
+ this.level++
+ this._prev_delimiters.push(this.delimiters)
+ this.delimiters = []
+ token_meta = { delimiters: this.delimiters }
+ }
+
+ this.pendingLevel = this.level
+ this.tokens.push(token)
+ this.tokens_meta.push(token_meta)
+ return token
+}
+
+// Scan a sequence of emphasis-like markers, and determine whether
+// it can start an emphasis sequence or end an emphasis sequence.
+//
+// - start - position to scan from (it should point at a valid marker);
+// - canSplitWord - determine if these markers can be found inside a word
+//
+StateInline.prototype.scanDelims = function (start, canSplitWord) {
+ const max = this.posMax
+ const marker = this.src.charCodeAt(start)
+
+ // treat beginning of the line as a whitespace
+ const lastChar = start > 0 ? this.src.charCodeAt(start - 1) : 0x20
+
+ let pos = start
+ while (pos < max && this.src.charCodeAt(pos) === marker) { pos++ }
+
+ const count = pos - start
+
+ // treat end of the line as a whitespace
+ const nextChar = pos < max ? this.src.charCodeAt(pos) : 0x20
+
+ const isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar))
+ const isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar))
+
+ const isLastWhiteSpace = isWhiteSpace(lastChar)
+ const isNextWhiteSpace = isWhiteSpace(nextChar)
+
+ const left_flanking =
+ !isNextWhiteSpace && (!isNextPunctChar || isLastWhiteSpace || isLastPunctChar)
+ const right_flanking =
+ !isLastWhiteSpace && (!isLastPunctChar || isNextWhiteSpace || isNextPunctChar)
+
+ const can_open = left_flanking && (canSplitWord || !right_flanking || isLastPunctChar)
+ const can_close = right_flanking && (canSplitWord || !left_flanking || isNextPunctChar)
+
+ return { can_open, can_close, length: count }
+}
+
+// re-export Token class to use in block rules
+StateInline.prototype.Token = Token
+
+export default StateInline
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/strikethrough.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/strikethrough.mjs
new file mode 100644
index 0000000..45f3c1b
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/strikethrough.mjs
@@ -0,0 +1,127 @@
+// ~~strike through~~
+//
+
+// Insert each marker as a separate text token, and add it to delimiter list
+//
+function strikethrough_tokenize (state, silent) {
+ const start = state.pos
+ const marker = state.src.charCodeAt(start)
+
+ if (silent) { return false }
+
+ if (marker !== 0x7E/* ~ */) { return false }
+
+ const scanned = state.scanDelims(state.pos, true)
+ let len = scanned.length
+ const ch = String.fromCharCode(marker)
+
+ if (len < 2) { return false }
+
+ let token
+
+ if (len % 2) {
+ token = state.push('text', '', 0)
+ token.content = ch
+ len--
+ }
+
+ for (let i = 0; i < len; i += 2) {
+ token = state.push('text', '', 0)
+ token.content = ch + ch
+
+ state.delimiters.push({
+ marker,
+ length: 0, // disable "rule of 3" length checks meant for emphasis
+ token: state.tokens.length - 1,
+ end: -1,
+ open: scanned.can_open,
+ close: scanned.can_close
+ })
+ }
+
+ state.pos += scanned.length
+
+ return true
+}
+
+function postProcess (state, delimiters) {
+ let token
+ const loneMarkers = []
+ const max = delimiters.length
+
+ for (let i = 0; i < max; i++) {
+ const startDelim = delimiters[i]
+
+ if (startDelim.marker !== 0x7E/* ~ */) {
+ continue
+ }
+
+ if (startDelim.end === -1) {
+ continue
+ }
+
+ const endDelim = delimiters[startDelim.end]
+
+ token = state.tokens[startDelim.token]
+ token.type = 's_open'
+ token.tag = 's'
+ token.nesting = 1
+ token.markup = '~~'
+ token.content = ''
+
+ token = state.tokens[endDelim.token]
+ token.type = 's_close'
+ token.tag = 's'
+ token.nesting = -1
+ token.markup = '~~'
+ token.content = ''
+
+ if (state.tokens[endDelim.token - 1].type === 'text' &&
+ state.tokens[endDelim.token - 1].content === '~') {
+ loneMarkers.push(endDelim.token - 1)
+ }
+ }
+
+ // If a marker sequence has an odd number of characters, it's splitted
+ // like this: `~~~~~` -> `~` + `~~` + `~~`, leaving one marker at the
+ // start of the sequence.
+ //
+ // So, we have to move all those markers after subsequent s_close tags.
+ //
+ while (loneMarkers.length) {
+ const i = loneMarkers.pop()
+ let j = i + 1
+
+ while (j < state.tokens.length && state.tokens[j].type === 's_close') {
+ j++
+ }
+
+ j--
+
+ if (i !== j) {
+ token = state.tokens[j]
+ state.tokens[j] = state.tokens[i]
+ state.tokens[i] = token
+ }
+ }
+}
+
+// Walk through delimiter list and replace text tokens with tags
+//
+function strikethrough_postProcess (state) {
+ const tokens_meta = state.tokens_meta
+ const max = state.tokens_meta.length
+
+ postProcess(state, state.delimiters)
+
+ for (let curr = 0; curr < max; curr++) {
+ if (tokens_meta[curr] && tokens_meta[curr].delimiters) {
+ postProcess(state, tokens_meta[curr].delimiters)
+ }
+ }
+}
+
+export default {
+ tokenize: strikethrough_tokenize,
+ postProcess: strikethrough_postProcess
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_inline/text.mjs b/packages/markdown-it-14.1.0/lib/rules_inline/text.mjs
new file mode 100644
index 0000000..9be4227
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_inline/text.mjs
@@ -0,0 +1,86 @@
+// Skip text characters for text token, place those to pending buffer
+// and increment current pos
+
+// Rule to skip pure text
+// '{}$%@~+=:' reserved for extentions
+
+// !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, _, `, {, |, }, or ~
+
+// !!!! Don't confuse with "Markdown ASCII Punctuation" chars
+// http://spec.commonmark.org/0.15/#ascii-punctuation-character
+function isTerminatorChar (ch) {
+ switch (ch) {
+ case 0x0A/* \n */:
+ case 0x21/* ! */:
+ case 0x23/* # */:
+ case 0x24/* $ */:
+ case 0x25/* % */:
+ case 0x26/* & */:
+ case 0x2A/* * */:
+ case 0x2B/* + */:
+ case 0x2D/* - */:
+ case 0x3A/* : */:
+ case 0x3C/* < */:
+ case 0x3D/* = */:
+ case 0x3E/* > */:
+ case 0x40/* @ */:
+ case 0x5B/* [ */:
+ case 0x5C/* \ */:
+ case 0x5D/* ] */:
+ case 0x5E/* ^ */:
+ case 0x5F/* _ */:
+ case 0x60/* ` */:
+ case 0x7B/* { */:
+ case 0x7D/* } */:
+ case 0x7E/* ~ */:
+ return true
+ default:
+ return false
+ }
+}
+
+export default function text (state, silent) {
+ let pos = state.pos
+
+ while (pos < state.posMax && !isTerminatorChar(state.src.charCodeAt(pos))) {
+ pos++
+ }
+
+ if (pos === state.pos) { return false }
+
+ if (!silent) { state.pending += state.src.slice(state.pos, pos) }
+
+ state.pos = pos
+
+ return true
+}
+
+// Alternative implementation, for memory.
+//
+// It costs 10% of performance, but allows extend terminators list, if place it
+// to `ParserInline` property. Probably, will switch to it sometime, such
+// flexibility required.
+
+/*
+var TERMINATOR_RE = /[\n!#$%&*+\-:<=>@[\\\]^_`{}~]/;
+
+module.exports = function text(state, silent) {
+ var pos = state.pos,
+ idx = state.src.slice(pos).search(TERMINATOR_RE);
+
+ // first char is terminator -> empty text
+ if (idx === 0) { return false; }
+
+ // no terminator -> text till end of string
+ if (idx < 0) {
+ if (!silent) { state.pending += state.src.slice(pos); }
+ state.pos = state.src.length;
+ return true;
+ }
+
+ if (!silent) { state.pending += state.src.slice(pos, pos + idx); }
+
+ state.pos += idx;
+
+ return true;
+}; */