@jsenv/ast 1.1.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/package.json +46 -0
- package/src/css/apply_post_css.js +57 -0
- package/src/css/parcel_css.js +58 -0
- package/src/css/postcss_plugin_url_visitor.js +287 -0
- package/src/html/html_analysis.js +35 -0
- package/src/html/html_inlining.js +40 -0
- package/src/html/html_node.js +112 -0
- package/src/html/html_node_attributes.js +43 -0
- package/src/html/html_node_position.js +122 -0
- package/src/html/html_node_text.js +32 -0
- package/src/html/html_parse.js +89 -0
- package/src/html/html_search.js +64 -0
- package/src/html/html_src_set.js +18 -0
- package/src/js/apply_babel_plugins.js +100 -0
- package/src/js/babel_utils.js +64 -0
- package/src/js/js_parse_error.js +15 -0
- package/src/js/js_static_analysis/helpers.js +17 -0
- package/src/js/js_static_analysis/import_export.js +135 -0
- package/src/js/js_static_analysis/new_url.js +132 -0
- package/src/js/js_static_analysis/system.js +74 -0
- package/src/js/js_static_analysis/web_worker.js +32 -0
- package/src/js/js_static_analysis/web_worker_entry_point.js +143 -0
- package/src/js/parse_js_urls.js +122 -0
- package/src/js/parse_js_with_acorn.js +40 -0
- package/src/main.js +44 -0
- package/src/require.js +3 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getHtmlNodeAttribute,
|
|
3
|
+
setHtmlNodeAttributes,
|
|
4
|
+
} from "./html_node_attributes.js"
|
|
5
|
+
|
|
6
|
+
export const storeHtmlNodePosition = (node) => {
|
|
7
|
+
const originalPositionAttributeName = `original-position`
|
|
8
|
+
const originalPosition = getHtmlNodeAttribute(
|
|
9
|
+
node,
|
|
10
|
+
originalPositionAttributeName,
|
|
11
|
+
)
|
|
12
|
+
if (originalPosition !== undefined) {
|
|
13
|
+
return true
|
|
14
|
+
}
|
|
15
|
+
const { sourceCodeLocation } = node
|
|
16
|
+
if (!sourceCodeLocation) {
|
|
17
|
+
return false
|
|
18
|
+
}
|
|
19
|
+
const { startLine, startCol, endLine, endCol } = sourceCodeLocation
|
|
20
|
+
setHtmlNodeAttributes(node, {
|
|
21
|
+
[originalPositionAttributeName]: `${startLine}:${startCol};${endLine}:${endCol}`,
|
|
22
|
+
})
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
25
|
+
export const storeHtmlNodeAttributePosition = (node, attributeName) => {
|
|
26
|
+
const { sourceCodeLocation } = node
|
|
27
|
+
if (!sourceCodeLocation) {
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
const attributeValue = getHtmlNodeAttribute(node, attributeName)
|
|
31
|
+
if (attributeValue === undefined) {
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
const attributeLocation = sourceCodeLocation.attrs[attributeName]
|
|
35
|
+
if (!attributeLocation) {
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
const originalPositionAttributeName = `original-${attributeName}-position`
|
|
39
|
+
const originalPosition = getHtmlNodeAttribute(
|
|
40
|
+
node,
|
|
41
|
+
originalPositionAttributeName,
|
|
42
|
+
)
|
|
43
|
+
if (originalPosition !== undefined) {
|
|
44
|
+
return true
|
|
45
|
+
}
|
|
46
|
+
const { startLine, startCol, endLine, endCol } = attributeLocation
|
|
47
|
+
setHtmlNodeAttributes(node, {
|
|
48
|
+
[originalPositionAttributeName]: `${startLine}:${startCol};${endLine}:${endCol}`,
|
|
49
|
+
})
|
|
50
|
+
return true
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const getHtmlNodePosition = (node, { preferOriginal = false } = {}) => {
|
|
54
|
+
const position = {}
|
|
55
|
+
const { sourceCodeLocation } = node
|
|
56
|
+
if (sourceCodeLocation) {
|
|
57
|
+
const { startLine, startCol, endLine, endCol } = sourceCodeLocation
|
|
58
|
+
Object.assign(position, {
|
|
59
|
+
line: startLine,
|
|
60
|
+
lineEnd: endLine,
|
|
61
|
+
column: startCol,
|
|
62
|
+
columnEnd: endCol,
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
const originalPosition = getHtmlNodeAttribute(node, "original-position")
|
|
66
|
+
if (originalPosition === undefined) {
|
|
67
|
+
return position
|
|
68
|
+
}
|
|
69
|
+
const [start, end] = originalPosition.split(";")
|
|
70
|
+
const [originalLine, originalColumn] = start.split(":")
|
|
71
|
+
const [originalLineEnd, originalColumnEnd] = end.split(":")
|
|
72
|
+
Object.assign(position, {
|
|
73
|
+
originalLine: parseInt(originalLine),
|
|
74
|
+
originalColumn: parseInt(originalColumn),
|
|
75
|
+
originalLineEnd: parseInt(originalLineEnd),
|
|
76
|
+
originalColumnEnd: parseInt(originalColumnEnd),
|
|
77
|
+
})
|
|
78
|
+
if (preferOriginal) {
|
|
79
|
+
position.line = position.originalLine
|
|
80
|
+
position.column = position.originalColumn
|
|
81
|
+
position.lineEnd = position.originalLineEnd
|
|
82
|
+
position.columnEnd = position.originalColumnEnd
|
|
83
|
+
position.isOriginal = true
|
|
84
|
+
}
|
|
85
|
+
return position
|
|
86
|
+
}
|
|
87
|
+
export const getHtmlNodeAttributePosition = (node, attributeName) => {
|
|
88
|
+
const position = {}
|
|
89
|
+
const { sourceCodeLocation } = node
|
|
90
|
+
if (sourceCodeLocation) {
|
|
91
|
+
const attributeLocation = sourceCodeLocation.attrs[attributeName]
|
|
92
|
+
if (attributeLocation) {
|
|
93
|
+
Object.assign(position, {
|
|
94
|
+
line: attributeLocation.startLine,
|
|
95
|
+
column: attributeLocation.startCol,
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const originalPositionAttributeName =
|
|
100
|
+
attributeName === "generated-from-src"
|
|
101
|
+
? "original-src-position"
|
|
102
|
+
: attributeName === "generated-from-href"
|
|
103
|
+
? "original-href-position"
|
|
104
|
+
: `original-${attributeName}-position`
|
|
105
|
+
const originalPosition = getHtmlNodeAttribute(
|
|
106
|
+
node,
|
|
107
|
+
originalPositionAttributeName,
|
|
108
|
+
)
|
|
109
|
+
if (originalPosition === undefined) {
|
|
110
|
+
return position
|
|
111
|
+
}
|
|
112
|
+
const [start, end] = originalPosition.split(";")
|
|
113
|
+
const [originalLine, originalColumn] = start.split(":")
|
|
114
|
+
const [originalLineEnd, originalColumnEnd] = end.split(":")
|
|
115
|
+
Object.assign(position, {
|
|
116
|
+
originalLine: parseInt(originalLine),
|
|
117
|
+
originalColumn: parseInt(originalColumn),
|
|
118
|
+
originalLineEnd: parseInt(originalLineEnd),
|
|
119
|
+
originalColumnEnd: parseInt(originalColumnEnd),
|
|
120
|
+
})
|
|
121
|
+
return position
|
|
122
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const getHtmlNodeText = (htmlNode) => {
|
|
2
|
+
const textNode = getTextNode(htmlNode)
|
|
3
|
+
return textNode ? textNode.value : undefined
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const getTextNode = (htmlNode) => {
|
|
7
|
+
const firstChild = htmlNode.childNodes[0]
|
|
8
|
+
const textNode =
|
|
9
|
+
firstChild && firstChild.nodeName === "#text" ? firstChild : null
|
|
10
|
+
return textNode
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const removeHtmlNodeText = (htmlNode) => {
|
|
14
|
+
const textNode = getTextNode(htmlNode)
|
|
15
|
+
if (textNode) {
|
|
16
|
+
htmlNode.childNodes = []
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const setHtmlNodeText = (htmlNode, textContent) => {
|
|
21
|
+
const textNode = getTextNode(htmlNode)
|
|
22
|
+
if (textNode) {
|
|
23
|
+
textNode.value = textContent
|
|
24
|
+
} else {
|
|
25
|
+
const newTextNode = {
|
|
26
|
+
nodeName: "#text",
|
|
27
|
+
value: textContent,
|
|
28
|
+
parentNode: htmlNode,
|
|
29
|
+
}
|
|
30
|
+
htmlNode.childNodes.splice(0, 0, newTextNode)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { parse, serialize, parseFragment } from "parse5"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getHtmlNodeAttribute,
|
|
5
|
+
setHtmlNodeAttributes,
|
|
6
|
+
} from "./html_node_attributes.js"
|
|
7
|
+
import {
|
|
8
|
+
storeHtmlNodePosition,
|
|
9
|
+
storeHtmlNodeAttributePosition,
|
|
10
|
+
} from "./html_node_position.js"
|
|
11
|
+
import { findHtmlChildNode, visitHtmlNodes } from "./html_search.js"
|
|
12
|
+
import { getHtmlNodeText } from "./html_node_text.js"
|
|
13
|
+
|
|
14
|
+
export const parseHtmlString = (
|
|
15
|
+
htmlString,
|
|
16
|
+
{ storeOriginalPositions = true } = {},
|
|
17
|
+
) => {
|
|
18
|
+
const htmlAst = parse(htmlString, { sourceCodeLocationInfo: true })
|
|
19
|
+
if (storeOriginalPositions) {
|
|
20
|
+
const htmlNode = findHtmlChildNode(
|
|
21
|
+
htmlAst,
|
|
22
|
+
(node) => node.nodeName === "html",
|
|
23
|
+
)
|
|
24
|
+
const stored = getHtmlNodeAttribute(htmlNode, "original-position-stored")
|
|
25
|
+
if (stored === undefined) {
|
|
26
|
+
visitHtmlNodes(htmlAst, {
|
|
27
|
+
"*": (node) => {
|
|
28
|
+
if (node.nodeName === "script" || node.nodeName === "style") {
|
|
29
|
+
const htmlNodeText = getHtmlNodeText(node)
|
|
30
|
+
if (htmlNodeText !== undefined) {
|
|
31
|
+
storeHtmlNodePosition(node)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
storeHtmlNodeAttributePosition(node, "src")
|
|
35
|
+
storeHtmlNodeAttributePosition(node, "href")
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
setHtmlNodeAttributes(htmlNode, {
|
|
39
|
+
"original-position-stored": "",
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return htmlAst
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const stringifyHtmlAst = (
|
|
47
|
+
htmlAst,
|
|
48
|
+
{ removeOriginalPositionAttributes = false } = {},
|
|
49
|
+
) => {
|
|
50
|
+
if (removeOriginalPositionAttributes) {
|
|
51
|
+
const htmlNode = findHtmlChildNode(
|
|
52
|
+
htmlAst,
|
|
53
|
+
(node) => node.nodeName === "html",
|
|
54
|
+
)
|
|
55
|
+
const storedAttribute = getHtmlNodeAttribute(
|
|
56
|
+
htmlNode,
|
|
57
|
+
"original-position-stored",
|
|
58
|
+
)
|
|
59
|
+
if (storedAttribute !== undefined) {
|
|
60
|
+
setHtmlNodeAttributes(htmlNode, {
|
|
61
|
+
"original-position-stored": undefined,
|
|
62
|
+
})
|
|
63
|
+
visitHtmlNodes(htmlAst, {
|
|
64
|
+
"*": (node) => {
|
|
65
|
+
setHtmlNodeAttributes(node, {
|
|
66
|
+
"original-position": undefined,
|
|
67
|
+
"original-src-position": undefined,
|
|
68
|
+
"original-href-position": undefined,
|
|
69
|
+
"injected-by": undefined,
|
|
70
|
+
"generated-by": undefined,
|
|
71
|
+
"generated-from-src": undefined,
|
|
72
|
+
"generated-from-href": undefined,
|
|
73
|
+
})
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const htmlString = serialize(htmlAst)
|
|
79
|
+
|
|
80
|
+
return htmlString
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const parseSvgString = (svgString) => {
|
|
84
|
+
const svgAst = parseFragment(svgString, {
|
|
85
|
+
sourceCodeLocationInfo: true,
|
|
86
|
+
})
|
|
87
|
+
return svgAst
|
|
88
|
+
}
|
|
89
|
+
export const stringifySvgAst = stringifyHtmlAst
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { getHtmlNodeAttribute } from "./html_node_attributes.js"
|
|
2
|
+
|
|
3
|
+
export const visitHtmlNodes = (htmlAst, visitors) => {
|
|
4
|
+
const visitNode = (node) => {
|
|
5
|
+
const visitor = visitors[node.nodeName] || visitors["*"]
|
|
6
|
+
if (visitor) {
|
|
7
|
+
const callbackReturnValue = visitor(node)
|
|
8
|
+
if (callbackReturnValue === "stop") {
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
const { childNodes } = node
|
|
13
|
+
if (childNodes) {
|
|
14
|
+
let i = 0
|
|
15
|
+
while (i < childNodes.length) {
|
|
16
|
+
visitNode(childNodes[i++])
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
visitNode(htmlAst)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const findHtmlNode = (htmlAst, predicate) => {
|
|
24
|
+
let nodeMatching = null
|
|
25
|
+
visitHtmlNodes(htmlAst, {
|
|
26
|
+
"*": (node) => {
|
|
27
|
+
if (predicate(node)) {
|
|
28
|
+
nodeMatching = node
|
|
29
|
+
return "stop"
|
|
30
|
+
}
|
|
31
|
+
return null
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
return nodeMatching
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const findHtmlChildNode = (htmlNode, predicate) => {
|
|
38
|
+
const { childNodes = [] } = htmlNode
|
|
39
|
+
return childNodes.find(predicate)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const findHtmlNodes = (htmlAst, predicate) => {
|
|
43
|
+
const nodes = []
|
|
44
|
+
visitHtmlNodes(htmlAst, {
|
|
45
|
+
"*": (node) => {
|
|
46
|
+
if (predicate(node)) {
|
|
47
|
+
nodes.push(node)
|
|
48
|
+
}
|
|
49
|
+
return null
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
return nodes
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const findHtmlNodeByTagName = (htmlAst, tagName) => {
|
|
56
|
+
return findHtmlNode(htmlAst, (node) => node.nodeName === tagName)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const findHtmlNodeById = (htmlAst, id) => {
|
|
60
|
+
return findHtmlNode(htmlAst, (node) => {
|
|
61
|
+
const idCandidate = getHtmlNodeAttribute(node, "id")
|
|
62
|
+
return idCandidate === id
|
|
63
|
+
})
|
|
64
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const parseSrcSet = (srcset) => {
|
|
2
|
+
const srcCandidates = []
|
|
3
|
+
srcset.split(",").forEach((set) => {
|
|
4
|
+
const [specifier, descriptor] = set.trim().split(" ")
|
|
5
|
+
srcCandidates.push({
|
|
6
|
+
specifier,
|
|
7
|
+
descriptor,
|
|
8
|
+
})
|
|
9
|
+
})
|
|
10
|
+
return srcCandidates
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const stringifySrcSet = (srcCandidates) => {
|
|
14
|
+
const srcset = srcCandidates
|
|
15
|
+
.map(({ specifier, descriptor }) => `${specifier} ${descriptor}`)
|
|
16
|
+
.join(", ")
|
|
17
|
+
return srcset
|
|
18
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Useful when writin a babel plugin:
|
|
3
|
+
* - https://astexplorer.net/
|
|
4
|
+
* - https://bvaughn.github.io/babel-repl
|
|
5
|
+
*/
|
|
6
|
+
import { fileURLToPath } from "node:url"
|
|
7
|
+
|
|
8
|
+
import { createJsParseError } from "./js_parse_error.js"
|
|
9
|
+
|
|
10
|
+
export const applyBabelPlugins = async ({
|
|
11
|
+
babelPlugins,
|
|
12
|
+
urlInfo,
|
|
13
|
+
ast,
|
|
14
|
+
options = {},
|
|
15
|
+
}) => {
|
|
16
|
+
const sourceType = {
|
|
17
|
+
js_module: "module",
|
|
18
|
+
js_classic: "classic",
|
|
19
|
+
[urlInfo.type]: undefined,
|
|
20
|
+
}[urlInfo.type]
|
|
21
|
+
const url = urlInfo.originalUrl
|
|
22
|
+
const generatedUrl = urlInfo.generatedUrl
|
|
23
|
+
const content = urlInfo.content
|
|
24
|
+
|
|
25
|
+
if (babelPlugins.length === 0) {
|
|
26
|
+
return { code: content }
|
|
27
|
+
}
|
|
28
|
+
const { transformAsync, transformFromAstAsync } = await import("@babel/core")
|
|
29
|
+
const sourceFileName = url.startsWith("file:")
|
|
30
|
+
? fileURLToPath(url)
|
|
31
|
+
: undefined
|
|
32
|
+
options = {
|
|
33
|
+
ast: false,
|
|
34
|
+
// https://babeljs.io/docs/en/options#source-map-options
|
|
35
|
+
sourceMaps: true,
|
|
36
|
+
sourceFileName,
|
|
37
|
+
filename: generatedUrl
|
|
38
|
+
? generatedUrl.startsWith("file:")
|
|
39
|
+
? fileURLToPath(url)
|
|
40
|
+
: undefined
|
|
41
|
+
: sourceFileName,
|
|
42
|
+
configFile: false,
|
|
43
|
+
babelrc: false,
|
|
44
|
+
highlightCode: false,
|
|
45
|
+
// consider using startColumn and startLine for inline scripts?
|
|
46
|
+
// see https://github.com/babel/babel/blob/3ee9db7afe741f4d2f7933c519d8e7672fccb08d/packages/babel-parser/src/options.js#L36-L39
|
|
47
|
+
parserOpts: {
|
|
48
|
+
sourceType,
|
|
49
|
+
// allowAwaitOutsideFunction: true,
|
|
50
|
+
plugins: [
|
|
51
|
+
// "importMeta",
|
|
52
|
+
// "topLevelAwait",
|
|
53
|
+
"dynamicImport",
|
|
54
|
+
"importAssertions",
|
|
55
|
+
"jsx",
|
|
56
|
+
"classProperties",
|
|
57
|
+
"classPrivateProperties",
|
|
58
|
+
"classPrivateMethods",
|
|
59
|
+
...(useTypeScriptExtension(url) ? ["typescript"] : []),
|
|
60
|
+
...(options.parserPlugins || []),
|
|
61
|
+
].filter(Boolean),
|
|
62
|
+
},
|
|
63
|
+
generatorOpts: {
|
|
64
|
+
compact: false,
|
|
65
|
+
},
|
|
66
|
+
plugins: babelPlugins,
|
|
67
|
+
...options,
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
if (ast) {
|
|
71
|
+
const result = await transformFromAstAsync(ast, content, options)
|
|
72
|
+
return result
|
|
73
|
+
}
|
|
74
|
+
const result = await transformAsync(content, options)
|
|
75
|
+
return result
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (error && error.code === "BABEL_PARSE_ERROR") {
|
|
78
|
+
throw createJsParseError({
|
|
79
|
+
message: error.message,
|
|
80
|
+
reasonCode: error.reasonCode,
|
|
81
|
+
content,
|
|
82
|
+
url,
|
|
83
|
+
line: error.loc.line,
|
|
84
|
+
column: error.loc.column,
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
throw error
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const useTypeScriptExtension = (url) => {
|
|
92
|
+
const { pathname } = new URL(url)
|
|
93
|
+
return pathname.endsWith(".ts") || pathname.endsWith(".tsx")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// const pattern = [
|
|
97
|
+
// "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
|
|
98
|
+
// "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))",
|
|
99
|
+
// ].join("|")
|
|
100
|
+
// const ansiRegex = new RegExp(pattern, "g")
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { require } from "../require.js"
|
|
2
|
+
|
|
3
|
+
// https://github.com/babel/babel/tree/master/packages/babel-helper-module-imports
|
|
4
|
+
export const injectJsImport = ({
|
|
5
|
+
programPath,
|
|
6
|
+
namespace,
|
|
7
|
+
name,
|
|
8
|
+
from,
|
|
9
|
+
nameHint,
|
|
10
|
+
sideEffect,
|
|
11
|
+
}) => {
|
|
12
|
+
const {
|
|
13
|
+
addNamespace,
|
|
14
|
+
addDefault,
|
|
15
|
+
addNamed,
|
|
16
|
+
addSideEffect,
|
|
17
|
+
} = require("@babel/helper-module-imports")
|
|
18
|
+
|
|
19
|
+
if (namespace) {
|
|
20
|
+
return addNamespace(programPath, from, {
|
|
21
|
+
nameHint,
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
if (name) {
|
|
25
|
+
return addNamed(programPath, name, from)
|
|
26
|
+
}
|
|
27
|
+
if (sideEffect) {
|
|
28
|
+
return addSideEffect(programPath, from)
|
|
29
|
+
}
|
|
30
|
+
return addDefault(programPath, from, {
|
|
31
|
+
nameHint,
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const generateExpressionAst = (expression, options) => {
|
|
36
|
+
const { parseExpression } = require("@babel/parser")
|
|
37
|
+
|
|
38
|
+
const ast = parseExpression(expression, options)
|
|
39
|
+
return ast
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const generateValueAst = (value) => {
|
|
43
|
+
const valueAst = generateExpressionAst(
|
|
44
|
+
value === undefined
|
|
45
|
+
? "undefined"
|
|
46
|
+
: value === null
|
|
47
|
+
? "null"
|
|
48
|
+
: JSON.stringify(value),
|
|
49
|
+
)
|
|
50
|
+
return valueAst
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const injectAstAfterImport = (programPath, ast) => {
|
|
54
|
+
const bodyNodePaths = programPath.get("body")
|
|
55
|
+
const notAnImportIndex = bodyNodePaths.findIndex(
|
|
56
|
+
(bodyNodePath) => bodyNodePath.node.type !== "ImportDeclaration",
|
|
57
|
+
)
|
|
58
|
+
const notAnImportNodePath = bodyNodePaths[notAnImportIndex]
|
|
59
|
+
if (notAnImportNodePath) {
|
|
60
|
+
notAnImportNodePath.insertBefore(ast)
|
|
61
|
+
} else {
|
|
62
|
+
bodyNodePaths[0].insertBefore(ast)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const createJsParseError = ({
|
|
2
|
+
message,
|
|
3
|
+
reasonCode,
|
|
4
|
+
url,
|
|
5
|
+
line,
|
|
6
|
+
column,
|
|
7
|
+
}) => {
|
|
8
|
+
const parseError = new Error(message)
|
|
9
|
+
parseError.reasonCode = reasonCode
|
|
10
|
+
parseError.code = "PARSE_ERROR"
|
|
11
|
+
parseError.url = url
|
|
12
|
+
parseError.line = line
|
|
13
|
+
parseError.column = column
|
|
14
|
+
return parseError
|
|
15
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const getTypePropertyNode = (node) => {
|
|
2
|
+
if (node.type !== "ObjectExpression") {
|
|
3
|
+
return null
|
|
4
|
+
}
|
|
5
|
+
const { properties } = node
|
|
6
|
+
return properties.find((property) => {
|
|
7
|
+
return (
|
|
8
|
+
property.type === "Property" &&
|
|
9
|
+
property.key.type === "Identifier" &&
|
|
10
|
+
property.key.name === "type"
|
|
11
|
+
)
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const isStringLiteralNode = (node) => {
|
|
16
|
+
return node.type === "Literal" && typeof node.value === "string"
|
|
17
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { isStringLiteralNode } from "./helpers.js"
|
|
2
|
+
|
|
3
|
+
export const analyzeImportDeclaration = (node, { onUrl }) => {
|
|
4
|
+
const specifierNode = node.source
|
|
5
|
+
const assertionInfo = extractImportAssertionsInfo(node)
|
|
6
|
+
onUrl({
|
|
7
|
+
type: "js_import_export",
|
|
8
|
+
subtype: "import_static",
|
|
9
|
+
specifier: specifierNode.value,
|
|
10
|
+
specifierStart: specifierNode.start,
|
|
11
|
+
specifierEnd: specifierNode.end,
|
|
12
|
+
specifierLine: specifierNode.loc.start.line,
|
|
13
|
+
specifierColumn: specifierNode.loc.start.column,
|
|
14
|
+
expectedType: assertionInfo ? assertionInfo.assert.type : "js_module",
|
|
15
|
+
...assertionInfo,
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
export const analyzeImportExpression = (node, { onUrl }) => {
|
|
19
|
+
const specifierNode = node.source
|
|
20
|
+
if (!isStringLiteralNode(specifierNode)) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
const assertionInfo = extractImportAssertionsInfo(node)
|
|
24
|
+
|
|
25
|
+
onUrl({
|
|
26
|
+
type: "js_import_export",
|
|
27
|
+
subtype: "import_dynamic",
|
|
28
|
+
specifier: specifierNode.value,
|
|
29
|
+
specifierStart: specifierNode.start,
|
|
30
|
+
specifierEnd: specifierNode.end,
|
|
31
|
+
specifierLine: specifierNode.loc.start.line,
|
|
32
|
+
specifierColumn: specifierNode.loc.start.column,
|
|
33
|
+
expectedType: assertionInfo ? assertionInfo.assert.type : "js_module",
|
|
34
|
+
...assertionInfo,
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
export const analyzeExportNamedDeclaration = (node, { onUrl }) => {
|
|
38
|
+
const specifierNode = node.source
|
|
39
|
+
if (!specifierNode) {
|
|
40
|
+
// This export has no "source", so it's probably
|
|
41
|
+
// a local variable or function, e.g.
|
|
42
|
+
// export { varName }
|
|
43
|
+
// export const constName = ...
|
|
44
|
+
// export function funcName() {}
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
onUrl({
|
|
48
|
+
type: "js_import_export",
|
|
49
|
+
subtype: "export_named",
|
|
50
|
+
specifier: specifierNode.value,
|
|
51
|
+
specifierStart: specifierNode.start,
|
|
52
|
+
specifierEnd: specifierNode.end,
|
|
53
|
+
specifierLine: specifierNode.loc.start.line,
|
|
54
|
+
specifierColumn: specifierNode.loc.start.column,
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
export const analyzeExportAllDeclaration = (node, { onUrl }) => {
|
|
58
|
+
const specifierNode = node.source
|
|
59
|
+
onUrl({
|
|
60
|
+
type: "js_import_export",
|
|
61
|
+
subtype: "export_all",
|
|
62
|
+
specifier: specifierNode.value,
|
|
63
|
+
specifierStart: specifierNode.start,
|
|
64
|
+
specifierEnd: specifierNode.end,
|
|
65
|
+
specifierLine: specifierNode.loc.start.line,
|
|
66
|
+
specifierColumn: specifierNode.loc.start.column,
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const extractImportAssertionsInfo = (node) => {
|
|
71
|
+
if (node.type === "ImportDeclaration") {
|
|
72
|
+
// static import
|
|
73
|
+
const { assertions } = node
|
|
74
|
+
if (!assertions) {
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
if (assertions.length === 0) {
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
const typeAssertionNode = assertions.find(
|
|
81
|
+
(assertion) => assertion.key.name === "type",
|
|
82
|
+
)
|
|
83
|
+
if (!typeAssertionNode) {
|
|
84
|
+
return null
|
|
85
|
+
}
|
|
86
|
+
const typeNode = typeAssertionNode.value
|
|
87
|
+
if (!isStringLiteralNode(typeNode)) {
|
|
88
|
+
return null
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
assertNode: typeAssertionNode,
|
|
92
|
+
assert: {
|
|
93
|
+
type: typeNode.value,
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// dynamic import
|
|
98
|
+
const args = node.arguments
|
|
99
|
+
if (!args) {
|
|
100
|
+
// acorn keeps node.arguments undefined for dynamic import without a second argument
|
|
101
|
+
return null
|
|
102
|
+
}
|
|
103
|
+
const firstArgNode = args[0]
|
|
104
|
+
if (!firstArgNode) {
|
|
105
|
+
return null
|
|
106
|
+
}
|
|
107
|
+
const { properties } = firstArgNode
|
|
108
|
+
const assertProperty = properties.find((property) => {
|
|
109
|
+
return property.key.name === "assert"
|
|
110
|
+
})
|
|
111
|
+
if (!assertProperty) {
|
|
112
|
+
return null
|
|
113
|
+
}
|
|
114
|
+
const assertValueNode = assertProperty.value
|
|
115
|
+
if (assertValueNode.type !== "ObjectExpression") {
|
|
116
|
+
return null
|
|
117
|
+
}
|
|
118
|
+
const assertValueProperties = assertValueNode.properties
|
|
119
|
+
const typePropertyNode = assertValueProperties.find((property) => {
|
|
120
|
+
return property.key.name === "type"
|
|
121
|
+
})
|
|
122
|
+
if (!typePropertyNode) {
|
|
123
|
+
return null
|
|
124
|
+
}
|
|
125
|
+
const typePropertyValue = typePropertyNode.value
|
|
126
|
+
if (!isStringLiteralNode(typePropertyValue)) {
|
|
127
|
+
return null
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
assertNode: firstArgNode,
|
|
131
|
+
assert: {
|
|
132
|
+
type: typePropertyValue.value,
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
}
|