@peaceroad/markdown-it-renderer-fence 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,120 @@
1
+ const attrReg = /^(?:([.#])(.+)|(.+?)(?:=(["'])?(.*?)\1)?)$/
2
+ const interAttrsSpaceReg = / +/
3
+ const htmlAttrReg = /([^\s=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g
4
+
5
+ const createAttrOrderIndexGetter = (order) => {
6
+ const exact = new Map()
7
+ const prefixes = []
8
+ for (let i = 0; i < order.length; i++) {
9
+ const key = order[i]
10
+ if (key.endsWith('*')) {
11
+ prefixes.push([key.slice(0, -1), i])
12
+ } else if (!exact.has(key)) {
13
+ exact.set(key, i)
14
+ }
15
+ }
16
+ const fallback = order.length
17
+ return (name) => {
18
+ const exactIndex = exact.get(name)
19
+ if (exactIndex !== undefined) return exactIndex
20
+ for (let i = 0; i < prefixes.length; i++) {
21
+ if (name.startsWith(prefixes[i][0])) return prefixes[i][1]
22
+ }
23
+ return fallback
24
+ }
25
+ }
26
+
27
+ const appendStyleValue = (style, addition) => {
28
+ if (!addition) return style
29
+ let next = addition.trim()
30
+ if (!next) return style
31
+ if (!next.endsWith(';')) next += ';'
32
+ if (!style) return next
33
+ const base = style.trimEnd()
34
+ const separator = base.endsWith(';') ? ' ' : '; '
35
+ return base + separator + next
36
+ }
37
+
38
+ const parseHtmlAttrs = (attrText) => {
39
+ const attrs = []
40
+ if (!attrText) return attrs
41
+ htmlAttrReg.lastIndex = 0
42
+ let match
43
+ while ((match = htmlAttrReg.exec(attrText)) !== null) {
44
+ const name = match[1]
45
+ const val = match[2] ?? match[3] ?? match[4] ?? ''
46
+ attrs.push([name, val])
47
+ }
48
+ return attrs
49
+ }
50
+
51
+ const mergeAttrSets = (target, source) => {
52
+ if (!source || source.length === 0) return
53
+ const index = new Map()
54
+ for (let i = 0; i < target.length; i++) index.set(target[i][0], i)
55
+ for (const [name, val] of source) {
56
+ const idx = index.get(name)
57
+ if (name === 'class') {
58
+ if (idx === undefined) {
59
+ target.push([name, val])
60
+ index.set(name, target.length - 1)
61
+ } else if (val) {
62
+ const existing = target[idx][1]
63
+ target[idx][1] = existing ? existing + ' ' + val : val
64
+ }
65
+ continue
66
+ }
67
+ if (name === 'style') {
68
+ if (idx === undefined) {
69
+ target.push([name, val])
70
+ index.set(name, target.length - 1)
71
+ } else {
72
+ target[idx][1] = appendStyleValue(target[idx][1], val)
73
+ }
74
+ continue
75
+ }
76
+ if (idx === undefined) {
77
+ target.push([name, val])
78
+ index.set(name, target.length - 1)
79
+ }
80
+ }
81
+ }
82
+
83
+ const getInfoAttr = (infoAttr) => {
84
+ const attrSets = infoAttr.trim().split(interAttrsSpaceReg)
85
+ const out = []
86
+ for (let attrSet of attrSets) {
87
+ const str = attrReg.exec(attrSet)
88
+ if (!str) continue
89
+ if (str[1] === '.') str[1] = 'class'
90
+ if (str[1] === '#') str[1] = 'id'
91
+ if (str[3]) {
92
+ str[1] = str[3]
93
+ str[2] = str[5] ? str[5].replace(/^['"]/, '').replace(/['"]$/, '') : ''
94
+ }
95
+ out.push([str[1], str[2]])
96
+ }
97
+ return out
98
+ }
99
+
100
+ const getLangFromClassAttr = (classText, langPrefix) => {
101
+ const value = String(classText || '')
102
+ if (!value || !langPrefix || value.indexOf(langPrefix) === -1) return ''
103
+ const list = value.trim().split(/\s+/)
104
+ for (let i = 0; i < list.length; i++) {
105
+ const name = list[i]
106
+ if (name.startsWith(langPrefix) && name.length > langPrefix.length) {
107
+ return name.slice(langPrefix.length)
108
+ }
109
+ }
110
+ return ''
111
+ }
112
+
113
+ export {
114
+ appendStyleValue,
115
+ createAttrOrderIndexGetter,
116
+ getInfoAttr,
117
+ getLangFromClassAttr,
118
+ mergeAttrSets,
119
+ parseHtmlAttrs,
120
+ }
@@ -0,0 +1,114 @@
1
+ const isSpaceCode = (code) => {
2
+ return code === 9 || code === 10 || code === 12 || code === 13 || code === 32
3
+ }
4
+
5
+ const isTagNameCharCode = (code) => {
6
+ return (
7
+ (code >= 48 && code <= 57) || // 0-9
8
+ (code >= 65 && code <= 90) || // A-Z
9
+ (code >= 97 && code <= 122) || // a-z
10
+ code === 45 || // -
11
+ code === 58 // :
12
+ )
13
+ }
14
+
15
+ const skipSpace = (source, start) => {
16
+ let i = start
17
+ const len = source.length
18
+ while (i < len && isSpaceCode(source.charCodeAt(i))) i++
19
+ return i
20
+ }
21
+
22
+ const parseOpenTag = (source, start) => {
23
+ if (source.charCodeAt(start) !== 60) return null // <
24
+ let i = start + 1
25
+ if (i >= source.length || source.charCodeAt(i) === 47) return null // /
26
+
27
+ const nameStart = i
28
+ while (i < source.length && isTagNameCharCode(source.charCodeAt(i))) i++
29
+ if (i === nameStart) return null
30
+ const name = source.slice(nameStart, i).toLowerCase()
31
+
32
+ const attrStart = i
33
+ let quote = 0
34
+ while (i < source.length) {
35
+ const ch = source.charCodeAt(i)
36
+ if (quote !== 0) {
37
+ if (ch === quote) quote = 0
38
+ i++
39
+ continue
40
+ }
41
+ if (ch === 34 || ch === 39) { // " or '
42
+ quote = ch
43
+ i++
44
+ continue
45
+ }
46
+ if (ch === 62) break // >
47
+ i++
48
+ }
49
+ if (i >= source.length) return null
50
+
51
+ let tail = i - 1
52
+ while (tail >= attrStart && isSpaceCode(source.charCodeAt(tail))) tail--
53
+ const selfClosing = tail >= attrStart && source.charCodeAt(tail) === 47 // /
54
+ const attrsText = source.slice(attrStart, i).trim()
55
+ return { name, attrsText, selfClosing, start, end: i + 1 }
56
+ }
57
+
58
+ const parseCloseTag = (source, start) => {
59
+ if (source.charCodeAt(start) !== 60) return null // <
60
+ let i = start + 1
61
+ if (i >= source.length || source.charCodeAt(i) !== 47) return null // /
62
+ i++
63
+ i = skipSpace(source, i)
64
+ const nameStart = i
65
+ while (i < source.length && isTagNameCharCode(source.charCodeAt(i))) i++
66
+ if (i === nameStart) return null
67
+ const name = source.slice(nameStart, i).toLowerCase()
68
+ i = skipSpace(source, i)
69
+ if (i >= source.length || source.charCodeAt(i) !== 62) return null // >
70
+ return { name, start, end: i + 1 }
71
+ }
72
+
73
+ const findCloseTag = (source, start, targetName) => {
74
+ const len = source.length
75
+ for (let i = start; i < len; i++) {
76
+ if (source.charCodeAt(i) !== 60) continue
77
+ const close = parseCloseTag(source, i)
78
+ if (!close) continue
79
+ if (close.name === targetName) return close
80
+ }
81
+ return null
82
+ }
83
+
84
+ const parsePreCodeWrapper = (html) => {
85
+ const source = String(html || '')
86
+ if (!source) return null
87
+ let i = skipSpace(source, 0)
88
+ const preOpen = parseOpenTag(source, i)
89
+ if (!preOpen || preOpen.selfClosing || preOpen.name !== 'pre') return null
90
+ i = skipSpace(source, preOpen.end)
91
+ const codeOpen = parseOpenTag(source, i)
92
+ if (!codeOpen || codeOpen.selfClosing || codeOpen.name !== 'code') return null
93
+
94
+ const codeClose = findCloseTag(source, codeOpen.end, 'code')
95
+ if (!codeClose) return null
96
+ const content = source.slice(codeOpen.end, codeClose.start)
97
+
98
+ i = skipSpace(source, codeClose.end)
99
+ const preClose = parseCloseTag(source, i)
100
+ if (!preClose || preClose.name !== 'pre') return null
101
+
102
+ i = skipSpace(source, preClose.end)
103
+ if (i !== source.length) return null
104
+
105
+ return {
106
+ preAttrsText: preOpen.attrsText,
107
+ codeAttrsText: codeOpen.attrsText,
108
+ content,
109
+ }
110
+ }
111
+
112
+ export {
113
+ parsePreCodeWrapper,
114
+ }