markdown-magic 3.0.0 → 3.0.1
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 +43 -29
- package/lib/block-parser-js.test.js +148 -156
- package/lib/block-parser.js +255 -262
- package/lib/block-parser.test.js +43 -6
- package/lib/cli.js +30 -19
- package/lib/cli.test.js +73 -73
- package/lib/globals.d.ts +66 -0
- package/lib/index.js +43 -9
- package/lib/process-contents.js +80 -39
- package/lib/process-file.js +4 -1
- package/lib/transforms/code.js +4 -10
- package/lib/transforms/file.js +7 -10
- package/lib/transforms/index.js +0 -0
- package/lib/transforms/remote.js +2 -3
- package/lib/transforms/sectionToc.js +18 -0
- package/lib/transforms/toc.js +10 -335
- package/lib/types.js +11 -0
- package/lib/utils/fs.js +21 -19
- package/lib/utils/fs.test.js +4 -5
- package/lib/utils/logs.js +7 -2
- package/lib/utils/md/filters.js +5 -5
- package/lib/utils/md/find-code-blocks.js +16 -8
- package/lib/utils/md/find-frontmatter.js +11 -13
- package/lib/utils/md/find-frontmatter.test.js +2 -2
- package/lib/utils/md/find-html-tags.js +1 -1
- package/lib/utils/md/find-images-md.js +27 -0
- package/lib/utils/md/find-images.js +39 -34
- package/lib/utils/md/find-links.js +72 -54
- package/lib/utils/md/find-unmatched-html-tags.js +1 -2
- package/lib/utils/md/fixtures/file-with-links.md +10 -0
- package/lib/utils/md/md.test.js +72 -4
- package/lib/utils/md/parse.js +91 -67
- package/lib/utils/regex-timeout.js +2 -1
- package/lib/utils/regex.js +3 -2
- package/lib/utils/remoteRequest.js +1 -0
- package/lib/utils/syntax.js +3 -0
- package/lib/utils/text.js +71 -3
- package/lib/utils/text.test.js +3 -9
- package/lib/utils/toc.js +315 -0
- package/package.json +7 -3
- package/lib/options-parser.js +0 -498
- package/lib/options-parser.test.js +0 -1237
- package/lib/utils/html-to-json/compat.js +0 -42
- package/lib/utils/html-to-json/format.js +0 -64
- package/lib/utils/html-to-json/index.js +0 -37
- package/lib/utils/html-to-json/lexer.js +0 -345
- package/lib/utils/html-to-json/parser.js +0 -146
- package/lib/utils/html-to-json/stringify.js +0 -37
- package/lib/utils/html-to-json/tags.js +0 -171
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
function startsWith (str, searchString, position) {
|
|
3
|
-
return str.substr(position || 0, searchString.length) === searchString
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
function endsWith (str, searchString, position) {
|
|
7
|
-
const index = (position || str.length) - searchString.length
|
|
8
|
-
const lastIndex = str.lastIndexOf(searchString, index)
|
|
9
|
-
return lastIndex !== -1 && lastIndex === index
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function stringIncludes (str, searchString, position) {
|
|
13
|
-
return str.indexOf(searchString, position || 0) !== -1
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function isRealNaN (x) {
|
|
17
|
-
return typeof x === 'number' && isNaN(x)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function arrayIncludes (array, searchElement, position) {
|
|
21
|
-
const len = array.length
|
|
22
|
-
if (len === 0) return false
|
|
23
|
-
|
|
24
|
-
const lookupIndex = position | 0
|
|
25
|
-
const isNaNElement = isRealNaN(searchElement)
|
|
26
|
-
let searchIndex = lookupIndex >= 0 ? lookupIndex : len + lookupIndex
|
|
27
|
-
while (searchIndex < len) {
|
|
28
|
-
const element = array[searchIndex++]
|
|
29
|
-
if (element === searchElement) return true
|
|
30
|
-
if (isNaNElement && isRealNaN(element)) return true
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return false
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
module.exports = {
|
|
37
|
-
startsWith,
|
|
38
|
-
endsWith,
|
|
39
|
-
stringIncludes,
|
|
40
|
-
isRealNaN,
|
|
41
|
-
arrayIncludes
|
|
42
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
function splitHead(str, sep) {
|
|
3
|
-
const idx = str.indexOf(sep)
|
|
4
|
-
if (idx === -1) return [str]
|
|
5
|
-
return [str.slice(0, idx), str.slice(idx + sep.length)]
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function unquote(str) {
|
|
9
|
-
const car = str.charAt(0)
|
|
10
|
-
const end = str.length - 1
|
|
11
|
-
const isQuoteStart = car === '"' || car === "'"
|
|
12
|
-
if (isQuoteStart && car === str.charAt(end)) {
|
|
13
|
-
return str.slice(1, end)
|
|
14
|
-
}
|
|
15
|
-
return str
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function format(nodes, options) {
|
|
19
|
-
return nodes.map(node => {
|
|
20
|
-
const type = node.type
|
|
21
|
-
let outputNode = { type, content: node.content }
|
|
22
|
-
if (type === 'element') {
|
|
23
|
-
outputNode = {
|
|
24
|
-
// TODO maybe harden with https://github.com/riot/dom-nodes
|
|
25
|
-
type: (/^[A-Z]/.test(node.tagName)) ? 'component' : type,
|
|
26
|
-
// isReactComponent: /^[A-Z]/.test(node.tagName),
|
|
27
|
-
tagName: node.tagName,
|
|
28
|
-
props: node.props,
|
|
29
|
-
propsRaw: node.propsRaw,
|
|
30
|
-
children: format(node.children, options)
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
if (options.includePositions) {
|
|
34
|
-
if (options.offset) {
|
|
35
|
-
const { lineOffset, charOffset } = options.offset
|
|
36
|
-
node.position.start.line = node.position.start.line + lineOffset
|
|
37
|
-
node.position.start.index = node.position.start.index + charOffset
|
|
38
|
-
node.position.end.line = node.position.end.line + lineOffset
|
|
39
|
-
node.position.end.index = node.position.end.index + charOffset
|
|
40
|
-
}
|
|
41
|
-
outputNode.position = node.position
|
|
42
|
-
}
|
|
43
|
-
return outputNode
|
|
44
|
-
})
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Old
|
|
48
|
-
function formatAttributes (attributes) {
|
|
49
|
-
return attributes.map(attribute => {
|
|
50
|
-
const parts = splitHead(attribute.trim(), '=')
|
|
51
|
-
const key = parts[0]
|
|
52
|
-
const value = typeof parts[1] === 'string'
|
|
53
|
-
? unquote(parts[1])
|
|
54
|
-
: null
|
|
55
|
-
return {key, value}
|
|
56
|
-
})
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
module.exports = {
|
|
60
|
-
splitHead,
|
|
61
|
-
unquote,
|
|
62
|
-
format,
|
|
63
|
-
formatAttributes
|
|
64
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// Fork of https://github.com/andrejewski/himalaya with tweaks
|
|
2
|
-
const { lexer } = require('./lexer')
|
|
3
|
-
const { parser } = require('./parser')
|
|
4
|
-
const { format } = require('./format')
|
|
5
|
-
const { toHTML } = require('./stringify')
|
|
6
|
-
const {
|
|
7
|
-
voidTags,
|
|
8
|
-
closingTags,
|
|
9
|
-
childlessTags,
|
|
10
|
-
closingTagAncestorBreakers
|
|
11
|
-
} = require('./tags')
|
|
12
|
-
|
|
13
|
-
const parseDefaults = {
|
|
14
|
-
voidTags,
|
|
15
|
-
closingTags,
|
|
16
|
-
childlessTags,
|
|
17
|
-
closingTagAncestorBreakers,
|
|
18
|
-
includePositions: false
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function parse(str, opts = {}) {
|
|
22
|
-
const options = Object.assign(parseDefaults, opts)
|
|
23
|
-
const tokens = lexer(str, options)
|
|
24
|
-
const nodes = parser(tokens, options)
|
|
25
|
-
return format(nodes, options)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function stringify(ast, opts = {}) {
|
|
29
|
-
const options = Object.assign(parseDefaults, opts)
|
|
30
|
-
return toHTML(ast, options)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
module.exports = {
|
|
34
|
-
parseDefaults,
|
|
35
|
-
parse,
|
|
36
|
-
stringify
|
|
37
|
-
}
|
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
const {
|
|
2
|
-
startsWith,
|
|
3
|
-
endsWith,
|
|
4
|
-
stringIncludes,
|
|
5
|
-
arrayIncludes
|
|
6
|
-
} = require('./compat')
|
|
7
|
-
|
|
8
|
-
function feedPosition(position, str, len) {
|
|
9
|
-
const start = position.index
|
|
10
|
-
const end = position.index = start + len
|
|
11
|
-
for (let i = start; i < end; i++) {
|
|
12
|
-
const char = str.charAt(i)
|
|
13
|
-
if (char === '\n') {
|
|
14
|
-
position.line++
|
|
15
|
-
position.column = 0
|
|
16
|
-
} else {
|
|
17
|
-
position.column++
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function jumpPosition (position, str, end) {
|
|
23
|
-
const len = end - position.index
|
|
24
|
-
return feedPosition(position, str, len)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function makeInitialPosition () {
|
|
28
|
-
return {
|
|
29
|
-
index: 0,
|
|
30
|
-
column: 0,
|
|
31
|
-
line: 1
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function copyPositionStart(position) {
|
|
36
|
-
return {
|
|
37
|
-
index: position.index,
|
|
38
|
-
line: position.line,
|
|
39
|
-
column: position.column + 1
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function copyPositionEnd(position) {
|
|
44
|
-
return {
|
|
45
|
-
index: position.index + 1,
|
|
46
|
-
line: position.line,
|
|
47
|
-
column: position.column + 1
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Old offset was index/column was 1 off in some cases
|
|
52
|
-
function copyPosition(position) {
|
|
53
|
-
return {
|
|
54
|
-
index: position.index,
|
|
55
|
-
line: position.line,
|
|
56
|
-
column: position.column
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function lexer (str, options) {
|
|
61
|
-
const state = {
|
|
62
|
-
str,
|
|
63
|
-
options,
|
|
64
|
-
position: makeInitialPosition(),
|
|
65
|
-
tokens: []
|
|
66
|
-
}
|
|
67
|
-
lex(state)
|
|
68
|
-
return state.tokens
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function lex (state) {
|
|
72
|
-
const {str, options: {childlessTags}} = state
|
|
73
|
-
const len = str?.length || 0
|
|
74
|
-
while (state.position.index < len) {
|
|
75
|
-
const start = state.position.index
|
|
76
|
-
lexText(state)
|
|
77
|
-
if (state.position.index === start) {
|
|
78
|
-
const isComment = startsWith(str, '!--', start + 1)
|
|
79
|
-
if (isComment) {
|
|
80
|
-
lexComment(state)
|
|
81
|
-
} else {
|
|
82
|
-
const tagName = lexTag(state)
|
|
83
|
-
const safeTag = tagName.toLowerCase()
|
|
84
|
-
if (arrayIncludes(childlessTags, safeTag)) {
|
|
85
|
-
lexSkipTag(tagName, state)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const alphanumeric = /[A-Za-z0-9]/
|
|
93
|
-
function findTextEnd (str, index) {
|
|
94
|
-
while (true) {
|
|
95
|
-
const textEnd = str.indexOf('<', index)
|
|
96
|
-
if (textEnd === -1) {
|
|
97
|
-
return textEnd
|
|
98
|
-
}
|
|
99
|
-
const char = str.charAt(textEnd + 1)
|
|
100
|
-
if (char === '/' || char === '!' || alphanumeric.test(char)) {
|
|
101
|
-
return textEnd
|
|
102
|
-
}
|
|
103
|
-
index = textEnd + 1
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function lexText (state) {
|
|
108
|
-
const type = 'text'
|
|
109
|
-
const {str, position} = state
|
|
110
|
-
let textEnd = findTextEnd(str, position.index)
|
|
111
|
-
if (textEnd === position.index) return
|
|
112
|
-
if (textEnd === -1) {
|
|
113
|
-
textEnd = str.length
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const start = copyPositionStart(position)
|
|
117
|
-
const content = str.slice(position.index, textEnd)
|
|
118
|
-
jumpPosition(position, str, textEnd)
|
|
119
|
-
const end = copyPositionEnd(position)
|
|
120
|
-
state.tokens.push({type, content, position: {start, end}})
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function lexComment (state) {
|
|
124
|
-
const {str, position} = state
|
|
125
|
-
const start = copyPositionStart(position)
|
|
126
|
-
feedPosition(position, str, 4) // "<!--".length
|
|
127
|
-
let contentEnd = str.indexOf('-->', position.index)
|
|
128
|
-
let commentEnd = contentEnd + 3 // "-->".length
|
|
129
|
-
if (contentEnd === -1) {
|
|
130
|
-
contentEnd = commentEnd = str.length
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const content = str.slice(position.index, contentEnd)
|
|
134
|
-
jumpPosition(position, str, commentEnd)
|
|
135
|
-
state.tokens.push({
|
|
136
|
-
type: 'comment',
|
|
137
|
-
content,
|
|
138
|
-
position: {
|
|
139
|
-
start,
|
|
140
|
-
end: copyPositionEnd(position)
|
|
141
|
-
}
|
|
142
|
-
})
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function lexTag (state) {
|
|
146
|
-
const {str, position} = state
|
|
147
|
-
{
|
|
148
|
-
const secondChar = str.charAt(position.index + 1)
|
|
149
|
-
const close = secondChar === '/'
|
|
150
|
-
const start = copyPositionStart(position, 0)
|
|
151
|
-
feedPosition(position, str, close ? 2 : 1)
|
|
152
|
-
state.tokens.push({type: 'tag-start', close, position: {start}})
|
|
153
|
-
}
|
|
154
|
-
const tagName = lexTagName(state)
|
|
155
|
-
lexTagAttributes(state)
|
|
156
|
-
{
|
|
157
|
-
const firstChar = str.charAt(position.index)
|
|
158
|
-
const close = firstChar === '/'
|
|
159
|
-
feedPosition(position, str, close ? 2 : 1)
|
|
160
|
-
const end = copyPositionEnd(position)
|
|
161
|
-
state.tokens.push({type: 'tag-end', close, position: {end}})
|
|
162
|
-
}
|
|
163
|
-
return tagName
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#special-white-space
|
|
167
|
-
const whitespace = /\s/
|
|
168
|
-
function isWhitespaceChar (char) {
|
|
169
|
-
return whitespace.test(char)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function lexTagName (state) {
|
|
173
|
-
const {str, position} = state
|
|
174
|
-
const len = str.length
|
|
175
|
-
let start = position.index
|
|
176
|
-
while (start < len) {
|
|
177
|
-
const char = str.charAt(start)
|
|
178
|
-
const isTagChar = !(isWhitespaceChar(char) || char === '/' || char === '>')
|
|
179
|
-
if (isTagChar) break
|
|
180
|
-
start++
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
let end = start + 1
|
|
184
|
-
while (end < len) {
|
|
185
|
-
const char = str.charAt(end)
|
|
186
|
-
const isTagChar = !(isWhitespaceChar(char) || char === '/' || char === '>')
|
|
187
|
-
if (!isTagChar) break
|
|
188
|
-
end++
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
jumpPosition(position, str, end)
|
|
192
|
-
const tagName = str.slice(start, end)
|
|
193
|
-
state.tokens.push({
|
|
194
|
-
type: 'tag',
|
|
195
|
-
content: tagName
|
|
196
|
-
})
|
|
197
|
-
return tagName
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function lexTagAttributes (state) {
|
|
201
|
-
const {str, position, tokens} = state
|
|
202
|
-
let cursor = position.index
|
|
203
|
-
let quote = null // null, single-, or double-quote
|
|
204
|
-
let wordBegin = cursor // index of word start
|
|
205
|
-
const words = [] // "key", "key=value", "key='value'", etc
|
|
206
|
-
const len = str.length
|
|
207
|
-
while (cursor < len) {
|
|
208
|
-
const char = str.charAt(cursor)
|
|
209
|
-
if (quote) {
|
|
210
|
-
const isQuoteEnd = char === quote
|
|
211
|
-
if (isQuoteEnd) {
|
|
212
|
-
quote = null
|
|
213
|
-
}
|
|
214
|
-
cursor++
|
|
215
|
-
continue
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const isTagEnd = char === '/' || char === '>'
|
|
219
|
-
if (isTagEnd) {
|
|
220
|
-
if (cursor !== wordBegin) {
|
|
221
|
-
words.push(str.slice(wordBegin, cursor))
|
|
222
|
-
}
|
|
223
|
-
break
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const isWordEnd = isWhitespaceChar(char)
|
|
227
|
-
if (isWordEnd) {
|
|
228
|
-
if (cursor !== wordBegin) {
|
|
229
|
-
words.push(str.slice(wordBegin, cursor))
|
|
230
|
-
}
|
|
231
|
-
wordBegin = cursor + 1
|
|
232
|
-
cursor++
|
|
233
|
-
continue
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const isQuoteStart = char === '\'' || char === '"'
|
|
237
|
-
if (isQuoteStart) {
|
|
238
|
-
quote = char
|
|
239
|
-
cursor++
|
|
240
|
-
continue
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
cursor++
|
|
244
|
-
}
|
|
245
|
-
jumpPosition(position, str, cursor)
|
|
246
|
-
|
|
247
|
-
const wLen = words.length
|
|
248
|
-
const type = 'attribute'
|
|
249
|
-
for (let i = 0; i < wLen; i++) {
|
|
250
|
-
const word = words[i]
|
|
251
|
-
const isNotPair = word.indexOf('=') === -1
|
|
252
|
-
if (isNotPair) {
|
|
253
|
-
const secondWord = words[i + 1]
|
|
254
|
-
if (secondWord && startsWith(secondWord, '=')) {
|
|
255
|
-
if (secondWord.length > 1) {
|
|
256
|
-
const newWord = word + secondWord
|
|
257
|
-
tokens.push({type, content: newWord})
|
|
258
|
-
i += 1
|
|
259
|
-
continue
|
|
260
|
-
}
|
|
261
|
-
const thirdWord = words[i + 2]
|
|
262
|
-
i += 1
|
|
263
|
-
if (thirdWord) {
|
|
264
|
-
const newWord = word + '=' + thirdWord
|
|
265
|
-
tokens.push({type, content: newWord})
|
|
266
|
-
i += 1
|
|
267
|
-
continue
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
if (endsWith(word, '=')) {
|
|
272
|
-
const secondWord = words[i + 1]
|
|
273
|
-
if (secondWord && !stringIncludes(secondWord, '=')) {
|
|
274
|
-
const newWord = word + secondWord
|
|
275
|
-
tokens.push({type, content: newWord})
|
|
276
|
-
i += 1
|
|
277
|
-
continue
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const newWord = word.slice(0, -1)
|
|
281
|
-
tokens.push({type, content: newWord})
|
|
282
|
-
continue
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
tokens.push({type, content: word})
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const push = [].push
|
|
290
|
-
|
|
291
|
-
function lexSkipTag (tagName, state) {
|
|
292
|
-
const {str, position, tokens} = state
|
|
293
|
-
const safeTagName = tagName.toLowerCase()
|
|
294
|
-
const len = str.length
|
|
295
|
-
let index = position.index
|
|
296
|
-
while (index < len) {
|
|
297
|
-
const nextTag = str.indexOf('</', index)
|
|
298
|
-
if (nextTag === -1) {
|
|
299
|
-
lexText(state)
|
|
300
|
-
break
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const tagStartPosition = copyPositionStart(position)
|
|
304
|
-
jumpPosition(tagStartPosition, str, nextTag)
|
|
305
|
-
const tagState = {str, position: tagStartPosition, tokens: []}
|
|
306
|
-
const name = lexTag(tagState)
|
|
307
|
-
if (safeTagName !== name.toLowerCase()) {
|
|
308
|
-
index = tagState.position.index
|
|
309
|
-
continue
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
if (nextTag !== position.index) {
|
|
313
|
-
const textStart = copyPositionStart(position)
|
|
314
|
-
jumpPosition(position, str, nextTag)
|
|
315
|
-
tokens.push({
|
|
316
|
-
type: 'text',
|
|
317
|
-
content: str.slice(textStart.index, nextTag),
|
|
318
|
-
position: {
|
|
319
|
-
start: textStart,
|
|
320
|
-
end: copyPositionEnd(position)
|
|
321
|
-
}
|
|
322
|
-
})
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
push.apply(tokens, tagState.tokens)
|
|
326
|
-
jumpPosition(position, str, tagState.position.index)
|
|
327
|
-
break
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
module.exports = {
|
|
332
|
-
feedPosition,
|
|
333
|
-
jumpPosition,
|
|
334
|
-
makeInitialPosition,
|
|
335
|
-
copyPosition,
|
|
336
|
-
lexer,
|
|
337
|
-
lex,
|
|
338
|
-
findTextEnd,
|
|
339
|
-
lexText,
|
|
340
|
-
lexTag,
|
|
341
|
-
isWhitespaceChar,
|
|
342
|
-
lexTagName,
|
|
343
|
-
lexTagAttributes,
|
|
344
|
-
lexSkipTag,
|
|
345
|
-
}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
const { optionsParse } = require('../../options-parser')
|
|
2
|
-
const { arrayIncludes }= require('./compat')
|
|
3
|
-
|
|
4
|
-
function parser (tokens, options) {
|
|
5
|
-
const root = {tagName: null, children: []}
|
|
6
|
-
const state = {tokens, options, cursor: 0, stack: [root]}
|
|
7
|
-
parse(state)
|
|
8
|
-
return root.children
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function hasTerminalParent (tagName, stack, terminals) {
|
|
12
|
-
const tagParents = terminals[tagName]
|
|
13
|
-
if (tagParents) {
|
|
14
|
-
let currentIndex = stack.length - 1
|
|
15
|
-
while (currentIndex >= 0) {
|
|
16
|
-
const parentTagName = stack[currentIndex].tagName
|
|
17
|
-
if (parentTagName === tagName) {
|
|
18
|
-
break
|
|
19
|
-
}
|
|
20
|
-
if (arrayIncludes(tagParents, parentTagName)) {
|
|
21
|
-
return true
|
|
22
|
-
}
|
|
23
|
-
currentIndex--
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return false
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function rewindStack (stack, newLength, childrenEndPosition, endPosition) {
|
|
30
|
-
stack[newLength].position.end = endPosition
|
|
31
|
-
for (let i = newLength + 1, len = stack.length; i < len; i++) {
|
|
32
|
-
stack[i].position.end = childrenEndPosition
|
|
33
|
-
}
|
|
34
|
-
stack.splice(newLength)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function parse (state) {
|
|
38
|
-
const {tokens, options} = state
|
|
39
|
-
let {stack} = state
|
|
40
|
-
let nodes = stack[stack.length - 1].children
|
|
41
|
-
const len = tokens.length
|
|
42
|
-
let {cursor} = state
|
|
43
|
-
while (cursor < len) {
|
|
44
|
-
const token = tokens[cursor]
|
|
45
|
-
if (token.type !== 'tag-start') {
|
|
46
|
-
nodes.push(token)
|
|
47
|
-
cursor++
|
|
48
|
-
continue
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const tagToken = tokens[++cursor]
|
|
52
|
-
cursor++
|
|
53
|
-
const tagName = tagToken.content.toLowerCase()
|
|
54
|
-
if (token.close) {
|
|
55
|
-
let index = stack.length
|
|
56
|
-
let shouldRewind = false
|
|
57
|
-
while (--index > -1) {
|
|
58
|
-
if (stack[index].tagName === tagName) {
|
|
59
|
-
shouldRewind = true
|
|
60
|
-
break
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
while (cursor < len) {
|
|
64
|
-
const endToken = tokens[cursor]
|
|
65
|
-
if (endToken.type !== 'tag-end') break
|
|
66
|
-
cursor++
|
|
67
|
-
}
|
|
68
|
-
if (shouldRewind) {
|
|
69
|
-
rewindStack(stack, index, token.position.start, tokens[cursor - 1].position.end)
|
|
70
|
-
break
|
|
71
|
-
} else {
|
|
72
|
-
continue
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const isClosingTag = arrayIncludes(options.closingTags, tagName)
|
|
77
|
-
let shouldRewindToAutoClose = isClosingTag
|
|
78
|
-
if (shouldRewindToAutoClose) {
|
|
79
|
-
const { closingTagAncestorBreakers: terminals } = options
|
|
80
|
-
shouldRewindToAutoClose = !hasTerminalParent(tagName, stack, terminals)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (shouldRewindToAutoClose) {
|
|
84
|
-
// rewind the stack to just above the previous
|
|
85
|
-
// closing tag of the same name
|
|
86
|
-
let currentIndex = stack.length - 1
|
|
87
|
-
while (currentIndex > 0) {
|
|
88
|
-
if (tagName === stack[currentIndex].tagName) {
|
|
89
|
-
rewindStack(stack, currentIndex, token.position.start, token.position.start)
|
|
90
|
-
const previousIndex = currentIndex - 1
|
|
91
|
-
nodes = stack[previousIndex].children
|
|
92
|
-
break
|
|
93
|
-
}
|
|
94
|
-
currentIndex = currentIndex - 1
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// let attributes = []
|
|
99
|
-
let propsRaw = ''
|
|
100
|
-
let attrToken
|
|
101
|
-
while (cursor < len) {
|
|
102
|
-
attrToken = tokens[cursor]
|
|
103
|
-
if (attrToken.type === 'tag-end') break
|
|
104
|
-
// attributes.push(attrToken.content)
|
|
105
|
-
propsRaw+= ((propsRaw !== '') ? ' ' : '') + attrToken.content
|
|
106
|
-
cursor++
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
cursor++
|
|
110
|
-
const children = []
|
|
111
|
-
const position = {
|
|
112
|
-
start: token.position.start,
|
|
113
|
-
end: attrToken.position.end
|
|
114
|
-
}
|
|
115
|
-
const elementNode = {
|
|
116
|
-
type: 'element',
|
|
117
|
-
tagName: tagToken.content,
|
|
118
|
-
// attributes,
|
|
119
|
-
props: (propsRaw) ? optionsParse(propsRaw) : {},
|
|
120
|
-
propsRaw,
|
|
121
|
-
children,
|
|
122
|
-
position
|
|
123
|
-
}
|
|
124
|
-
nodes.push(elementNode)
|
|
125
|
-
|
|
126
|
-
const hasChildren = !(attrToken.close || arrayIncludes(options.voidTags, tagName))
|
|
127
|
-
if (hasChildren) {
|
|
128
|
-
const size = stack.push({tagName, children, position})
|
|
129
|
-
const innerState = {tokens, options, cursor, stack}
|
|
130
|
-
parse(innerState)
|
|
131
|
-
cursor = innerState.cursor
|
|
132
|
-
const rewoundInElement = stack.length === size
|
|
133
|
-
if (rewoundInElement) {
|
|
134
|
-
elementNode.position.end = tokens[cursor - 1].position.end
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
state.cursor = cursor
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
module.exports = {
|
|
142
|
-
parser,
|
|
143
|
-
hasTerminalParent,
|
|
144
|
-
rewindStack,
|
|
145
|
-
parse,
|
|
146
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
const { arrayIncludes }= require('./compat')
|
|
2
|
-
|
|
3
|
-
// Old
|
|
4
|
-
function formatAttributes(attributes) {
|
|
5
|
-
return attributes.reduce((attrs, attribute) => {
|
|
6
|
-
const {key, value} = attribute
|
|
7
|
-
if (value === null) {
|
|
8
|
-
return `${attrs} ${key}`
|
|
9
|
-
}
|
|
10
|
-
const quoteEscape = value.indexOf('\'') !== -1
|
|
11
|
-
const quote = quoteEscape ? '"' : '\''
|
|
12
|
-
return `${attrs} ${key}=${quote}${value}${quote}`
|
|
13
|
-
}, '')
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function toHTML(tree, options) {
|
|
17
|
-
return tree.map((node) => {
|
|
18
|
-
if (node.type === 'text') {
|
|
19
|
-
return node.content
|
|
20
|
-
}
|
|
21
|
-
if (node.type === 'comment') {
|
|
22
|
-
return `<!--${node.content}-->`
|
|
23
|
-
}
|
|
24
|
-
const {tagName, propsRaw, children} = node
|
|
25
|
-
// @TODO update prop parsing to keep new lines
|
|
26
|
-
const propsString = (propsRaw) ? ` ${propsRaw}` : ''
|
|
27
|
-
const isSelfClosing = arrayIncludes(options.voidTags, tagName.toLowerCase())
|
|
28
|
-
return isSelfClosing
|
|
29
|
-
? `<${tagName}${propsString}>`
|
|
30
|
-
: `<${tagName}${propsString}>${toHTML(children, options)}</${tagName}>`
|
|
31
|
-
}).join('')
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
module.exports = {
|
|
35
|
-
formatAttributes,
|
|
36
|
-
toHTML
|
|
37
|
-
}
|