@jsenv/ast 3.0.6 → 3.0.8

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.
@@ -1,22 +1,22 @@
1
1
  import {
2
2
  getHtmlNodeAttribute,
3
3
  setHtmlNodeAttributes,
4
- } from "./html_node_attributes.js"
5
- import { setHtmlNodeText } from "./html_node_text.js"
4
+ } from "./html_node_attributes.js";
5
+ import { setHtmlNodeText } from "./html_node_text.js";
6
6
 
7
7
  export const inlineScriptNode = (script, textContent) => {
8
- const src = getHtmlNodeAttribute(script, "src")
8
+ const src = getHtmlNodeAttribute(script, "src");
9
9
  setHtmlNodeAttributes(script, {
10
10
  "inlined-from-src": src,
11
11
  "src": undefined,
12
12
  "crossorigin": undefined,
13
13
  "integrity": undefined,
14
- })
15
- setHtmlNodeText(script, textContent)
16
- }
14
+ });
15
+ setHtmlNodeText(script, textContent);
16
+ };
17
17
 
18
18
  export const inlineLinkStylesheetNode = (link, textContent) => {
19
- const href = getHtmlNodeAttribute(link, "href")
19
+ const href = getHtmlNodeAttribute(link, "href");
20
20
  setHtmlNodeAttributes(link, {
21
21
  "inlined-from-href": href,
22
22
  "href": undefined,
@@ -25,16 +25,16 @@ export const inlineLinkStylesheetNode = (link, textContent) => {
25
25
  "as": undefined,
26
26
  "crossorigin": undefined,
27
27
  "integrity": undefined,
28
- })
29
- link.nodeName = "style"
30
- link.tagName = "style"
31
- setHtmlNodeText(link, textContent)
32
- }
28
+ });
29
+ link.nodeName = "style";
30
+ link.tagName = "style";
31
+ setHtmlNodeText(link, textContent);
32
+ };
33
33
 
34
34
  export const inlineImgNode = (img, contentAsBase64) => {
35
- const src = getHtmlNodeAttribute(img, "src")
35
+ const src = getHtmlNodeAttribute(img, "src");
36
36
  setHtmlNodeAttributes(img, {
37
37
  "inlined-from-src": src,
38
38
  "src": contentAsBase64,
39
- })
40
- }
39
+ });
40
+ };
@@ -1,157 +1,192 @@
1
- import { parseFragment } from "parse5"
1
+ import { parseFragment } from "parse5";
2
2
 
3
- import { setHtmlNodeAttributes } from "./html_node_attributes.js"
4
- import { findHtmlNode } from "./html_search.js"
5
- import { analyzeScriptNode } from "./html_analysis.js"
3
+ import { setHtmlNodeAttributes } from "./html_node_attributes.js";
4
+ import { findHtmlNode } from "./html_search.js";
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
- const { childNodes } = htmlNode.parentNode
9
- childNodes.splice(childNodes.indexOf(htmlNode), 1)
10
- }
14
+ const { childNodes } = htmlNode.parentNode;
15
+ childNodes.splice(childNodes.indexOf(htmlNode), 1);
16
+ };
11
17
 
12
18
  export const createHtmlNode = ({ tagName, textContent = "", ...rest }) => {
13
19
  const html = `<${tagName} ${stringifyAttributes(
14
20
  rest,
15
- )}>${textContent}</${tagName}>`
16
- const fragment = parseFragment(html)
17
- return fragment.childNodes[0]
18
- }
21
+ )}>${textContent}</${tagName}>`;
22
+ const fragment = parseFragment(html);
23
+ return fragment.childNodes[0];
24
+ };
19
25
 
20
26
  export const injectHtmlNode = (htmlAst, node, jsenvPluginName = "jsenv") => {
21
27
  setHtmlNodeAttributes(node, {
22
28
  "jsenv-injected-by": jsenvPluginName,
23
- })
24
- const htmlHtmlNode = findChild(htmlAst, (node) => node.nodeName === "html")
25
- const bodyNode = findChild(htmlHtmlNode, (node) => node.nodeName === "body")
26
- return insertHtmlNodeAfter(node, bodyNode)
27
- }
29
+ });
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
+ if (after) {
44
+ insertHtmlNodeAfter(node, after);
45
+ } else {
46
+ injectWithWhitespaces(node, bodyNode, 0);
47
+ }
48
+ };
28
49
 
29
- export const injectScriptNodeAsEarlyAsPossible = (
50
+ export const injectHtmlNodeAsEarlyAsPossible = (
30
51
  htmlAst,
31
- scriptNode,
52
+ node,
32
53
  jsenvPluginName = "jsenv",
33
54
  ) => {
34
- setHtmlNodeAttributes(scriptNode, {
55
+ setHtmlNodeAttributes(node, {
35
56
  "jsenv-injected-by": jsenvPluginName,
36
- })
37
- const isJsModule = analyzeScriptNode(scriptNode).type === "js_module"
38
- if (isJsModule) {
39
- const firstImportmapScript = findHtmlNode(htmlAst, (node) => {
40
- return (
41
- node.nodeName === "script" &&
42
- analyzeScriptNode(node).type === "importmap"
43
- )
44
- })
45
-
46
- if (firstImportmapScript) {
47
- const importmapParent = firstImportmapScript.parentNode
48
- const importmapSiblings = importmapParent.childNodes
49
- const nextSiblings = importmapSiblings.slice(
50
- importmapSiblings.indexOf(firstImportmapScript) + 1,
51
- )
52
- let after = firstImportmapScript
53
- for (const nextSibling of nextSiblings) {
54
- if (nextSibling.nodeName === "script") {
55
- return insertHtmlNodeBefore(scriptNode, importmapParent, nextSibling)
56
- }
57
- if (nextSibling.nodeName === "link") {
58
- after = nextSibling
57
+ });
58
+ const isScript = node.nodeName === "script";
59
+
60
+ if (isScript) {
61
+ const isJsModule = analyzeScriptNode(node).type === "js_module";
62
+ if (isJsModule) {
63
+ const firstImportmapScript = findHtmlNode(htmlAst, (node) => {
64
+ return (
65
+ node.nodeName === "script" &&
66
+ analyzeScriptNode(node).type === "importmap"
67
+ );
68
+ });
69
+
70
+ if (firstImportmapScript) {
71
+ const importmapParent = firstImportmapScript.parentNode;
72
+ const importmapSiblings = importmapParent.childNodes;
73
+ const nextSiblings = importmapSiblings.slice(
74
+ importmapSiblings.indexOf(firstImportmapScript) + 1,
75
+ );
76
+ let after = firstImportmapScript;
77
+ for (const nextSibling of nextSiblings) {
78
+ if (nextSibling.nodeName === "script") {
79
+ insertHtmlNodeBefore(node, nextSibling);
80
+ return;
81
+ }
82
+ if (nextSibling.nodeName === "link") {
83
+ after = nextSibling;
84
+ }
59
85
  }
86
+ insertHtmlNodeAfter(node, after);
87
+ return;
60
88
  }
61
- return insertHtmlNodeAfter(scriptNode, importmapParent, after)
62
89
  }
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)
90
+ const headNode = findChild(htmlAst, (node) => node.nodeName === "html")
91
+ .childNodes[0];
92
+ let after = headNode.childNodes[0];
93
+ for (const child of headNode.childNodes) {
94
+ if (child.nodeName === "script") {
95
+ insertHtmlNodeBefore(node, child);
96
+ return;
97
+ }
98
+ if (child.nodeName === "link") {
99
+ after = child;
100
+ }
70
101
  }
71
- if (child.nodeName === "link") {
72
- after = child
102
+ if (after) {
103
+ insertHtmlNodeAfter(node, after);
73
104
  }
105
+ return;
74
106
  }
75
- return insertHtmlNodeAfter(scriptNode, headNode, after)
76
- }
77
107
 
78
- export const insertHtmlNodeBefore = (
79
- nodeToInsert,
80
- futureParentNode,
81
- futureNextSibling,
82
- ) => {
83
- const { childNodes = [] } = futureParentNode
84
- const futureIndex = futureNextSibling
85
- ? childNodes.indexOf(futureNextSibling)
86
- : 0
87
- injectWithWhitespaces(nodeToInsert, futureParentNode, futureIndex)
88
- }
89
-
90
- export const insertHtmlNodeAfter = (
91
- nodeToInsert,
92
- futureParentNode,
93
- futurePrevSibling,
94
- ) => {
95
- const { childNodes = [] } = futureParentNode
96
- const futureIndex = futurePrevSibling
97
- ? childNodes.indexOf(futurePrevSibling) + 1
98
- : childNodes.length
99
- injectWithWhitespaces(nodeToInsert, futureParentNode, futureIndex)
100
- }
108
+ injectHtmlNode(htmlAst, node);
109
+ return;
110
+ };
111
+
112
+ export const insertHtmlNodeBefore = (nodeToInsert, futureNextSibling) => {
113
+ const futureParentNode = futureNextSibling.parentNode;
114
+ const { childNodes = [] } = futureParentNode;
115
+ const futureIndex = childNodes.indexOf(futureNextSibling);
116
+ injectWithWhitespaces(nodeToInsert, futureParentNode, futureIndex);
117
+ };
118
+
119
+ export const insertHtmlNodeAfter = (nodeToInsert, futurePrevSibling) => {
120
+ const futureParentNode = futurePrevSibling.parentNode;
121
+ const { childNodes = [] } = futureParentNode;
122
+ const futureIndex = childNodes.indexOf(futurePrevSibling) + 1;
123
+ injectWithWhitespaces(nodeToInsert, futureParentNode, futureIndex);
124
+ };
101
125
 
102
126
  const injectWithWhitespaces = (nodeToInsert, futureParentNode, futureIndex) => {
103
- const { childNodes = [] } = futureParentNode
104
- const previousSiblings = childNodes.slice(0, futureIndex)
105
- const nextSiblings = childNodes.slice(futureIndex)
106
- const futureChildNodes = []
107
- const previousSibling = previousSiblings[previousSiblings.length - 1]
108
- if (previousSibling) {
109
- futureChildNodes.push(...previousSiblings)
127
+ const { childNodes = [] } = futureParentNode;
128
+ const previousSiblings = childNodes.slice(0, futureIndex);
129
+ const nextSiblings = childNodes.slice(futureIndex);
130
+ const futureChildNodes = [];
131
+
132
+ if (previousSiblings.length) {
133
+ futureChildNodes.push(...previousSiblings);
110
134
  }
111
- if (!previousSibling || !isWhitespaceNode(previousSibling)) {
135
+ const previousSibling = childNodes[futureIndex - 1];
136
+ if (
137
+ nodeToInsert.nodeName !== "#text" &&
138
+ (!previousSibling || !isWhitespaceNode(previousSibling))
139
+ ) {
140
+ let indentation;
141
+ if (childNodes.length) {
142
+ indentation = getIndentation(childNodes[childNodes.length - 1]);
143
+ } else {
144
+ const parentIndentation = getIndentation(futureParentNode);
145
+ indentation = increaseIndentation(parentIndentation, 2);
146
+ }
112
147
  futureChildNodes.push({
113
148
  nodeName: "#text",
114
- value: "\n ",
149
+ value: `\n${indentation}`,
115
150
  parentNode: futureParentNode,
116
- })
151
+ });
117
152
  }
118
- futureChildNodes.push(nodeToInsert)
119
- const nextSibling = nextSiblings[0]
120
- if (!nextSibling || !isWhitespaceNode(nextSibling)) {
121
- futureChildNodes.push({
122
- nodeName: "#text",
123
- value: "\n ",
124
- parentNode: futureParentNode,
125
- })
153
+ futureChildNodes.push(nodeToInsert);
154
+ nodeToInsert.parentNode = futureParentNode;
155
+ if (nextSiblings.length) {
156
+ futureChildNodes.push(...nextSiblings);
126
157
  }
127
- if (nextSibling) {
128
- futureChildNodes.push(...nextSiblings)
158
+ futureParentNode.childNodes = futureChildNodes;
159
+
160
+ // update indentation when node contains text
161
+ const text = getHtmlNodeText(nodeToInsert);
162
+ if (text) {
163
+ setHtmlNodeText(nodeToInsert, text, { indentation: "auto" });
129
164
  }
130
- futureParentNode.childNodes = futureChildNodes
131
- }
165
+ };
132
166
 
133
167
  const isWhitespaceNode = (node) => {
134
- if (node.nodeName !== "#text") return false
135
- if (node.value.length === 0) return false
136
- return /^\s+$/.test(node.value)
137
- }
168
+ if (node.nodeName !== "#text") return false;
169
+ if (node.value.length === 0) return false;
170
+ return /^\s+$/.test(node.value);
171
+ };
138
172
 
139
- const findChild = ({ childNodes = [] }, predicate) => childNodes.find(predicate)
173
+ const findChild = ({ childNodes = [] }, predicate) =>
174
+ childNodes.find(predicate);
140
175
 
141
176
  const stringifyAttributes = (object) => {
142
- let string = ""
177
+ let string = "";
143
178
  Object.keys(object).forEach((key) => {
144
- const value = object[key]
145
- if (value === undefined) return
146
- if (string !== "") string += " "
147
- string += `${key}=${valueToHtmlAttributeValue(value)}`
148
- })
149
- return string
150
- }
179
+ const value = object[key];
180
+ if (value === undefined) return;
181
+ if (string !== "") string += " ";
182
+ string += `${key}=${valueToHtmlAttributeValue(value)}`;
183
+ });
184
+ return string;
185
+ };
151
186
 
152
187
  const valueToHtmlAttributeValue = (value) => {
153
188
  if (typeof value === "string") {
154
- return JSON.stringify(value)
189
+ return JSON.stringify(value);
155
190
  }
156
- return `"${JSON.stringify(value)}"`
157
- }
191
+ return `"${JSON.stringify(value)}"`;
192
+ };
@@ -1,43 +1,43 @@
1
1
  export const getHtmlNodeAttribute = (htmlNode, attributeName) => {
2
- const attribute = getHtmlAttributeByName(htmlNode, attributeName)
3
- return attribute ? attribute.value || "" : undefined
4
- }
2
+ const attribute = getHtmlAttributeByName(htmlNode, attributeName);
3
+ return attribute ? attribute.value || "" : undefined;
4
+ };
5
5
 
6
6
  export const setHtmlNodeAttributes = (htmlNode, attributesToAssign) => {
7
7
  if (typeof attributesToAssign !== "object") {
8
- throw new TypeError(`attributesToAssign must be an object`)
8
+ throw new TypeError(`attributesToAssign must be an object`);
9
9
  }
10
- const { attrs } = htmlNode
11
- if (!attrs) return
10
+ const { attrs } = htmlNode;
11
+ if (!attrs) return;
12
12
  Object.keys(attributesToAssign).forEach((key) => {
13
- const existingAttributeIndex = attrs.findIndex(({ name }) => name === key)
14
- const value = attributesToAssign[key]
13
+ const existingAttributeIndex = attrs.findIndex(({ name }) => name === key);
14
+ const value = attributesToAssign[key];
15
15
  // remove no-op
16
16
  if (existingAttributeIndex === -1 && value === undefined) {
17
- return
17
+ return;
18
18
  }
19
19
  // add
20
20
  if (existingAttributeIndex === -1 && value !== undefined) {
21
21
  attrs.push({
22
22
  name: key,
23
23
  value,
24
- })
25
- return
24
+ });
25
+ return;
26
26
  }
27
27
  // remove
28
28
  if (value === undefined) {
29
- attrs.splice(existingAttributeIndex, 1)
30
- return
29
+ attrs.splice(existingAttributeIndex, 1);
30
+ return;
31
31
  }
32
32
  // update
33
- attrs[existingAttributeIndex].value = value
34
- })
35
- }
33
+ attrs[existingAttributeIndex].value = value;
34
+ });
35
+ };
36
36
 
37
37
  const getHtmlAttributeByName = (htmlNode, attributeName) => {
38
- const attrs = htmlNode.attrs
38
+ const attrs = htmlNode.attrs;
39
39
  const attribute = attrs
40
40
  ? attrs.find((attr) => attr.name === attributeName)
41
- : null
42
- return attribute
43
- }
41
+ : null;
42
+ return attribute;
43
+ };
@@ -1,99 +1,99 @@
1
1
  import {
2
2
  getHtmlNodeAttribute,
3
3
  setHtmlNodeAttributes,
4
- } from "./html_node_attributes.js"
4
+ } from "./html_node_attributes.js";
5
5
 
6
6
  export const storeHtmlNodePosition = (node) => {
7
- const originalPositionAttributeName = `original-position`
7
+ const originalPositionAttributeName = `original-position`;
8
8
  const originalPosition = getHtmlNodeAttribute(
9
9
  node,
10
10
  originalPositionAttributeName,
11
- )
11
+ );
12
12
  if (originalPosition !== undefined) {
13
- return true
13
+ return true;
14
14
  }
15
- const { sourceCodeLocation } = node
15
+ const { sourceCodeLocation } = node;
16
16
  if (!sourceCodeLocation) {
17
- return false
17
+ return false;
18
18
  }
19
- const { startLine, startCol, endLine, endCol } = sourceCodeLocation
19
+ const { startLine, startCol, endLine, endCol } = sourceCodeLocation;
20
20
  setHtmlNodeAttributes(node, {
21
21
  [originalPositionAttributeName]: `${startLine}:${startCol};${endLine}:${endCol}`,
22
- })
23
- return true
24
- }
22
+ });
23
+ return true;
24
+ };
25
25
  export const storeHtmlNodeAttributePosition = (node, attributeName) => {
26
- const { sourceCodeLocation } = node
26
+ const { sourceCodeLocation } = node;
27
27
  if (!sourceCodeLocation) {
28
- return false
28
+ return false;
29
29
  }
30
- const attributeValue = getHtmlNodeAttribute(node, attributeName)
30
+ const attributeValue = getHtmlNodeAttribute(node, attributeName);
31
31
  if (attributeValue === undefined) {
32
- return false
32
+ return false;
33
33
  }
34
- const attributeLocation = sourceCodeLocation.attrs[attributeName]
34
+ const attributeLocation = sourceCodeLocation.attrs[attributeName];
35
35
  if (!attributeLocation) {
36
- return false
36
+ return false;
37
37
  }
38
- const originalPositionAttributeName = `original-${attributeName}-position`
38
+ const originalPositionAttributeName = `original-${attributeName}-position`;
39
39
  const originalPosition = getHtmlNodeAttribute(
40
40
  node,
41
41
  originalPositionAttributeName,
42
- )
42
+ );
43
43
  if (originalPosition !== undefined) {
44
- return true
44
+ return true;
45
45
  }
46
- const { startLine, startCol, endLine, endCol } = attributeLocation
46
+ const { startLine, startCol, endLine, endCol } = attributeLocation;
47
47
  setHtmlNodeAttributes(node, {
48
48
  [originalPositionAttributeName]: `${startLine}:${startCol};${endLine}:${endCol}`,
49
- })
50
- return true
51
- }
49
+ });
50
+ return true;
51
+ };
52
52
 
53
53
  export const getHtmlNodePosition = (node, { preferOriginal = false } = {}) => {
54
- const position = {}
55
- const { sourceCodeLocation } = node
54
+ const position = {};
55
+ const { sourceCodeLocation } = node;
56
56
  if (sourceCodeLocation) {
57
- const { startLine, startCol, endLine, endCol } = sourceCodeLocation
57
+ const { startLine, startCol, endLine, endCol } = sourceCodeLocation;
58
58
  Object.assign(position, {
59
59
  line: startLine,
60
60
  lineEnd: endLine,
61
61
  column: startCol,
62
62
  columnEnd: endCol,
63
- })
63
+ });
64
64
  }
65
- const originalPosition = getHtmlNodeAttribute(node, "original-position")
65
+ const originalPosition = getHtmlNodeAttribute(node, "original-position");
66
66
  if (originalPosition === undefined) {
67
- return position
67
+ return position;
68
68
  }
69
- const [start, end] = originalPosition.split(";")
70
- const [originalLine, originalColumn] = start.split(":")
71
- const [originalLineEnd, originalColumnEnd] = end.split(":")
69
+ const [start, end] = originalPosition.split(";");
70
+ const [originalLine, originalColumn] = start.split(":");
71
+ const [originalLineEnd, originalColumnEnd] = end.split(":");
72
72
  Object.assign(position, {
73
73
  originalLine: parseInt(originalLine),
74
74
  originalColumn: parseInt(originalColumn),
75
75
  originalLineEnd: parseInt(originalLineEnd),
76
76
  originalColumnEnd: parseInt(originalColumnEnd),
77
- })
77
+ });
78
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
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
84
  }
85
- return position
86
- }
85
+ return position;
86
+ };
87
87
  export const getHtmlNodeAttributePosition = (node, attributeName) => {
88
- const position = {}
89
- const { sourceCodeLocation } = node
88
+ const position = {};
89
+ const { sourceCodeLocation } = node;
90
90
  if (sourceCodeLocation) {
91
- const attributeLocation = sourceCodeLocation.attrs[attributeName]
91
+ const attributeLocation = sourceCodeLocation.attrs[attributeName];
92
92
  if (attributeLocation) {
93
93
  Object.assign(position, {
94
94
  line: attributeLocation.startLine,
95
95
  column: attributeLocation.startCol,
96
- })
96
+ });
97
97
  }
98
98
  }
99
99
  const originalPositionAttributeName =
@@ -101,22 +101,22 @@ export const getHtmlNodeAttributePosition = (node, attributeName) => {
101
101
  ? "original-src-position"
102
102
  : attributeName === "inlined-from-href"
103
103
  ? "original-href-position"
104
- : `original-${attributeName}-position`
104
+ : `original-${attributeName}-position`;
105
105
  const originalPosition = getHtmlNodeAttribute(
106
106
  node,
107
107
  originalPositionAttributeName,
108
- )
108
+ );
109
109
  if (originalPosition === undefined) {
110
- return position
110
+ return position;
111
111
  }
112
- const [start, end] = originalPosition.split(";")
113
- const [originalLine, originalColumn] = start.split(":")
114
- const [originalLineEnd, originalColumnEnd] = end.split(":")
112
+ const [start, end] = originalPosition.split(";");
113
+ const [originalLine, originalColumn] = start.split(":");
114
+ const [originalLineEnd, originalColumnEnd] = end.split(":");
115
115
  Object.assign(position, {
116
116
  originalLine: parseInt(originalLine),
117
117
  originalColumn: parseInt(originalColumn),
118
118
  originalLineEnd: parseInt(originalLineEnd),
119
119
  originalColumnEnd: parseInt(originalColumnEnd),
120
- })
121
- return position
122
- }
120
+ });
121
+ return position;
122
+ };