@jsenv/ast 6.10.5 → 6.10.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/ast",
3
- "version": "6.10.5",
3
+ "version": "6.10.7",
4
4
  "type": "module",
5
5
  "description": "Parse and transform HTML, CSS and JS",
6
6
  "repository": {
@@ -29,8 +29,8 @@
29
29
  "@babel/core": "8.0.1",
30
30
  "@babel/helper-module-imports": "8.0.0",
31
31
  "@babel/parser": "8.0.4",
32
- "@jsenv/humanize": "1.8.2",
33
- "@jsenv/urls": "2.9.13",
32
+ "@jsenv/humanize": "1.8.3",
33
+ "@jsenv/urls": "2.9.14",
34
34
  "@jsenv/utils": "2.3.1",
35
35
  "acorn": "8.18.0",
36
36
  "acorn-import-attributes": "1.9.5",
@@ -34,6 +34,12 @@ export const getUrlForContentInsideHtml = (node, htmlUrlInfo, reference) => {
34
34
  return inlineContentUrl;
35
35
  }
36
36
  }
37
+ // a node injected by jsenv has no place in the original html, so its position
38
+ // is read in the generated one: a coordinate space the page own nodes are not
39
+ // numbered in. Both spaces would mint the same url for different content.
40
+ if (getHtmlNodeAttribute(node, "jsenv-injected-by")) {
41
+ basename = "injected";
42
+ }
37
43
  const { line, column, lineEnd, columnEnd } = getHtmlNodePosition(node, {
38
44
  preferOriginal: true,
39
45
  });
@@ -54,7 +54,6 @@ export const injectJsenvScript = (
54
54
  htmlAst,
55
55
  { type, src, content, initCall, pluginName = "jsenv", ...attributes },
56
56
  ) => {
57
- const jsenvScriptsNode = getJsenvScriptsNode(htmlAst);
58
57
  if (type === "module") {
59
58
  if (src) {
60
59
  if (initCall) {
@@ -67,7 +66,7 @@ export const injectJsenvScript = (
67
66
 
68
67
  ${stringifyCall(initCall)};`,
69
68
  });
70
- insertHtmlNodeInside(inlineScriptNode, jsenvScriptsNode);
69
+ injectScriptNode(htmlAst, inlineScriptNode);
71
70
  return;
72
71
  }
73
72
  const remoteScriptNode = createHtmlNode({
@@ -77,7 +76,7 @@ ${stringifyCall(initCall)};`,
77
76
  "jsenv-injected-by": pluginName,
78
77
  ...attributes,
79
78
  });
80
- insertHtmlNodeInside(remoteScriptNode, jsenvScriptsNode);
79
+ injectScriptNode(htmlAst, remoteScriptNode);
81
80
  return;
82
81
  }
83
82
  const inlineScriptNode = createHtmlNode({
@@ -87,7 +86,7 @@ ${stringifyCall(initCall)};`,
87
86
  "children": content,
88
87
  ...attributes,
89
88
  });
90
- insertHtmlNodeInside(inlineScriptNode, jsenvScriptsNode);
89
+ injectScriptNode(htmlAst, inlineScriptNode);
91
90
  return;
92
91
  }
93
92
  if (src) {
@@ -97,7 +96,7 @@ ${stringifyCall(initCall)};`,
97
96
  "jsenv-injected-by": pluginName,
98
97
  ...attributes,
99
98
  });
100
- insertHtmlNodeInside(remoteScriptNode, jsenvScriptsNode);
99
+ injectScriptNode(htmlAst, remoteScriptNode);
101
100
  if (initCall) {
102
101
  const inlineScriptNode = createHtmlNode({
103
102
  "tagName": "script",
@@ -105,7 +104,7 @@ ${stringifyCall(initCall)};`,
105
104
  "children": `${stringifyCall(initCall)};`,
106
105
  ...attributes,
107
106
  });
108
- insertHtmlNodeInside(inlineScriptNode, jsenvScriptsNode);
107
+ injectScriptNode(htmlAst, inlineScriptNode);
109
108
  }
110
109
  return;
111
110
  }
@@ -115,54 +114,69 @@ ${stringifyCall(initCall)};`,
115
114
  "children": content,
116
115
  ...attributes,
117
116
  });
118
- insertHtmlNodeInside(inlineScriptNode, jsenvScriptsNode);
117
+ injectScriptNode(htmlAst, inlineScriptNode);
119
118
  };
120
119
 
121
- const getJsenvScriptsNode = (htmlAst) => {
122
- // get or insert <jsenv-scripts>
123
- let jsenvScripts = findHtmlNode(
120
+ // Scripts injected by jsenv are kept together between two marker comments, so
121
+ // the served html shows at a glance what the page wrote and what jsenv added.
122
+ // Comments are used rather than a wrapper element because the group lives in
123
+ // <head>, where an unknown tag would end <head> and reparent everything below
124
+ // it into <body>.
125
+ const JSENV_SCRIPTS_START = " jsenv scripts ";
126
+ const JSENV_SCRIPTS_END = " /jsenv scripts ";
127
+
128
+ const injectScriptNode = (htmlAst, scriptNode) => {
129
+ const endComment = findHtmlNode(
124
130
  htmlAst,
125
- (node) => node.nodeName === "jsenv-scripts",
131
+ (node) => node.nodeName === "#comment" && node.data === JSENV_SCRIPTS_END,
126
132
  );
127
- if (jsenvScripts) {
128
- return jsenvScripts;
133
+ if (endComment) {
134
+ insertHtmlNodeBefore(scriptNode, endComment);
135
+ return;
129
136
  }
130
- jsenvScripts = createHtmlNode({
131
- tagName: "jsenv-scripts",
132
- });
137
+ insertFirstScriptNode(htmlAst, scriptNode);
138
+ insertHtmlNodeBefore(createHtmlComment(JSENV_SCRIPTS_START), scriptNode);
139
+ insertHtmlNodeAfter(createHtmlComment(JSENV_SCRIPTS_END), scriptNode);
140
+ };
141
+
142
+ // the group opens in <head> after any <link>, <meta> and <script
143
+ // type="importmap">, and before the first script the page wrote itself
144
+ const insertFirstScriptNode = (htmlAst, scriptNode) => {
133
145
  const headNode = findChild(htmlAst, (node) => node.nodeName === "html")
134
146
  .childNodes[0];
135
147
  let after = headNode.childNodes[0];
136
148
  for (const child of headNode.childNodes) {
137
- if (child.nodeName === "link") {
149
+ if (child.nodeName === "link" || child.nodeName === "meta") {
138
150
  after = child;
139
151
  continue;
140
152
  }
141
153
  if (child.nodeName === "script") {
142
- const { type } = analyzeScriptNode(child);
143
- if (type === "js_classic") {
144
- insertHtmlNodeBefore(jsenvScripts, child);
145
- return jsenvScripts;
154
+ if (getHtmlNodeAttribute(child, "jsenv-injected-by")) {
155
+ after = child;
156
+ continue;
146
157
  }
158
+ const { type } = analyzeScriptNode(child);
147
159
  if (type === "importmap") {
148
- insertHtmlNodeAfter(jsenvScripts, child);
149
- return jsenvScripts;
150
- }
151
- if (type === "js_module") {
152
- insertHtmlNodeBefore(jsenvScripts, child);
153
- return jsenvScripts;
160
+ after = child;
161
+ continue;
154
162
  }
155
- }
156
- if (child.nodeName === "meta") {
157
- after = child;
163
+ insertHtmlNodeBefore(scriptNode, child);
164
+ return;
158
165
  }
159
166
  }
160
167
  if (after) {
161
- insertHtmlNodeAfter(jsenvScripts, after);
162
- return jsenvScripts;
168
+ insertHtmlNodeAfter(scriptNode, after);
169
+ return;
163
170
  }
164
- injectHtmlNode(htmlAst, jsenvScripts);
165
- return jsenvScripts;
171
+ injectHtmlNode(
172
+ htmlAst,
173
+ scriptNode,
174
+ getHtmlNodeAttribute(scriptNode, "jsenv-injected-by"),
175
+ );
176
+ };
177
+
178
+ const createHtmlComment = (data) => {
179
+ return parseFragment(`<!--${data}-->`).childNodes[0];
166
180
  };
167
181
  const stringifyCall = (initCall, { pretty = true } = {}) => {
168
182
  if (!Object.hasOwn(initCall, "params")) {