@jsenv/ast 3.0.8 → 4.0.2

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 CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@jsenv/ast",
3
- "version": "3.0.8",
3
+ "version": "4.0.2",
4
4
  "description": "Parse and transform HTML,CSS and JS",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/jsenv/core",
9
- "directory": "packages/ast"
9
+ "directory": "packages/rest/ast"
10
10
  },
11
11
  "publishConfig": {
12
12
  "access": "public"
@@ -35,7 +35,7 @@
35
35
  "dependencies": {
36
36
  "@babel/parser": "7.21.8",
37
37
  "acorn": "8.8.2",
38
- "acorn-import-assertions": "1.8.0",
38
+ "acorn-import-assertions": "1.9.0",
39
39
  "acorn-walk": "8.2.0",
40
40
  "parse5": "7.1.2",
41
41
  "postcss": "8.4.23",
@@ -61,7 +61,11 @@ export const parseHtmlString = (
61
61
 
62
62
  export const stringifyHtmlAst = (
63
63
  htmlAst,
64
- { cleanupJsenvAttributes = false, cleanupPositionAttributes = false } = {},
64
+ {
65
+ preserveLineBreaks = true,
66
+ cleanupJsenvAttributes = false,
67
+ cleanupPositionAttributes = false,
68
+ } = {},
65
69
  ) => {
66
70
  if (cleanupJsenvAttributes || cleanupPositionAttributes) {
67
71
  const htmlNode = findHtmlChildNode(
@@ -98,8 +102,10 @@ export const stringifyHtmlAst = (
98
102
  });
99
103
  }
100
104
  }
101
- // ensure body and html have \n
102
- ensureLineBreaksBetweenHtmlNodes(htmlAst);
105
+ if (preserveLineBreaks) {
106
+ // ensure body and html have \n
107
+ ensureLineBreaksBetweenHtmlNodes(htmlAst);
108
+ }
103
109
  const htmlString = serialize(htmlAst);
104
110
  return htmlString;
105
111
  };
@@ -15,3 +15,26 @@ export const getTypePropertyNode = (node) => {
15
15
  export const isStringLiteralNode = (node) => {
16
16
  return node.type === "Literal" && typeof node.value === "string";
17
17
  };
18
+
19
+ export const extractContentInfo = (node) => {
20
+ if (isStringLiteralNode(node)) {
21
+ return {
22
+ nodeType: "StringLiteral",
23
+ quote: node.raw[0],
24
+ content: node.value,
25
+ };
26
+ }
27
+ if (node.type === "TemplateLiteral") {
28
+ const quasis = node.quasis;
29
+ if (quasis.length !== 1) {
30
+ return null;
31
+ }
32
+ const templateElementNode = quasis[0];
33
+ return {
34
+ nodeType: "TemplateLiteral",
35
+ quote: "`",
36
+ content: templateElementNode.value.cooked,
37
+ };
38
+ }
39
+ return null;
40
+ };
@@ -0,0 +1,33 @@
1
+ import { extractContentInfo } from "./helpers.js";
2
+
3
+ export const isJSONParseCall = (node) => {
4
+ const callee = node.callee;
5
+ return (
6
+ callee.type === "MemberExpression" &&
7
+ callee.object.type === "Identifier" &&
8
+ callee.object.name === "JSON" &&
9
+ callee.property.type === "Identifier" &&
10
+ callee.property.name === "parse"
11
+ );
12
+ };
13
+
14
+ export const analyzeJSONParseCall = (node, { onInlineContent }) => {
15
+ const nodeHoldingContent = node.arguments[0];
16
+ const contentInfo = extractContentInfo(nodeHoldingContent);
17
+ if (contentInfo) {
18
+ onInlineContent({
19
+ node: nodeHoldingContent,
20
+ type: "json_parse_first_arg",
21
+ contentType: "application/json",
22
+ start: nodeHoldingContent.start,
23
+ end: nodeHoldingContent.end,
24
+ line: nodeHoldingContent.loc.start.line,
25
+ column: nodeHoldingContent.loc.start.column,
26
+ lineEnd: nodeHoldingContent.loc.end.line,
27
+ columnEnd: nodeHoldingContent.loc.end.column,
28
+ nodeType: contentInfo.nodeType,
29
+ quote: contentInfo.quote,
30
+ content: contentInfo.content,
31
+ });
32
+ }
33
+ };
@@ -0,0 +1,55 @@
1
+ import {
2
+ isStringLiteralNode,
3
+ getTypePropertyNode,
4
+ extractContentInfo,
5
+ } from "./helpers.js";
6
+
7
+ export const isNewBlobCall = (node) => {
8
+ return (
9
+ node.type === "NewExpression" &&
10
+ node.callee.type === "Identifier" &&
11
+ node.callee.name === "Blob"
12
+ );
13
+ };
14
+ export const analyzeNewBlobCall = (node, { onInlineContent }) => {
15
+ const [firstArg, secondArg] = node.arguments;
16
+ if (!firstArg) {
17
+ return;
18
+ }
19
+ if (!secondArg) {
20
+ return;
21
+ }
22
+ if (firstArg.type !== "ArrayExpression") {
23
+ return;
24
+ }
25
+ if (firstArg.elements.length !== 1) {
26
+ return;
27
+ }
28
+ const typePropertyNode = getTypePropertyNode(secondArg);
29
+ if (!typePropertyNode) {
30
+ return;
31
+ }
32
+ const typePropertyValueNode = typePropertyNode.value;
33
+ if (!isStringLiteralNode(typePropertyValueNode)) {
34
+ return;
35
+ }
36
+ const nodeHoldingContent = firstArg.elements[0];
37
+ const contentType = typePropertyValueNode.value;
38
+ const contentInfo = extractContentInfo(nodeHoldingContent);
39
+ if (contentInfo) {
40
+ onInlineContent({
41
+ node: nodeHoldingContent,
42
+ type: "new_blob_first_arg",
43
+ contentType,
44
+ start: nodeHoldingContent.start,
45
+ end: nodeHoldingContent.end,
46
+ line: nodeHoldingContent.loc.start.line,
47
+ column: nodeHoldingContent.loc.start.column,
48
+ lineEnd: nodeHoldingContent.loc.end.line,
49
+ columnEnd: nodeHoldingContent.loc.end.column,
50
+ nodeType: contentInfo.nodeType,
51
+ quote: contentInfo.quote,
52
+ content: contentInfo.content,
53
+ });
54
+ }
55
+ };
@@ -0,0 +1,49 @@
1
+ import {
2
+ isStringLiteralNode,
3
+ getTypePropertyNode,
4
+ extractContentInfo,
5
+ } from "./helpers.js";
6
+
7
+ export const isNewInlineContentCall = (node) => {
8
+ return (
9
+ node.type === "NewExpression" &&
10
+ node.callee.type === "Identifier" &&
11
+ node.callee.name === "__InlineContent__"
12
+ );
13
+ };
14
+ export const analyzeNewInlineContentCall = (node, { onInlineContent }) => {
15
+ const [firstArg, secondArg] = node.arguments;
16
+ if (!firstArg) {
17
+ return;
18
+ }
19
+ if (!secondArg) {
20
+ return;
21
+ }
22
+ const typePropertyNode = getTypePropertyNode(secondArg);
23
+ if (!typePropertyNode) {
24
+ return;
25
+ }
26
+ const typePropertyValueNode = typePropertyNode.value;
27
+ if (!isStringLiteralNode(typePropertyValueNode)) {
28
+ return;
29
+ }
30
+ const nodeHoldingContent = firstArg;
31
+ const contentType = typePropertyValueNode.value;
32
+ const contentInfo = extractContentInfo(nodeHoldingContent);
33
+ if (contentInfo) {
34
+ onInlineContent({
35
+ node: nodeHoldingContent,
36
+ type: "new_inline_content_first_arg",
37
+ contentType,
38
+ start: nodeHoldingContent.start,
39
+ end: nodeHoldingContent.end,
40
+ line: nodeHoldingContent.loc.start.line,
41
+ column: nodeHoldingContent.loc.start.column,
42
+ lineEnd: nodeHoldingContent.loc.end.line,
43
+ columnEnd: nodeHoldingContent.loc.end.column,
44
+ nodeType: contentInfo.nodeType,
45
+ quote: contentInfo.quote,
46
+ content: contentInfo.content,
47
+ });
48
+ }
49
+ };
@@ -35,12 +35,25 @@ import {
35
35
  isSystemResolveCall,
36
36
  analyzeSystemResolveCall,
37
37
  } from "./js_static_analysis/system.js";
38
+ import {
39
+ isNewBlobCall,
40
+ analyzeNewBlobCall,
41
+ } from "./js_static_analysis/new_blob.js";
42
+ import {
43
+ isNewInlineContentCall,
44
+ analyzeNewInlineContentCall,
45
+ } from "./js_static_analysis/new_inline_content.js";
46
+ import {
47
+ isJSONParseCall,
48
+ analyzeJSONParseCall,
49
+ } from "./js_static_analysis/json_parse.js";
38
50
 
39
51
  export const parseJsUrls = async ({
40
52
  js,
41
53
  url,
42
54
  isJsModule = false,
43
55
  isWebWorker = false,
56
+ inlineContent = true,
44
57
  } = {}) => {
45
58
  const jsUrls = [];
46
59
  const jsAst = await parseJsWithAcorn({
@@ -51,6 +64,12 @@ export const parseJsUrls = async ({
51
64
  const onUrl = (jsUrl) => {
52
65
  jsUrls.push(jsUrl);
53
66
  };
67
+ const onInlineContent = (inlineContentInfo) => {
68
+ jsUrls.push({
69
+ isInline: true,
70
+ ...inlineContentInfo,
71
+ });
72
+ };
54
73
  ancestor(jsAst, {
55
74
  ImportDeclaration: (node) => {
56
75
  analyzeImportDeclaration(node, { onUrl });
@@ -100,6 +119,12 @@ export const parseJsUrls = async ({
100
119
  });
101
120
  return;
102
121
  }
122
+ if (inlineContent && isJSONParseCall(node)) {
123
+ analyzeJSONParseCall(node, {
124
+ onInlineContent,
125
+ });
126
+ return;
127
+ }
103
128
  },
104
129
  NewExpression: (node, ancestors) => {
105
130
  if (isNewWorkerCall(node)) {
@@ -132,6 +157,18 @@ export const parseJsUrls = async ({
132
157
  });
133
158
  return;
134
159
  }
160
+ if (inlineContent && isNewInlineContentCall(node)) {
161
+ analyzeNewInlineContentCall(node, {
162
+ onInlineContent,
163
+ });
164
+ return;
165
+ }
166
+ if (inlineContent && isNewBlobCall(node)) {
167
+ analyzeNewBlobCall(node, {
168
+ onInlineContent,
169
+ });
170
+ return;
171
+ }
135
172
  },
136
173
  });
137
174
  return jsUrls;