@jsenv/ast 3.0.5 → 3.0.7
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/package.json +4 -4
- package/src/html/html_node.js +96 -68
- package/src/html/html_node_text.js +87 -1
- package/src/html/html_parse.js +80 -8
- package/src/main.js +1 -1
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/ast",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.7",
|
|
4
4
|
"description": "Parse and transform HTML,CSS and JS",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "https://github.com/jsenv/
|
|
8
|
+
"url": "https://github.com/jsenv/core",
|
|
9
9
|
"directory": "packages/ast"
|
|
10
10
|
},
|
|
11
11
|
"publishConfig": {
|
|
@@ -33,12 +33,12 @@
|
|
|
33
33
|
"test": "node --conditions=development ./scripts/test.mjs"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@babel/parser": "7.21.
|
|
36
|
+
"@babel/parser": "7.21.8",
|
|
37
37
|
"acorn": "8.8.2",
|
|
38
38
|
"acorn-import-assertions": "1.8.0",
|
|
39
39
|
"acorn-walk": "8.2.0",
|
|
40
40
|
"parse5": "7.1.2",
|
|
41
|
-
"postcss": "8.4.
|
|
41
|
+
"postcss": "8.4.23",
|
|
42
42
|
"postcss-value-parser": "4.2.0"
|
|
43
43
|
}
|
|
44
44
|
}
|
package/src/html/html_node.js
CHANGED
|
@@ -3,6 +3,12 @@ import { parseFragment } from "parse5"
|
|
|
3
3
|
import { setHtmlNodeAttributes } from "./html_node_attributes.js"
|
|
4
4
|
import { findHtmlNode } from "./html_search.js"
|
|
5
5
|
import { analyzeScriptNode } from "./html_analysis.js"
|
|
6
|
+
import {
|
|
7
|
+
getIndentation,
|
|
8
|
+
increaseIndentation,
|
|
9
|
+
getHtmlNodeText,
|
|
10
|
+
setHtmlNodeText,
|
|
11
|
+
} from "./html_node_text.js"
|
|
6
12
|
|
|
7
13
|
export const removeHtmlNode = (htmlNode) => {
|
|
8
14
|
const { childNodes } = htmlNode.parentNode
|
|
@@ -21,81 +27,93 @@ export const injectHtmlNode = (htmlAst, node, jsenvPluginName = "jsenv") => {
|
|
|
21
27
|
setHtmlNodeAttributes(node, {
|
|
22
28
|
"jsenv-injected-by": jsenvPluginName,
|
|
23
29
|
})
|
|
24
|
-
const
|
|
25
|
-
const bodyNode = findChild(
|
|
26
|
-
|
|
30
|
+
const htmlNode = findChild(htmlAst, (node) => node.nodeName === "html")
|
|
31
|
+
const bodyNode = findChild(htmlNode, (node) => node.nodeName === "body")
|
|
32
|
+
let after
|
|
33
|
+
if (node.nodeName !== "#text") {
|
|
34
|
+
// last child that is not a text
|
|
35
|
+
for (const child of bodyNode.childNodes) {
|
|
36
|
+
if (child.nodeName !== "#text") {
|
|
37
|
+
after = child
|
|
38
|
+
break
|
|
39
|
+
}
|
|
40
|
+
after = child
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
insertHtmlNodeAfter(node, after)
|
|
27
44
|
}
|
|
28
45
|
|
|
29
|
-
export const
|
|
46
|
+
export const injectHtmlNodeAsEarlyAsPossible = (
|
|
30
47
|
htmlAst,
|
|
31
|
-
|
|
48
|
+
node,
|
|
32
49
|
jsenvPluginName = "jsenv",
|
|
33
50
|
) => {
|
|
34
|
-
setHtmlNodeAttributes(
|
|
51
|
+
setHtmlNodeAttributes(node, {
|
|
35
52
|
"jsenv-injected-by": jsenvPluginName,
|
|
36
53
|
})
|
|
37
|
-
const
|
|
38
|
-
if (isJsModule) {
|
|
39
|
-
const firstImportmapScript = findHtmlNode(htmlAst, (node) => {
|
|
40
|
-
return (
|
|
41
|
-
node.nodeName === "script" &&
|
|
42
|
-
analyzeScriptNode(node).type === "importmap"
|
|
43
|
-
)
|
|
44
|
-
})
|
|
54
|
+
const isScript = node.nodeName === "script"
|
|
45
55
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
if (isScript) {
|
|
57
|
+
const isJsModule = analyzeScriptNode(node).type === "js_module"
|
|
58
|
+
if (isJsModule) {
|
|
59
|
+
const firstImportmapScript = findHtmlNode(htmlAst, (node) => {
|
|
60
|
+
return (
|
|
61
|
+
node.nodeName === "script" &&
|
|
62
|
+
analyzeScriptNode(node).type === "importmap"
|
|
63
|
+
)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
if (firstImportmapScript) {
|
|
67
|
+
const importmapParent = firstImportmapScript.parentNode
|
|
68
|
+
const importmapSiblings = importmapParent.childNodes
|
|
69
|
+
const nextSiblings = importmapSiblings.slice(
|
|
70
|
+
importmapSiblings.indexOf(firstImportmapScript) + 1,
|
|
71
|
+
)
|
|
72
|
+
let after = firstImportmapScript
|
|
73
|
+
for (const nextSibling of nextSiblings) {
|
|
74
|
+
if (nextSibling.nodeName === "script") {
|
|
75
|
+
insertHtmlNodeBefore(node, nextSibling)
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
if (nextSibling.nodeName === "link") {
|
|
79
|
+
after = nextSibling
|
|
80
|
+
}
|
|
59
81
|
}
|
|
82
|
+
insertHtmlNodeAfter(node, after)
|
|
83
|
+
return
|
|
60
84
|
}
|
|
61
|
-
return insertHtmlNodeAfter(scriptNode, importmapParent, after)
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
const headNode = findChild(htmlAst, (node) => node.nodeName === "html")
|
|
65
|
-
.childNodes[0]
|
|
66
|
-
let after = headNode.childNodes[0]
|
|
67
|
-
for (const child of headNode.childNodes) {
|
|
68
|
-
if (child.nodeName === "script") {
|
|
69
|
-
return insertHtmlNodeBefore(scriptNode, headNode, child)
|
|
70
85
|
}
|
|
71
|
-
|
|
72
|
-
|
|
86
|
+
const headNode = findChild(htmlAst, (node) => node.nodeName === "html")
|
|
87
|
+
.childNodes[0]
|
|
88
|
+
let after = headNode.childNodes[0]
|
|
89
|
+
for (const child of headNode.childNodes) {
|
|
90
|
+
if (child.nodeName === "script") {
|
|
91
|
+
insertHtmlNodeBefore(node, child)
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
if (child.nodeName === "link") {
|
|
95
|
+
after = child
|
|
96
|
+
}
|
|
73
97
|
}
|
|
98
|
+
insertHtmlNodeAfter(node, after)
|
|
99
|
+
return
|
|
74
100
|
}
|
|
75
|
-
|
|
101
|
+
|
|
102
|
+
injectHtmlNode(htmlAst, node)
|
|
103
|
+
return
|
|
76
104
|
}
|
|
77
105
|
|
|
78
|
-
export const insertHtmlNodeBefore = (
|
|
79
|
-
|
|
80
|
-
futureParentNode,
|
|
81
|
-
futureNextSibling,
|
|
82
|
-
) => {
|
|
106
|
+
export const insertHtmlNodeBefore = (nodeToInsert, futureNextSibling) => {
|
|
107
|
+
const futureParentNode = futureNextSibling.parentNode
|
|
83
108
|
const { childNodes = [] } = futureParentNode
|
|
84
|
-
const futureIndex = futureNextSibling
|
|
85
|
-
? childNodes.indexOf(futureNextSibling)
|
|
86
|
-
: 0
|
|
109
|
+
const futureIndex = childNodes.indexOf(futureNextSibling)
|
|
87
110
|
injectWithWhitespaces(nodeToInsert, futureParentNode, futureIndex)
|
|
88
111
|
}
|
|
89
112
|
|
|
90
|
-
export const insertHtmlNodeAfter = (
|
|
91
|
-
|
|
92
|
-
futureParentNode,
|
|
93
|
-
futurePrevSibling,
|
|
94
|
-
) => {
|
|
113
|
+
export const insertHtmlNodeAfter = (nodeToInsert, futurePrevSibling) => {
|
|
114
|
+
const futureParentNode = futurePrevSibling.parentNode
|
|
95
115
|
const { childNodes = [] } = futureParentNode
|
|
96
|
-
const futureIndex = futurePrevSibling
|
|
97
|
-
? childNodes.indexOf(futurePrevSibling) + 1
|
|
98
|
-
: childNodes.length
|
|
116
|
+
const futureIndex = childNodes.indexOf(futurePrevSibling) + 1
|
|
99
117
|
injectWithWhitespaces(nodeToInsert, futureParentNode, futureIndex)
|
|
100
118
|
}
|
|
101
119
|
|
|
@@ -104,30 +122,40 @@ const injectWithWhitespaces = (nodeToInsert, futureParentNode, futureIndex) => {
|
|
|
104
122
|
const previousSiblings = childNodes.slice(0, futureIndex)
|
|
105
123
|
const nextSiblings = childNodes.slice(futureIndex)
|
|
106
124
|
const futureChildNodes = []
|
|
107
|
-
|
|
108
|
-
if (
|
|
125
|
+
|
|
126
|
+
if (previousSiblings.length) {
|
|
109
127
|
futureChildNodes.push(...previousSiblings)
|
|
110
128
|
}
|
|
111
|
-
|
|
129
|
+
const previousSibling = childNodes[futureIndex - 1]
|
|
130
|
+
if (
|
|
131
|
+
nodeToInsert.nodeName !== "#text" &&
|
|
132
|
+
(!previousSibling || !isWhitespaceNode(previousSibling))
|
|
133
|
+
) {
|
|
134
|
+
let indentation
|
|
135
|
+
if (childNodes.length) {
|
|
136
|
+
indentation = getIndentation(childNodes[childNodes.length - 1])
|
|
137
|
+
} else {
|
|
138
|
+
const parentIndentation = getIndentation(futureParentNode)
|
|
139
|
+
indentation = increaseIndentation(parentIndentation, 2)
|
|
140
|
+
}
|
|
112
141
|
futureChildNodes.push({
|
|
113
142
|
nodeName: "#text",
|
|
114
|
-
value:
|
|
143
|
+
value: `\n${indentation}`,
|
|
115
144
|
parentNode: futureParentNode,
|
|
116
145
|
})
|
|
117
146
|
}
|
|
118
147
|
futureChildNodes.push(nodeToInsert)
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
futureChildNodes.push({
|
|
122
|
-
nodeName: "#text",
|
|
123
|
-
value: "\n ",
|
|
124
|
-
parentNode: futureParentNode,
|
|
125
|
-
})
|
|
126
|
-
}
|
|
127
|
-
if (nextSibling) {
|
|
148
|
+
nodeToInsert.parentNode = futureParentNode
|
|
149
|
+
if (nextSiblings.length) {
|
|
128
150
|
futureChildNodes.push(...nextSiblings)
|
|
129
151
|
}
|
|
130
152
|
futureParentNode.childNodes = futureChildNodes
|
|
153
|
+
|
|
154
|
+
// update indentation when node contains text
|
|
155
|
+
const text = getHtmlNodeText(nodeToInsert)
|
|
156
|
+
if (text) {
|
|
157
|
+
setHtmlNodeText(nodeToInsert, text, { indentation: "auto" })
|
|
158
|
+
}
|
|
131
159
|
}
|
|
132
160
|
|
|
133
161
|
const isWhitespaceNode = (node) => {
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getHtmlNodeAttribute,
|
|
3
|
+
setHtmlNodeAttributes,
|
|
4
|
+
} from "./html_node_attributes.js"
|
|
5
|
+
|
|
1
6
|
export const getHtmlNodeText = (htmlNode) => {
|
|
2
7
|
const textNode = getTextNode(htmlNode)
|
|
3
8
|
return textNode ? textNode.value : undefined
|
|
4
9
|
}
|
|
5
10
|
|
|
6
11
|
const getTextNode = (htmlNode) => {
|
|
12
|
+
if (htmlNode.nodeName === "#text") {
|
|
13
|
+
return null
|
|
14
|
+
}
|
|
7
15
|
const firstChild = htmlNode.childNodes[0]
|
|
8
16
|
const textNode =
|
|
9
17
|
firstChild && firstChild.nodeName === "#text" ? firstChild : null
|
|
@@ -17,7 +25,24 @@ export const removeHtmlNodeText = (htmlNode) => {
|
|
|
17
25
|
}
|
|
18
26
|
}
|
|
19
27
|
|
|
20
|
-
export const setHtmlNodeText = (
|
|
28
|
+
export const setHtmlNodeText = (
|
|
29
|
+
htmlNode,
|
|
30
|
+
textContent,
|
|
31
|
+
{ indentation } = {},
|
|
32
|
+
) => {
|
|
33
|
+
if (indentation === "auto") {
|
|
34
|
+
const contentIndentedAttribute = getHtmlNodeAttribute(
|
|
35
|
+
htmlNode,
|
|
36
|
+
"content-indented",
|
|
37
|
+
)
|
|
38
|
+
if (contentIndentedAttribute === undefined) {
|
|
39
|
+
const indentation = getIndentation(htmlNode)
|
|
40
|
+
textContent = setIndentation(textContent, indentation)
|
|
41
|
+
setHtmlNodeAttributes(htmlNode, {
|
|
42
|
+
"content-indented": "",
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
}
|
|
21
46
|
const textNode = getTextNode(htmlNode)
|
|
22
47
|
if (textNode) {
|
|
23
48
|
textNode.value = textContent
|
|
@@ -30,3 +55,64 @@ export const setHtmlNodeText = (htmlNode, textContent) => {
|
|
|
30
55
|
htmlNode.childNodes.splice(0, 0, newTextNode)
|
|
31
56
|
}
|
|
32
57
|
}
|
|
58
|
+
|
|
59
|
+
export const getIndentation = (htmlNode) => {
|
|
60
|
+
const parentNode = htmlNode.parentNode
|
|
61
|
+
if (!parentNode) {
|
|
62
|
+
return ""
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const siblings = parentNode.childNodes
|
|
66
|
+
const index = siblings.indexOf(htmlNode)
|
|
67
|
+
if (index === 0) {
|
|
68
|
+
if (htmlNode.nodeName === "#text") {
|
|
69
|
+
return extractIndentation(htmlNode)
|
|
70
|
+
}
|
|
71
|
+
return ""
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let textNodeIndex = 0
|
|
75
|
+
let textNode
|
|
76
|
+
while (textNodeIndex !== index) {
|
|
77
|
+
const previousSibling = siblings[textNodeIndex]
|
|
78
|
+
textNodeIndex++
|
|
79
|
+
if (previousSibling.nodeName === "#text") {
|
|
80
|
+
textNode = previousSibling
|
|
81
|
+
break
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (!textNode) {
|
|
85
|
+
return ""
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return extractIndentation(textNode)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const extractIndentation = (textNode) => {
|
|
92
|
+
const text = textNode.value
|
|
93
|
+
const lines = text.split(/\r?\n/)
|
|
94
|
+
const lastLine = lines[lines.length - 1]
|
|
95
|
+
if (lastLine.match(/^[\t ]+$/)) {
|
|
96
|
+
return lastLine
|
|
97
|
+
}
|
|
98
|
+
return ""
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const setIndentation = (htmlNodeText, indentation) => {
|
|
102
|
+
const contentIdentation = increaseIndentation(indentation, 2)
|
|
103
|
+
const lines = htmlNodeText.trimEnd().split(/\r?\n/)
|
|
104
|
+
let result = `\n`
|
|
105
|
+
let i = 0
|
|
106
|
+
while (i < lines.length) {
|
|
107
|
+
const line = lines[i]
|
|
108
|
+
i++
|
|
109
|
+
result += `${contentIdentation}${line}\n`
|
|
110
|
+
}
|
|
111
|
+
result += `${indentation}`
|
|
112
|
+
return result
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const increaseIndentation = (indentation, size) => {
|
|
116
|
+
const char = indentation[0]
|
|
117
|
+
return char ? `${indentation}${char.repeat(size)}` : " ".repeat(size)
|
|
118
|
+
}
|
package/src/html/html_parse.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parse, serialize, parseFragment } from "parse5"
|
|
2
2
|
|
|
3
|
+
import { insertHtmlNodeAfter, insertHtmlNodeBefore } from "./html_node.js"
|
|
3
4
|
import {
|
|
4
5
|
getHtmlNodeAttribute,
|
|
5
6
|
setHtmlNodeAttributes,
|
|
@@ -40,14 +41,29 @@ export const parseHtmlString = (
|
|
|
40
41
|
})
|
|
41
42
|
}
|
|
42
43
|
}
|
|
44
|
+
const htmlNode = htmlAst.childNodes.find((node) => node.nodeName === "html")
|
|
45
|
+
if (htmlNode) {
|
|
46
|
+
const bodyNode = htmlNode.childNodes.find(
|
|
47
|
+
(node) => node.nodeName === "body",
|
|
48
|
+
)
|
|
49
|
+
// for some reason "parse5" adds "\n\n" to the last text node of <body>
|
|
50
|
+
const lastBodyNode = bodyNode.childNodes[bodyNode.childNodes.length - 1]
|
|
51
|
+
if (
|
|
52
|
+
lastBodyNode &&
|
|
53
|
+
lastBodyNode.nodeName === "#text" &&
|
|
54
|
+
lastBodyNode.value.endsWith("\n\n")
|
|
55
|
+
) {
|
|
56
|
+
lastBodyNode.value = lastBodyNode.value.slice(0, -2)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
43
59
|
return htmlAst
|
|
44
60
|
}
|
|
45
61
|
|
|
46
62
|
export const stringifyHtmlAst = (
|
|
47
63
|
htmlAst,
|
|
48
|
-
{ cleanupJsenvAttributes = false } = {},
|
|
64
|
+
{ cleanupJsenvAttributes = false, cleanupPositionAttributes = false } = {},
|
|
49
65
|
) => {
|
|
50
|
-
if (cleanupJsenvAttributes) {
|
|
66
|
+
if (cleanupJsenvAttributes || cleanupPositionAttributes) {
|
|
51
67
|
const htmlNode = findHtmlChildNode(
|
|
52
68
|
htmlAst,
|
|
53
69
|
(node) => node.nodeName === "html",
|
|
@@ -66,21 +82,77 @@ export const stringifyHtmlAst = (
|
|
|
66
82
|
"original-position": undefined,
|
|
67
83
|
"original-src-position": undefined,
|
|
68
84
|
"original-href-position": undefined,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
85
|
+
...(cleanupJsenvAttributes
|
|
86
|
+
? {
|
|
87
|
+
"inlined-from-src": undefined,
|
|
88
|
+
"inlined-from-href": undefined,
|
|
89
|
+
"jsenv-cooked-by": undefined,
|
|
90
|
+
"jsenv-inlined-by": undefined,
|
|
91
|
+
"jsenv-injected-by": undefined,
|
|
92
|
+
"jsenv-debug": undefined,
|
|
93
|
+
"content-indented": undefined,
|
|
94
|
+
}
|
|
95
|
+
: {}),
|
|
75
96
|
})
|
|
76
97
|
},
|
|
77
98
|
})
|
|
78
99
|
}
|
|
79
100
|
}
|
|
101
|
+
// ensure body and html have \n
|
|
102
|
+
ensureLineBreaksBetweenHtmlNodes(htmlAst)
|
|
80
103
|
const htmlString = serialize(htmlAst)
|
|
81
104
|
return htmlString
|
|
82
105
|
}
|
|
83
106
|
|
|
107
|
+
const ensureLineBreaksBetweenHtmlNodes = (rootNode) => {
|
|
108
|
+
const mutations = []
|
|
109
|
+
|
|
110
|
+
const documentType = rootNode.childNodes[0]
|
|
111
|
+
if (documentType.nodeName === "#documentType") {
|
|
112
|
+
const html = rootNode.childNodes[1]
|
|
113
|
+
if (html.nodeName === "html") {
|
|
114
|
+
mutations.push(() => {
|
|
115
|
+
insertHtmlNodeAfter({ nodeName: "#text", value: "\n" }, documentType)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
const head = html.childNodes[0]
|
|
119
|
+
if (head.nodeName === "head") {
|
|
120
|
+
mutations.push(() => {
|
|
121
|
+
insertHtmlNodeBefore({ nodeName: "#text", value: "\n " }, head)
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const body = html.childNodes.find((node) => node.nodeName === "body")
|
|
126
|
+
if (body) {
|
|
127
|
+
const bodyLastChild = body.childNodes[body.childNodes.length - 1]
|
|
128
|
+
if (bodyLastChild.nodeName !== "#text") {
|
|
129
|
+
mutations.push(() => {
|
|
130
|
+
insertHtmlNodeAfter(
|
|
131
|
+
{ nodeName: "#text", value: "\n " },
|
|
132
|
+
bodyLastChild,
|
|
133
|
+
)
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
if (
|
|
137
|
+
bodyLastChild.nodeName === "#text" &&
|
|
138
|
+
bodyLastChild.value[bodyLastChild.value.length - 1] === "\n"
|
|
139
|
+
) {
|
|
140
|
+
bodyLastChild.value = bodyLastChild.value.slice(0, -1)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const htmlLastChild = html.childNodes[html.childNodes.length - 1]
|
|
145
|
+
if (htmlLastChild.nodeName !== "#text") {
|
|
146
|
+
mutations.push(() => {
|
|
147
|
+
insertHtmlNodeAfter({ nodeName: "#text", value: "\n" }, htmlLastChild)
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
mutations.forEach((m) => m())
|
|
154
|
+
}
|
|
155
|
+
|
|
84
156
|
export const parseSvgString = (svgString) => {
|
|
85
157
|
const svgAst = parseFragment(svgString, {
|
|
86
158
|
sourceCodeLocationInfo: true,
|