@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.
- package/README.md +440 -112
- package/THIRD_PARTY_NOTICES.md +56 -0
- package/index.js +33 -479
- package/package.json +29 -3
- package/src/custom-highlight/option-validator.js +152 -0
- package/src/custom-highlight/payload-utils.js +43 -0
- package/src/custom-highlight/runtime-utils.js +83 -0
- package/src/custom-highlight/shiki-keyword-rules.js +407 -0
- package/src/custom-highlight/shiki-keyword.js +417 -0
- package/src/entry/custom-highlight-runtime.js +11 -0
- package/src/entry/custom-highlight.js +25 -0
- package/src/entry/markup-highlight.js +40 -0
- package/src/fence/render-api-constants.js +34 -0
- package/src/fence/render-api-provider.js +810 -0
- package/src/fence/render-api-renderer.js +136 -0
- package/src/fence/render-api-runtime.js +712 -0
- package/src/fence/render-api.js +117 -0
- package/src/fence/render-markup.js +161 -0
- package/src/fence/render-shared.js +428 -0
- package/src/utils/attr-utils.js +120 -0
- package/src/utils/pre-code-wrapper-parser.js +114 -0
|
@@ -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
|
+
}
|