@jsenv/core 27.0.0-alpha.83 → 27.0.0-alpha.86

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.
Files changed (62) hide show
  1. package/dist/html/explorer.html +1 -1
  2. package/dist/js/event_source_client.js +3 -3
  3. package/dist/js/s.js +2 -2
  4. package/dist/main.js +503 -417
  5. package/dist/s.js +2 -2
  6. package/dist/s.js.map +2 -1
  7. package/package.json +7 -3
  8. package/src/build/build.js +5 -8
  9. package/src/build/build_urls_generator.js +1 -2
  10. package/src/build/inject_global_version_mappings.js +4 -4
  11. package/src/build/inject_service_worker_urls.js +2 -2
  12. package/src/build/resync_ressource_hints.js +17 -18
  13. package/src/build/start_build_server.js +5 -1
  14. package/src/build/version_generator.js +60 -0
  15. package/src/dev/plugins/explorer/client/explorer.html +1 -1
  16. package/src/dev/plugins/explorer/jsenv_plugin_explorer.js +2 -3
  17. package/src/dev/plugins/toolbar/client/util/fetching.js +1 -1
  18. package/src/dev/plugins/toolbar/jsenv_plugin_toolbar.js +3 -3
  19. package/src/dev/start_dev_server.js +5 -1
  20. package/src/execute/runtimes/browsers/from_playwright.js +2 -2
  21. package/src/execute/runtimes/node/node_process.js +2 -2
  22. package/src/helpers/command/command.js +73 -0
  23. package/src/helpers/worker_reload.js +4 -3
  24. package/src/omega/compat/runtime_compat.js +2 -1
  25. package/src/omega/kitchen.js +4 -1
  26. package/src/omega/server/user_agent.js +2 -1
  27. package/src/omega/url_graph/sort_by_dependencies.js +27 -0
  28. package/src/omega/url_graph/url_info_transformations.js +24 -14
  29. package/src/plugins/autoreload/dev_sse/client/reload.js +6 -3
  30. package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_client.js +3 -3
  31. package/src/plugins/bundling/css/bundle_css.js +4 -4
  32. package/src/plugins/bundling/js_module/bundle_js_module.js +86 -67
  33. package/src/plugins/commonjs_globals/jsenv_plugin_commonjs_globals.js +2 -2
  34. package/src/plugins/file_urls/jsenv_plugin_file_urls.js +4 -5
  35. package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +62 -74
  36. package/src/plugins/http_urls/jsenv_plugin_http_urls.js +10 -6
  37. package/src/plugins/import_meta_hot/html_hot_dependencies.js +9 -15
  38. package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +3 -3
  39. package/src/plugins/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +2 -2
  40. package/src/plugins/importmap/jsenv_plugin_importmap.js +25 -27
  41. package/src/plugins/inject_globals/inject_globals.js +4 -4
  42. package/src/plugins/inline/jsenv_plugin_data_urls.js +1 -1
  43. package/src/plugins/inline/jsenv_plugin_html_inline_content.js +41 -43
  44. package/src/plugins/inline/jsenv_plugin_js_inline_content.js +4 -4
  45. package/src/plugins/minification/css/minify_css.js +1 -1
  46. package/src/plugins/transpilation/as_js_classic/client/s.js +2 -2
  47. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +2 -4
  48. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +45 -67
  49. package/src/plugins/transpilation/babel/global_this/babel_plugin_global_this_as_jsenv_import.js +2 -3
  50. package/src/plugins/transpilation/babel/helpers/babel_plugin_babel_helpers_as_jsenv_imports.js +3 -4
  51. package/src/plugins/transpilation/babel/jsenv_plugin_babel.js +1 -1
  52. package/src/plugins/transpilation/babel/new_stylesheet/babel_plugin_new_stylesheet_as_jsenv_import.js +2 -3
  53. package/src/plugins/transpilation/babel/regenerator_runtime/babel_plugin_regenerator_runtime_as_jsenv_import.js +2 -3
  54. package/src/plugins/transpilation/css_parcel/jsenv_plugin_css_parcel.js +1 -1
  55. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +1 -1
  56. package/src/plugins/transpilation/jsenv_plugin_top_level_await.js +2 -1
  57. package/src/plugins/url_analysis/css/css_urls.js +2 -3
  58. package/src/plugins/url_analysis/html/html_urls.js +98 -113
  59. package/src/plugins/url_analysis/js/js_urls.js +3 -2
  60. package/src/plugins/url_analysis/jsenv_plugin_url_analysis.js +10 -6
  61. package/src/test/coverage/empty_coverage_factory.js +1 -1
  62. package/src/test/coverage/file_by_file_coverage.js +1 -2
package/dist/main.js CHANGED
@@ -6,42 +6,30 @@ import { urlToRelativeUrl, generateInlineContentUrl, ensurePathnameTrailingSlash
6
6
  import { fileURLToPath, pathToFileURL } from "node:url";
7
7
  import { workerData, Worker } from "node:worker_threads";
8
8
  import { URL_META } from "@jsenv/url-meta";
9
- import { parseHtmlString, stringifyHtmlAst, visitHtmlAst, getHtmlNodeAttributeByName, htmlNodePosition, findNode, getHtmlNodeTextNode, removeHtmlNode, setHtmlNodeGeneratedText, removeHtmlNodeAttributeByName, parseScriptNode, injectScriptAsEarlyAsPossible, createHtmlNode, removeHtmlNodeText, assignHtmlNodeAttributes, parseLinkNode } from "@jsenv/utils/html_ast/html_ast.js";
10
- import { htmlAttributeSrcSet } from "@jsenv/utils/html_ast/html_attribute_src_set.js";
11
- import { createMagicSource } from "@jsenv/utils/sourcemap/magic_source.js";
12
- import { applyPostCss } from "@jsenv/utils/css_ast/apply_post_css.js";
13
- import { postCssPluginUrlVisitor } from "@jsenv/utils/css_ast/postcss_plugin_url_visitor.js";
14
- import { parseJsUrls } from "@jsenv/utils/js_ast/parse_js_urls.js";
9
+ import { parseHtmlString, stringifyHtmlAst, visitHtmlNodes, getHtmlNodeAttribute, setHtmlNodeAttributes, parseSrcSet, getHtmlNodePosition, getHtmlNodeAttributePosition, applyPostCss, postCssPluginUrlVisitor, parseJsUrls, findHtmlNode, getHtmlNodeText, removeHtmlNode, setHtmlNodeText, analyzeScriptNode, applyBabelPlugins, injectScriptNodeAsEarlyAsPossible, createHtmlNode, removeHtmlNodeText, transpileWithParcel, injectJsImport, minifyWithParcel, analyzeLinkNode } from "@jsenv/ast";
10
+ import { createMagicSource, composeTwoSourcemaps, sourcemapConverter, SOURCEMAP, generateSourcemapFileUrl, generateSourcemapDataUrl } from "@jsenv/sourcemap";
15
11
  import { resolveImport, normalizeImportMap, composeTwoImportMaps } from "@jsenv/importmap";
16
12
  import { applyNodeEsmResolution, defaultLookupPackageScope, defaultReadPackageJson, readCustomConditionsFromProcessArgs, applyFileSystemMagicResolution, getExtensionsToTry } from "@jsenv/node-esm-resolution";
17
13
  import { statSync, realpathSync, readdirSync, readFileSync, existsSync } from "node:fs";
18
- import { CONTENT_TYPE } from "@jsenv/utils/content_type/content_type.js";
19
- import { JS_QUOTES } from "@jsenv/utils/string/js_quotes.js";
20
- import { applyBabelPlugins } from "@jsenv/utils/js_ast/apply_babel_plugins.js";
21
- import { transpileWithParcel, minifyWithParcel } from "@jsenv/utils/css_ast/parcel_css.js";
14
+ import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js";
15
+ import { JS_QUOTES } from "@jsenv/utils/src/string/js_quotes.js";
22
16
  import { createRequire } from "node:module";
23
- import { composeTwoSourcemaps } from "@jsenv/utils/sourcemap/sourcemap_composition_v3.js";
24
17
  import babelParser from "@babel/parser";
25
- import { findHighestVersion } from "@jsenv/utils/semantic_versioning/highest_version.js";
26
- import { injectImport } from "@jsenv/utils/js_ast/babel_utils.js";
27
- import { sortByDependencies } from "@jsenv/utils/graph/sort_by_dependencies.js";
28
- import { applyRollupPlugins } from "@jsenv/utils/js_ast/apply_rollup_plugins.js";
29
- import { sourcemapConverter } from "@jsenv/utils/sourcemap/sourcemap_converter.js";
30
- import { SOURCEMAP, generateSourcemapUrl, sourcemapToBase64Url } from "@jsenv/utils/sourcemap/sourcemap_utils.js";
18
+ import { findHighestVersion } from "@jsenv/utils/src/semantic_versioning/highest_version.js";
31
19
  import { validateResponseIntegrity } from "@jsenv/integrity";
32
20
  import { convertFileSystemErrorToResponseProperties } from "@jsenv/server/src/internal/convertFileSystemErrorToResponseProperties.js";
33
- import { memoizeByFirstArgument } from "@jsenv/utils/memoize/memoize_by_first_argument.js";
21
+ import { memoizeByFirstArgument } from "@jsenv/utils/src/memoize/memoize_by_first_argument.js";
34
22
  import { memoryUsage } from "node:process";
35
23
  import wrapAnsi from "wrap-ansi";
36
24
  import stripAnsi from "strip-ansi";
37
25
  import cuid from "cuid";
38
26
  import v8 from "node:v8";
39
27
  import { runInNewContext, Script } from "node:vm";
40
- import { memoize } from "@jsenv/utils/memoize/memoize.js";
41
- import { escapeRegexpSpecialChars } from "@jsenv/utils/string/escape_regexp_special_chars.js";
28
+ import { memoize } from "@jsenv/utils/src/memoize/memoize.js";
29
+ import { escapeRegexpSpecialChars } from "@jsenv/utils/src/string/escape_regexp_special_chars.js";
42
30
  import { fork } from "node:child_process";
43
31
  import { uneval } from "@jsenv/uneval";
44
- import { createVersionGenerator } from "@jsenv/utils/versioning/version_generator.js";
32
+ import { createHash } from "node:crypto";
45
33
 
46
34
  const createReloadableWorker = (workerFileUrl, options = {}) => {
47
35
  const workerFilePath = fileURLToPath(workerFileUrl);
@@ -75,12 +63,13 @@ const createReloadableWorker = (workerFileUrl, options = {}) => {
75
63
  worker.once("error", error => {
76
64
  console.error(error);
77
65
  });
78
- worker.once("exit", () => {
79
- worker = null;
80
- });
81
66
  await new Promise(resolve => {
82
67
  worker.once("online", resolve);
83
68
  });
69
+ worker.once("exit", () => {
70
+ worker = null;
71
+ });
72
+ return worker;
84
73
  };
85
74
 
86
75
  const reload = async () => {
@@ -118,9 +107,14 @@ const parseAndTransformHtmlUrls = async (urlInfo, context) => {
118
107
  column,
119
108
  originalLine,
120
109
  originalColumn,
121
- specifier,
122
- attribute
110
+ node,
111
+ attributeName,
112
+ specifier
123
113
  }) => {
114
+ const {
115
+ crossorigin,
116
+ integrity
117
+ } = readFetchMetas(node);
124
118
  const isRessourceHint = ["preconnect", "dns-prefetch", "prefetch", "preload", "modulepreload"].includes(subtype);
125
119
  const [reference] = referenceUtils.found({
126
120
  type,
@@ -130,10 +124,14 @@ const parseAndTransformHtmlUrls = async (urlInfo, context) => {
130
124
  specifier,
131
125
  specifierLine: line,
132
126
  specifierColumn: column,
133
- isRessourceHint
127
+ isRessourceHint,
128
+ crossorigin,
129
+ integrity
134
130
  });
135
131
  actions.push(async () => {
136
- attribute.value = await referenceUtils.readGeneratedSpecifier(reference);
132
+ setHtmlNodeAttributes(node, {
133
+ [attributeName]: await referenceUtils.readGeneratedSpecifier(reference)
134
+ });
137
135
  });
138
136
  }
139
137
  });
@@ -147,6 +145,24 @@ const parseAndTransformHtmlUrls = async (urlInfo, context) => {
147
145
  content: stringifyHtmlAst(htmlAst)
148
146
  };
149
147
  };
148
+ const crossOriginCompatibleTagNames = ["script", "link", "img", "source"];
149
+ const integrityCompatibleTagNames = ["script", "link", "img", "source"];
150
+
151
+ const readFetchMetas = node => {
152
+ const meta = {};
153
+
154
+ if (crossOriginCompatibleTagNames.includes(node.nodeName)) {
155
+ const crossorigin = getHtmlNodeAttribute(node, "crossorigin") !== undefined;
156
+ meta.crossorigin = crossorigin;
157
+ }
158
+
159
+ if (integrityCompatibleTagNames.includes(node.nodeName)) {
160
+ const integrity = getHtmlNodeAttribute(node, "integrity");
161
+ meta.integrity = integrity;
162
+ }
163
+
164
+ return meta;
165
+ };
150
166
 
151
167
  const visitHtmlUrls = ({
152
168
  url,
@@ -158,18 +174,18 @@ const visitHtmlUrls = ({
158
174
  subtype,
159
175
  expectedType,
160
176
  node,
161
- attribute,
177
+ attributeName,
162
178
  specifier
163
179
  }) => {
164
- const generatedFromInlineContent = Boolean(getHtmlNodeAttributeByName(node, "generated-from-inline-content"));
180
+ const generatedFromInlineContent = getHtmlNodeAttribute(node, "generated-from-inline-content") !== undefined;
165
181
  let position;
166
182
 
167
183
  if (generatedFromInlineContent) {
168
184
  // when generated from inline content,
169
185
  // line, column is not "src" nor "generated-from-src" but "original-position"
170
- position = htmlNodePosition.readNodePosition(node);
186
+ position = getHtmlNodePosition(node);
171
187
  } else {
172
- position = htmlNodePosition.readAttributePosition(node, attribute.name);
188
+ position = getHtmlNodeAttributePosition(node, attributeName);
173
189
  }
174
190
 
175
191
  const {
@@ -185,19 +201,68 @@ const visitHtmlUrls = ({
185
201
  column,
186
202
  // originalLine, originalColumn
187
203
  specifier,
188
- attribute,
189
- // injected:Boolean(getHtmlNodeAttributeByName(node, "injected-by"))
190
- // srcGeneratedFromInlineContent
191
- ...readFetchMetas(node)
204
+ node,
205
+ attributeName
192
206
  });
193
207
  };
194
208
 
195
- const visitors = {
209
+ const visitAttributeAsUrlSpecifier = ({
210
+ node,
211
+ attributeName,
212
+ ...rest
213
+ }) => {
214
+ const value = getHtmlNodeAttribute(node, attributeName);
215
+
216
+ if (value) {
217
+ const generatedBy = getHtmlNodeAttribute(node, "generated-by");
218
+
219
+ if (generatedBy !== undefined) {
220
+ // during build the importmap is inlined
221
+ // and shoud not be considered as a dependency anymore
222
+ return;
223
+ }
224
+
225
+ addDependency({ ...rest,
226
+ node,
227
+ attributeName,
228
+ specifier: attributeName === "generated-from-src" || attributeName === "generated-from-href" ? new URL(value, url).href : value
229
+ });
230
+ } else if (attributeName === "src") {
231
+ visitAttributeAsUrlSpecifier({ ...rest,
232
+ node,
233
+ attributeName: "generated-from-src"
234
+ });
235
+ } else if (attributeName === "href") {
236
+ visitAttributeAsUrlSpecifier({ ...rest,
237
+ node,
238
+ attributeName: "generated-from-href"
239
+ });
240
+ }
241
+ };
242
+
243
+ const visitSrcset = ({
244
+ type,
245
+ node
246
+ }) => {
247
+ const srcset = getHtmlNodeAttribute(node, "srcset");
248
+
249
+ if (srcset) {
250
+ const srcCandidates = parseSrcSet(srcset);
251
+ srcCandidates.forEach(srcCandidate => {
252
+ addDependency({
253
+ type,
254
+ node,
255
+ attributeName: "srcset",
256
+ specifier: srcCandidate.specifier
257
+ });
258
+ });
259
+ }
260
+ };
261
+
262
+ visitHtmlNodes(htmlAst, {
196
263
  link: node => {
197
- const relAttribute = getHtmlNodeAttributeByName(node, "rel");
198
- const rel = relAttribute ? relAttribute.value : undefined;
199
- const typeAttribute = getHtmlNodeAttributeByName(node, "type");
200
- const type = typeAttribute ? typeAttribute.value : undefined;
264
+ const rel = getHtmlNodeAttribute(node, "rel");
265
+ const type = getHtmlNodeAttribute(node, "type");
201
266
  visitAttributeAsUrlSpecifier({
202
267
  type: "link_href",
203
268
  subtype: rel,
@@ -213,15 +278,16 @@ const visitHtmlUrls = ({
213
278
  },
214
279
  // style: () => {},
215
280
  script: node => {
216
- const typeAttributeNode = getHtmlNodeAttributeByName(node, "type");
281
+ const type = getHtmlNodeAttribute(node, "type");
282
+ const expectedType = {
283
+ "undefined": "js_classic",
284
+ "text/javascript": "js_classic",
285
+ "module": "js_module",
286
+ "importmap": "importmap"
287
+ }[type];
217
288
  visitAttributeAsUrlSpecifier({
218
289
  type: "script_src",
219
- expectedType: {
220
- "undefined": "js_classic",
221
- "text/javascript": "js_classic",
222
- "module": "js_module",
223
- "importmap": "importmap"
224
- }[typeAttributeNode ? typeAttributeNode.value : undefined],
290
+ expectedType,
225
291
  node,
226
292
  attributeName: "src"
227
293
  });
@@ -277,102 +343,9 @@ const visitHtmlUrls = ({
277
343
  attributeName: "href"
278
344
  });
279
345
  }
280
- };
281
-
282
- const visitAttributeAsUrlSpecifier = ({
283
- type,
284
- subtype,
285
- expectedType,
286
- node,
287
- attributeName
288
- }) => {
289
- const attribute = getHtmlNodeAttributeByName(node, attributeName);
290
- const value = attribute ? attribute.value : undefined;
291
-
292
- if (value) {
293
- const generatedBy = getHtmlNodeAttributeByName(node, "generated-by");
294
-
295
- if (generatedBy) {
296
- // during build the importmap is inlined
297
- // and shoud not be considered as a dependency anymore
298
- return;
299
- }
300
-
301
- addDependency({
302
- type,
303
- subtype,
304
- expectedType,
305
- node,
306
- attribute,
307
- specifier: attributeName === "generated-from-src" || attributeName === "generated-from-href" ? new URL(value, url).href : value
308
- });
309
- } else if (attributeName === "src") {
310
- visitAttributeAsUrlSpecifier({
311
- type,
312
- subtype,
313
- expectedType,
314
- node,
315
- attributeName: "generated-from-src"
316
- });
317
- } else if (attributeName === "href") {
318
- visitAttributeAsUrlSpecifier({
319
- type,
320
- subtype,
321
- expectedType,
322
- node,
323
- attributeName: "generated-from-href"
324
- });
325
- }
326
- };
327
-
328
- const visitSrcset = ({
329
- type,
330
- node
331
- }) => {
332
- const srcsetAttribute = getHtmlNodeAttributeByName(node, "srcset");
333
- const srcset = srcsetAttribute ? srcsetAttribute.value : undefined;
334
-
335
- if (srcset) {
336
- const srcCandidates = htmlAttributeSrcSet.parse(srcset);
337
- srcCandidates.forEach(srcCandidate => {
338
- addDependency({
339
- type,
340
- node,
341
- attribute: srcsetAttribute,
342
- specifier: srcCandidate.specifier
343
- });
344
- });
345
- }
346
- };
347
-
348
- visitHtmlAst(htmlAst, node => {
349
- const visitor = visitors[node.nodeName];
350
-
351
- if (visitor) {
352
- visitor(node);
353
- }
354
346
  });
355
347
  };
356
348
 
357
- const crossOriginCompatibleTagNames = ["script", "link", "img", "source"];
358
- const integrityCompatibleTagNames = ["script", "link", "img", "source"];
359
-
360
- const readFetchMetas = node => {
361
- const meta = {};
362
-
363
- if (crossOriginCompatibleTagNames.includes(node.nodeName)) {
364
- const crossoriginAttribute = getHtmlNodeAttributeByName(node, "crossorigin");
365
- meta.crossorigin = crossoriginAttribute ? crossoriginAttribute.value : undefined;
366
- }
367
-
368
- if (integrityCompatibleTagNames.includes(node.nodeName)) {
369
- const integrityAttribute = getHtmlNodeAttributeByName(node, "integrity");
370
- meta.integrity = integrityAttribute ? integrityAttribute.value : undefined;
371
- }
372
-
373
- return meta;
374
- };
375
-
376
349
  /*
377
350
  * https://github.com/parcel-bundler/parcel/blob/v2/packages/transformers/css/src/CSSTransformer.js
378
351
  */
@@ -547,7 +520,8 @@ const parseAndTransformWebmanifestUrls = async (urlInfo, context) => {
547
520
 
548
521
  const jsenvPluginUrlAnalysis = ({
549
522
  rootDirectoryUrl,
550
- include
523
+ include,
524
+ supportedProtocols = ["file:", "data:", "virtual:", "http:", "https:"]
551
525
  }) => {
552
526
  let getIncludeInfo = () => undefined;
553
527
 
@@ -592,12 +566,12 @@ const jsenvPluginUrlAnalysis = ({
592
566
  return;
593
567
  }
594
568
 
595
- if (reference.url.startsWith("data:")) {
596
- reference.shouldHandle = true;
597
- return;
598
- }
569
+ const {
570
+ protocol
571
+ } = new URL(reference.url);
572
+ const protocolIsSupported = supportedProtocols.some(supportedProtocol => protocol === supportedProtocol);
599
573
 
600
- if (reference.url.startsWith("file:")) {
574
+ if (protocolIsSupported) {
601
575
  reference.shouldHandle = true;
602
576
  return;
603
577
  }
@@ -748,14 +722,14 @@ const jsenvPluginImportmap = () => {
748
722
  transformUrlContent: {
749
723
  html: async (htmlUrlInfo, context) => {
750
724
  const htmlAst = parseHtmlString(htmlUrlInfo.content);
751
- const importmap = findNode(htmlAst, node => {
725
+ const importmap = findHtmlNode(htmlAst, node => {
752
726
  if (node.nodeName !== "script") {
753
727
  return false;
754
728
  }
755
729
 
756
- const typeAttribute = getHtmlNodeAttributeByName(node, "type");
730
+ const type = getHtmlNodeAttribute(node, "type");
757
731
 
758
- if (!typeAttribute || typeAttribute.value !== "importmap") {
732
+ if (type === undefined || type !== "importmap") {
759
733
  return false;
760
734
  }
761
735
 
@@ -774,7 +748,7 @@ const jsenvPluginImportmap = () => {
774
748
  lineEnd,
775
749
  columnEnd,
776
750
  isOriginal
777
- } = htmlNodePosition.readNodePosition(importmap, {
751
+ } = getHtmlNodePosition(importmap, {
778
752
  preferOriginal: true
779
753
  });
780
754
  const inlineImportmapUrl = generateInlineContentUrl({
@@ -797,9 +771,9 @@ const jsenvPluginImportmap = () => {
797
771
  await context.cook(inlineImportmapUrlInfo, {
798
772
  reference: inlineImportmapReference
799
773
  });
800
- setHtmlNodeGeneratedText(importmap, {
801
- generatedText: inlineImportmapUrlInfo.content,
802
- generatedBy: "jsenv:importmap"
774
+ setHtmlNodeText(importmap, inlineImportmapUrlInfo.content);
775
+ setHtmlNodeAttributes(importmap, {
776
+ "generated-by": "jsenv:importmap"
803
777
  });
804
778
  onHtmlImportmapParsed(JSON.parse(inlineImportmapUrlInfo.content), htmlUrlInfo.url);
805
779
  };
@@ -816,11 +790,11 @@ const jsenvPluginImportmap = () => {
816
790
  reference: importmapReference
817
791
  });
818
792
  onHtmlImportmapParsed(JSON.parse(importmapUrlInfo.content), htmlUrlInfo.url);
819
- removeHtmlNodeAttributeByName(importmap, "src");
820
- setHtmlNodeGeneratedText(importmap, {
821
- generatedText: importmapUrlInfo.content,
822
- generatedBy: "jsenv:importmap",
823
- generatedFromSrc: src
793
+ setHtmlNodeText(importmap, importmapUrlInfo.content);
794
+ setHtmlNodeAttributes(importmap, {
795
+ "src": undefined,
796
+ "generated-by": "jsenv:importmap",
797
+ "generated-from-src": src
824
798
  });
825
799
  const {
826
800
  line,
@@ -828,7 +802,7 @@ const jsenvPluginImportmap = () => {
828
802
  lineEnd,
829
803
  columnEnd,
830
804
  isOriginal
831
- } = htmlNodePosition.readNodePosition(importmap, {
805
+ } = getHtmlNodePosition(importmap, {
832
806
  preferOriginal: true
833
807
  });
834
808
  const inlineImportmapUrl = generateInlineContentUrl({
@@ -849,16 +823,15 @@ const jsenvPluginImportmap = () => {
849
823
  });
850
824
  };
851
825
 
852
- const srcAttribute = getHtmlNodeAttributeByName(importmap, "src");
853
- const src = srcAttribute ? srcAttribute.value : undefined;
826
+ const src = getHtmlNodeAttribute(importmap, "src");
854
827
 
855
828
  if (src) {
856
829
  await handleImportmapWithSrc(importmap, src);
857
830
  } else {
858
- const textNode = getHtmlNodeTextNode(importmap);
831
+ const htmlNodeText = getHtmlNodeText(importmap);
859
832
 
860
- if (textNode) {
861
- await handleInlineImportmap(importmap, textNode);
833
+ if (htmlNodeText) {
834
+ await handleInlineImportmap(importmap, htmlNodeText);
862
835
  }
863
836
  } // once this plugin knows the importmap, it will use it
864
837
  // to map imports. These import specifiers will be normalized
@@ -1107,7 +1080,7 @@ const jsenvPluginUrlVersion = () => {
1107
1080
  const jsenvPluginFileUrls = ({
1108
1081
  magicExtensions = ["inherit", ".js"],
1109
1082
  magicDirectoryIndex = true,
1110
- preservesSymlink = true,
1083
+ preserveSymlinks = false,
1111
1084
  directoryReferenceAllowed = false
1112
1085
  }) => {
1113
1086
  return [{
@@ -1162,7 +1135,7 @@ const jsenvPluginFileUrls = ({
1162
1135
  if (foundADirectory && directoryReferenceAllowed) {
1163
1136
  reference.data.foundADirectory = true;
1164
1137
  const directoryFacadeUrl = urlObject.href;
1165
- const directoryUrlRaw = preservesSymlink ? directoryFacadeUrl : resolveSymlink(directoryFacadeUrl);
1138
+ const directoryUrlRaw = preserveSymlinks ? directoryFacadeUrl : resolveSymlink(directoryFacadeUrl);
1166
1139
  const directoryUrl = `${directoryUrlRaw}${search}${hash}`;
1167
1140
  return directoryUrl;
1168
1141
  }
@@ -1181,7 +1154,7 @@ const jsenvPluginFileUrls = ({
1181
1154
 
1182
1155
  reference.data.foundADirectory = filesystemResolution.isDirectory;
1183
1156
  const fileFacadeUrl = filesystemResolution.url;
1184
- const fileUrlRaw = preservesSymlink ? fileFacadeUrl : resolveSymlink(fileFacadeUrl);
1157
+ const fileUrlRaw = preserveSymlinks ? fileFacadeUrl : resolveSymlink(fileFacadeUrl);
1185
1158
  const fileUrl = `${fileUrlRaw}${search}${hash}`;
1186
1159
  return fileUrl;
1187
1160
  }
@@ -1279,13 +1252,14 @@ const resolveSymlink = fileUrl => {
1279
1252
  const jsenvPluginHttpUrls = () => {
1280
1253
  return {
1281
1254
  name: "jsenv:http_urls",
1282
- appliesDuring: "*" // fetchUrlContent: (urlInfo) => {
1283
- // if (urlInfo.url.startsWith("http") || urlInfo.url.startsWith("https")) {
1284
- // return { shouldHandle: false }
1285
- // }
1286
- // return null
1287
- // },
1255
+ appliesDuring: "*",
1256
+ redirectUrl: reference => {
1257
+ if (reference.url.startsWith("http:") || reference.url.startsWith("https:")) {
1258
+ reference.shouldHandle = false;
1259
+ } // TODO: according to some pattern matching jsenv could be allowed
1260
+ // to fetch and transform http urls
1288
1261
 
1262
+ }
1289
1263
  };
1290
1264
  };
1291
1265
 
@@ -1301,13 +1275,9 @@ const jsenvPluginHtmlInlineContent = ({
1301
1275
  const actions = [];
1302
1276
 
1303
1277
  const handleInlineStyle = node => {
1304
- if (node.nodeName !== "style") {
1305
- return;
1306
- }
1307
-
1308
- const textNode = getHtmlNodeTextNode(node);
1278
+ const htmlNodeText = getHtmlNodeText(node);
1309
1279
 
1310
- if (!textNode) {
1280
+ if (!htmlNodeText) {
1311
1281
  return;
1312
1282
  }
1313
1283
 
@@ -1318,7 +1288,7 @@ const jsenvPluginHtmlInlineContent = ({
1318
1288
  lineEnd,
1319
1289
  columnEnd,
1320
1290
  isOriginal
1321
- } = htmlNodePosition.readNodePosition(node, {
1291
+ } = getHtmlNodePosition(node, {
1322
1292
  preferOriginal: true
1323
1293
  });
1324
1294
  const inlineStyleUrl = generateInlineContentUrl({
@@ -1340,55 +1310,47 @@ const jsenvPluginHtmlInlineContent = ({
1340
1310
  specifierColumn: column,
1341
1311
  specifier: inlineStyleUrl,
1342
1312
  contentType: "text/css",
1343
- content: textNode.value
1313
+ content: htmlNodeText
1344
1314
  });
1345
1315
  await context.cook(inlineStyleUrlInfo, {
1346
1316
  reference: inlineStyleReference
1347
1317
  });
1348
- setHtmlNodeGeneratedText(node, {
1349
- generatedText: inlineStyleUrlInfo.content,
1350
- generatedBy: "jsenv:html_inline_content"
1318
+ setHtmlNodeText(node, inlineStyleUrlInfo.content);
1319
+ setHtmlNodeAttributes(node, {
1320
+ "generated-by": "jsenv:html_inline_content"
1351
1321
  });
1352
1322
  });
1353
1323
  };
1354
1324
 
1355
1325
  const handleInlineScript = node => {
1356
- if (node.nodeName !== "script") {
1357
- return;
1358
- }
1359
-
1360
- const textNode = getHtmlNodeTextNode(node);
1326
+ const htmlNodeText = getHtmlNodeText(node);
1361
1327
 
1362
- if (!textNode) {
1328
+ if (!htmlNodeText) {
1363
1329
  return;
1364
1330
  } // If the inline script was already handled by an other plugin, ignore it
1365
1331
  // - we want to preserve inline scripts generated by html supervisor during dev
1366
1332
  // - we want to avoid cooking twice a script during build
1367
1333
 
1368
1334
 
1369
- const generatedBy = getHtmlNodeAttributeByName(node, "generated-by");
1335
+ const generatedBy = getHtmlNodeAttribute(node, "generated-by");
1370
1336
 
1371
- if (generatedBy) {
1372
- if (generatedBy.value === "jsenv:as_js_classic_html") {
1373
- if (!analyzeConvertedScripts) {
1374
- return;
1375
- }
1376
- }
1337
+ if (generatedBy === "jsenv:as_js_classic_html" && !analyzeConvertedScripts) {
1338
+ return;
1339
+ }
1377
1340
 
1378
- if (generatedBy.value === "jsenv:html_supervisor") {
1379
- return;
1380
- }
1341
+ if (generatedBy === "jsenv:html_supervisor") {
1342
+ return;
1381
1343
  }
1382
1344
 
1383
1345
  actions.push(async () => {
1384
- const scriptCategory = parseScriptNode(node);
1346
+ const scriptCategory = analyzeScriptNode(node);
1385
1347
  const {
1386
1348
  line,
1387
1349
  column,
1388
1350
  lineEnd,
1389
1351
  columnEnd,
1390
1352
  isOriginal
1391
- } = htmlNodePosition.readNodePosition(node, {
1353
+ } = getHtmlNodePosition(node, {
1392
1354
  preferOriginal: true
1393
1355
  }); // from MDN about [type] attribute:
1394
1356
  // "Any other value: The embedded content is treated as a data block
@@ -1424,21 +1386,25 @@ const jsenvPluginHtmlInlineContent = ({
1424
1386
  isOriginalPosition: isOriginal,
1425
1387
  specifier: inlineScriptUrl,
1426
1388
  contentType,
1427
- content: textNode.value
1389
+ content: htmlNodeText
1428
1390
  });
1429
1391
  await context.cook(inlineScriptUrlInfo, {
1430
1392
  reference: inlineScriptReference
1431
1393
  });
1432
- setHtmlNodeGeneratedText(node, {
1433
- generatedText: inlineScriptUrlInfo.content,
1434
- generatedBy: "jsenv:html_inline_content"
1394
+ setHtmlNodeText(node, inlineScriptUrlInfo.content);
1395
+ setHtmlNodeAttributes(node, {
1396
+ "generated-by": "jsenv:html_inline_content"
1435
1397
  });
1436
1398
  });
1437
1399
  };
1438
1400
 
1439
- visitHtmlAst(htmlAst, node => {
1440
- handleInlineStyle(node);
1441
- handleInlineScript(node);
1401
+ visitHtmlNodes(htmlAst, {
1402
+ style: node => {
1403
+ handleInlineStyle(node);
1404
+ },
1405
+ script: node => {
1406
+ handleInlineScript(node);
1407
+ }
1442
1408
  });
1443
1409
 
1444
1410
  if (actions.length === 0) {
@@ -1978,15 +1944,15 @@ const jsenvPluginHtmlSupervisor = ({
1978
1944
  const htmlAst = parseHtmlString(content);
1979
1945
  const scriptsToSupervise = [];
1980
1946
 
1981
- const handleInlineScript = (node, textNode) => {
1982
- const scriptCategory = parseScriptNode(node);
1947
+ const handleInlineScript = (node, htmlNodeText) => {
1948
+ const scriptCategory = analyzeScriptNode(node);
1983
1949
  const {
1984
1950
  line,
1985
1951
  column,
1986
1952
  lineEnd,
1987
1953
  columnEnd,
1988
1954
  isOriginal
1989
- } = htmlNodePosition.readNodePosition(node, {
1955
+ } = getHtmlNodePosition(node, {
1990
1956
  preferOriginal: true
1991
1957
  });
1992
1958
  let inlineScriptUrl = generateInlineContentUrl({
@@ -2008,7 +1974,7 @@ const jsenvPluginHtmlSupervisor = ({
2008
1974
  specifierColumn: column,
2009
1975
  specifier: inlineScriptUrl,
2010
1976
  contentType: "text/javascript",
2011
- content: textNode.value
1977
+ content: htmlNodeText
2012
1978
  });
2013
1979
  removeHtmlNodeText(node);
2014
1980
  scriptsToSupervise.push({
@@ -2019,21 +1985,19 @@ const jsenvPluginHtmlSupervisor = ({
2019
1985
  });
2020
1986
  };
2021
1987
 
2022
- const handleScriptWithSrc = (node, srcAttribute) => {
2023
- const scriptCategory = parseScriptNode(node);
2024
- const integrityAttribute = getHtmlNodeAttributeByName(node, "integrity");
2025
- const integrity = integrityAttribute ? integrityAttribute.value : undefined;
2026
- const crossoriginAttribute = getHtmlNodeAttributeByName(node, "crossorigin");
2027
- const crossorigin = crossoriginAttribute ? crossoriginAttribute.value : undefined;
2028
- const deferAttribute = getHtmlNodeAttributeByName(node, "crossorigin");
2029
- const defer = deferAttribute ? deferAttribute.value : undefined;
2030
- const asyncAttribute = getHtmlNodeAttributeByName(node, "crossorigin");
2031
- const async = asyncAttribute ? asyncAttribute.value : undefined;
2032
- removeHtmlNodeAttributeByName(node, "src");
1988
+ const handleScriptWithSrc = (node, src) => {
1989
+ const scriptCategory = analyzeScriptNode(node);
1990
+ const integrity = getHtmlNodeAttribute(node, "integrity");
1991
+ const crossorigin = getHtmlNodeAttribute(node, "crossorigin") !== undefined;
1992
+ const defer = getHtmlNodeAttribute(node, "defer") !== undefined;
1993
+ const async = getHtmlNodeAttribute(node, "async") !== undefined;
1994
+ setHtmlNodeAttributes(node, {
1995
+ src: undefined
1996
+ });
2033
1997
  scriptsToSupervise.push({
2034
1998
  node,
2035
1999
  type: scriptCategory,
2036
- src: srcAttribute.value,
2000
+ src,
2037
2001
  defer,
2038
2002
  async,
2039
2003
  integrity,
@@ -2041,41 +2005,39 @@ const jsenvPluginHtmlSupervisor = ({
2041
2005
  });
2042
2006
  };
2043
2007
 
2044
- visitHtmlAst(htmlAst, node => {
2045
- if (node.nodeName !== "script") {
2046
- return;
2047
- }
2048
-
2049
- const scriptCategory = parseScriptNode(node);
2008
+ visitHtmlNodes(htmlAst, {
2009
+ script: node => {
2010
+ const scriptCategory = analyzeScriptNode(node);
2050
2011
 
2051
- if (scriptCategory !== "classic" && scriptCategory !== "module") {
2052
- return;
2053
- }
2012
+ if (scriptCategory !== "classic" && scriptCategory !== "module") {
2013
+ return;
2014
+ }
2054
2015
 
2055
- const injectedByAttribute = getHtmlNodeAttributeByName(node, "injected-by");
2016
+ const injectedBy = getHtmlNodeAttribute(node, "injected-by");
2056
2017
 
2057
- if (injectedByAttribute) {
2058
- return;
2059
- }
2018
+ if (injectedBy !== undefined) {
2019
+ return;
2020
+ }
2060
2021
 
2061
- const noHtmlSupervisor = getHtmlNodeAttributeByName(node, "no-html-supervisor");
2022
+ const noHtmlSupervisor = getHtmlNodeAttribute(node, "no-html-supervisor");
2062
2023
 
2063
- if (noHtmlSupervisor) {
2064
- return;
2065
- }
2024
+ if (noHtmlSupervisor !== undefined) {
2025
+ return;
2026
+ }
2066
2027
 
2067
- const textNode = getHtmlNodeTextNode(node);
2028
+ const htmlNodeText = getHtmlNodeText(node);
2068
2029
 
2069
- if (textNode) {
2070
- handleInlineScript(node, textNode);
2071
- return;
2072
- }
2030
+ if (htmlNodeText) {
2031
+ handleInlineScript(node, htmlNodeText);
2032
+ return;
2033
+ }
2073
2034
 
2074
- const srcAttribute = getHtmlNodeAttributeByName(node, "src");
2035
+ const src = getHtmlNodeAttribute(node, "src");
2075
2036
 
2076
- if (srcAttribute) {
2077
- handleScriptWithSrc(node, srcAttribute);
2078
- return;
2037
+ if (src) {
2038
+ handleScriptWithSrc(node, src);
2039
+ return;
2040
+ }
2079
2041
  }
2080
2042
  });
2081
2043
  const [htmlSupervisorInstallerFileReference] = referenceUtils.inject({
@@ -2083,7 +2045,7 @@ const jsenvPluginHtmlSupervisor = ({
2083
2045
  expectedType: "js_module",
2084
2046
  specifier: htmlSupervisorInstallerFileUrl
2085
2047
  });
2086
- injectScriptAsEarlyAsPossible(htmlAst, createHtmlNode({
2048
+ injectScriptNodeAsEarlyAsPossible(htmlAst, createHtmlNode({
2087
2049
  "tagName": "script",
2088
2050
  "type": "module",
2089
2051
  "textContent": `
@@ -2099,7 +2061,7 @@ const jsenvPluginHtmlSupervisor = ({
2099
2061
  expectedType: "js_classic",
2100
2062
  specifier: htmlSupervisorSetupFileUrl
2101
2063
  });
2102
- injectScriptAsEarlyAsPossible(htmlAst, createHtmlNode({
2064
+ injectScriptNodeAsEarlyAsPossible(htmlAst, createHtmlNode({
2103
2065
  "tagName": "script",
2104
2066
  "src": htmlSupervisorSetupFileReference.generatedSpecifier,
2105
2067
  "injected-by": "jsenv:html_supervisor"
@@ -2114,20 +2076,24 @@ const jsenvPluginHtmlSupervisor = ({
2114
2076
  integrity,
2115
2077
  crossorigin
2116
2078
  }) => {
2117
- setHtmlNodeGeneratedText(node, {
2118
- generatedText: generateCodeToSuperviseScript({
2119
- type,
2120
- src,
2121
- isInline,
2122
- defer,
2123
- async,
2124
- integrity,
2125
- crossorigin,
2126
- htmlSupervisorInstallerSpecifier: htmlSupervisorInstallerFileReference.generatedSpecifier
2127
- }),
2128
- generatedBy: "jsenv:html_supervisor",
2129
- generatedFromSrc: src,
2130
- generatedFromInlineContent: isInline
2079
+ setHtmlNodeText(node, generateCodeToSuperviseScript({
2080
+ type,
2081
+ src,
2082
+ isInline,
2083
+ defer,
2084
+ async,
2085
+ integrity,
2086
+ crossorigin,
2087
+ htmlSupervisorInstallerSpecifier: htmlSupervisorInstallerFileReference.generatedSpecifier
2088
+ }));
2089
+ setHtmlNodeAttributes(node, {
2090
+ "generated-by": "jsenv:html_supervisor",
2091
+ ...(src ? {
2092
+ "generated-from-src": src
2093
+ } : {}),
2094
+ ...(isInline ? {
2095
+ "generated-from-inline-content": ""
2096
+ } : {})
2131
2097
  });
2132
2098
  });
2133
2099
  const htmlModified = stringifyHtmlAst(htmlAst);
@@ -2742,12 +2708,7 @@ const jsenvPluginAsJsClassicHtml = ({
2742
2708
  const classicScriptNodes = [];
2743
2709
 
2744
2710
  const visitLinkNodes = node => {
2745
- if (node.nodeName !== "link") {
2746
- return;
2747
- }
2748
-
2749
- const relAttribute = getHtmlNodeAttributeByName(node, "rel");
2750
- const rel = relAttribute ? relAttribute.value : undefined;
2711
+ const rel = getHtmlNodeAttribute(node, "rel");
2751
2712
 
2752
2713
  if (rel === "modulepreload") {
2753
2714
  modulePreloadNodes.push(node);
@@ -2755,8 +2716,7 @@ const jsenvPluginAsJsClassicHtml = ({
2755
2716
  }
2756
2717
 
2757
2718
  if (rel === "preload") {
2758
- const asAttribute = getHtmlNodeAttributeByName(node, "as");
2759
- const asValue = asAttribute ? asAttribute.value : undefined;
2719
+ const asValue = getHtmlNodeAttribute(node, "as");
2760
2720
 
2761
2721
  if (asValue === "script") {
2762
2722
  preloadAsScriptNodes.push(node);
@@ -2767,12 +2727,7 @@ const jsenvPluginAsJsClassicHtml = ({
2767
2727
  };
2768
2728
 
2769
2729
  const visitScriptNodes = node => {
2770
- if (node.nodeName !== "script") {
2771
- return;
2772
- }
2773
-
2774
- const typeAttribute = getHtmlNodeAttributeByName(node, "type");
2775
- const type = typeAttribute ? typeAttribute.value : undefined;
2730
+ const type = getHtmlNodeAttribute(node, "type");
2776
2731
 
2777
2732
  if (type === "module") {
2778
2733
  moduleScriptNodes.push(node);
@@ -2785,9 +2740,13 @@ const jsenvPluginAsJsClassicHtml = ({
2785
2740
  }
2786
2741
  };
2787
2742
 
2788
- visitHtmlAst(htmlAst, node => {
2789
- visitLinkNodes(node);
2790
- visitScriptNodes(node);
2743
+ visitHtmlNodes(htmlAst, {
2744
+ link: node => {
2745
+ visitLinkNodes(node);
2746
+ },
2747
+ script: node => {
2748
+ visitScriptNodes(node);
2749
+ }
2791
2750
  });
2792
2751
  const actions = [];
2793
2752
  const jsModuleUrls = [];
@@ -2829,10 +2788,10 @@ const jsenvPluginAsJsClassicHtml = ({
2829
2788
  };
2830
2789
 
2831
2790
  classicScriptNodes.forEach(classicScriptNode => {
2832
- const srcAttribute = getHtmlNodeAttributeByName(classicScriptNode, "src");
2791
+ const src = getHtmlNodeAttribute(classicScriptNode, "src");
2833
2792
 
2834
- if (srcAttribute) {
2835
- const reference = urlInfo.references.find(ref => ref.generatedSpecifier === srcAttribute.value && ref.type === "script_src");
2793
+ if (src !== undefined) {
2794
+ const reference = urlInfo.references.find(ref => ref.generatedSpecifier === src && ref.type === "script_src");
2836
2795
  const urlObject = new URL(reference.url);
2837
2796
 
2838
2797
  if (urlObject.searchParams.has("as_js_classic")) {
@@ -2851,10 +2810,10 @@ const jsenvPluginAsJsClassicHtml = ({
2851
2810
  }
2852
2811
  });
2853
2812
  moduleScriptNodes.forEach(moduleScriptNode => {
2854
- const srcAttribute = getHtmlNodeAttributeByName(moduleScriptNode, "src");
2813
+ const src = getHtmlNodeAttribute(moduleScriptNode, "src");
2855
2814
 
2856
- if (srcAttribute) {
2857
- const reference = urlInfo.references.find(ref => ref.generatedSpecifier === srcAttribute.value && ref.type === "script_src" && ref.expectedType === "js_module");
2815
+ if (src !== undefined) {
2816
+ const reference = urlInfo.references.find(ref => ref.generatedSpecifier === src && ref.type === "script_src" && ref.expectedType === "js_module");
2858
2817
  jsModuleUrls.push(reference.url);
2859
2818
 
2860
2819
  if (shouldTransformScriptTypeModule) {
@@ -2862,8 +2821,10 @@ const jsenvPluginAsJsClassicHtml = ({
2862
2821
  const [newReference] = await getReferenceAsJsClassic(reference, {
2863
2822
  cookIt: true
2864
2823
  });
2865
- removeHtmlNodeAttributeByName(moduleScriptNode, "type");
2866
- srcAttribute.value = newReference.generatedSpecifier;
2824
+ setHtmlNodeAttributes(moduleScriptNode, {
2825
+ type: undefined,
2826
+ src: newReference.generatedSpecifier
2827
+ });
2867
2828
  });
2868
2829
  }
2869
2830
 
@@ -2871,7 +2832,7 @@ const jsenvPluginAsJsClassicHtml = ({
2871
2832
  }
2872
2833
 
2873
2834
  if (shouldTransformScriptTypeModule) {
2874
- const textNode = getHtmlNodeTextNode(moduleScriptNode);
2835
+ const htmlNodeText = getHtmlNodeText(moduleScriptNode);
2875
2836
  actions.push(async () => {
2876
2837
  const {
2877
2838
  line,
@@ -2879,7 +2840,7 @@ const jsenvPluginAsJsClassicHtml = ({
2879
2840
  lineEnd,
2880
2841
  columnEnd,
2881
2842
  isOriginal
2882
- } = htmlNodePosition.readNodePosition(moduleScriptNode, {
2843
+ } = getHtmlNodePosition(moduleScriptNode, {
2883
2844
  preferOriginal: true
2884
2845
  });
2885
2846
  let inlineScriptUrl = generateInlineContentUrl({
@@ -2902,15 +2863,15 @@ const jsenvPluginAsJsClassicHtml = ({
2902
2863
  specifierColumn: column,
2903
2864
  specifier: inlineScriptUrl,
2904
2865
  contentType: "text/javascript",
2905
- content: textNode.value
2866
+ content: htmlNodeText
2906
2867
  });
2907
2868
  const [, newUrlInfo] = await getReferenceAsJsClassic(inlineReference, {
2908
2869
  cookIt: true
2909
2870
  });
2910
- removeHtmlNodeAttributeByName(moduleScriptNode, "type");
2911
- setHtmlNodeGeneratedText(moduleScriptNode, {
2912
- generatedText: newUrlInfo.content,
2913
- generatedBy: "jsenv:as_js_classic_html"
2871
+ setHtmlNodeText(moduleScriptNode, newUrlInfo.content);
2872
+ setHtmlNodeAttributes(moduleScriptNode, {
2873
+ "type": undefined,
2874
+ "generated-by": "jsenv:as_js_classic_html"
2914
2875
  });
2915
2876
  });
2916
2877
  }
@@ -2918,8 +2879,7 @@ const jsenvPluginAsJsClassicHtml = ({
2918
2879
 
2919
2880
  if (shouldTransformScriptTypeModule) {
2920
2881
  preloadAsScriptNodes.forEach(preloadAsScriptNode => {
2921
- const hrefAttribute = getHtmlNodeAttributeByName(preloadAsScriptNode, "href");
2922
- const href = hrefAttribute.value;
2882
+ const href = getHtmlNodeAttribute(preloadAsScriptNode, "href");
2923
2883
  const reference = urlInfo.references.find(ref => ref.generatedSpecifier === href && ref.type === "link_href" && ref.expectedType === undefined);
2924
2884
  const expectedScriptType = jsModuleUrls.includes(reference.url) ? "module" : "classic";
2925
2885
 
@@ -2934,16 +2894,15 @@ const jsenvPluginAsJsClassicHtml = ({
2934
2894
  [newReference] = await getReferenceAsJsClassic(reference);
2935
2895
  }
2936
2896
 
2937
- assignHtmlNodeAttributes(preloadAsScriptNode, {
2938
- href: newReference.generatedSpecifier
2897
+ setHtmlNodeAttributes(preloadAsScriptNode, {
2898
+ href: newReference.generatedSpecifier,
2899
+ crossorigin: undefined
2939
2900
  });
2940
- removeHtmlNodeAttributeByName(preloadAsScriptNode, "crossorigin");
2941
2901
  });
2942
2902
  }
2943
2903
  });
2944
2904
  modulePreloadNodes.forEach(modulePreloadNode => {
2945
- const hrefAttribute = getHtmlNodeAttributeByName(modulePreloadNode, "href");
2946
- const href = hrefAttribute.value;
2905
+ const href = getHtmlNodeAttribute(modulePreloadNode, "href");
2947
2906
  const reference = urlInfo.references.find(ref => ref.generatedSpecifier === href && ref.type === "link_href" && ref.expectedType === "js_module");
2948
2907
  actions.push(async () => {
2949
2908
  let newReference;
@@ -2954,7 +2913,7 @@ const jsenvPluginAsJsClassicHtml = ({
2954
2913
  [newReference] = await getReferenceAsJsClassic(reference);
2955
2914
  }
2956
2915
 
2957
- assignHtmlNodeAttributes(modulePreloadNode, {
2916
+ setHtmlNodeAttributes(modulePreloadNode, {
2958
2917
  rel: "preload",
2959
2918
  as: "script",
2960
2919
  href: newReference.generatedSpecifier
@@ -2978,7 +2937,7 @@ const jsenvPluginAsJsClassicHtml = ({
2978
2937
  expectedType: "js_classic",
2979
2938
  specifier: systemJsClientFileUrl
2980
2939
  });
2981
- injectScriptAsEarlyAsPossible(htmlAst, createHtmlNode({
2940
+ injectScriptNodeAsEarlyAsPossible(htmlAst, createHtmlNode({
2982
2941
  "tagName": "script",
2983
2942
  "src": systemJsReference.generatedSpecifier,
2984
2943
  "injected-by": "jsenv:as_js_classic_html"
@@ -4057,7 +4016,7 @@ const babelPluginBabelHelpersAsJsenvImports = (babel, {
4057
4016
  }
4058
4017
 
4059
4018
  const babelHelperImportSpecifier = getBabelHelperFileUrl(name);
4060
- const helper = injectImport({
4019
+ const helper = injectJsImport({
4061
4020
  programPath: file.path,
4062
4021
  from: getImportSpecifier(babelHelperImportSpecifier),
4063
4022
  nameHint: `_${name}`,
@@ -4160,7 +4119,7 @@ const babelPluginNewStylesheetAsJsenvImport = (babel, {
4160
4119
  });
4161
4120
 
4162
4121
  if (usesNewStylesheet) {
4163
- injectImport({
4122
+ injectJsImport({
4164
4123
  programPath,
4165
4124
  from: getImportSpecifier(newStylesheetClientFileUrl),
4166
4125
  sideEffect: true
@@ -4225,7 +4184,7 @@ const babelPluginGlobalThisAsJsenvImport = (babel, {
4225
4184
  } = path; // we should do this once, tree shaking will remote it but still
4226
4185
 
4227
4186
  if (node.name === "globalThis") {
4228
- injectImport({
4187
+ injectJsImport({
4229
4188
  programPath: path.scope.getProgramParent().path,
4230
4189
  from: getImportSpecifier(globalThisClientFileUrl),
4231
4190
  sideEffect: true
@@ -4259,7 +4218,7 @@ const babelPluginRegeneratorRuntimeAsJsenvImport = (babel, {
4259
4218
  } = path;
4260
4219
 
4261
4220
  if (node.name === "regeneratorRuntime") {
4262
- injectImport({
4221
+ injectJsImport({
4263
4222
  programPath: path.scope.getProgramParent().path,
4264
4223
  from: getImportSpecifier(regeneratorRuntimeClientFileUrl),
4265
4224
  sideEffect: true
@@ -4503,6 +4462,39 @@ const jsenvPluginNodeRuntime = ({
4503
4462
  };
4504
4463
  };
4505
4464
 
4465
+ const sortByDependencies = nodes => {
4466
+ const visited = [];
4467
+ const sorted = [];
4468
+ const circular = [];
4469
+
4470
+ const visit = url => {
4471
+ const isSorted = sorted.includes(url);
4472
+
4473
+ if (isSorted) {
4474
+ return;
4475
+ }
4476
+
4477
+ const isVisited = visited.includes(url);
4478
+
4479
+ if (isVisited) {
4480
+ circular.push(url);
4481
+ sorted.push(url);
4482
+ } else {
4483
+ visited.push(url);
4484
+ nodes[url].dependencies.forEach(dependencyUrl => {
4485
+ visit(dependencyUrl);
4486
+ });
4487
+ sorted.push(url);
4488
+ }
4489
+ };
4490
+
4491
+ Object.keys(nodes).forEach(url => {
4492
+ visit(url);
4493
+ });
4494
+ sorted.circular = circular;
4495
+ return sorted;
4496
+ };
4497
+
4506
4498
  /*
4507
4499
  * Each @import found in css is replaced by the file content
4508
4500
  * - There is no need to worry about urls (such as background-image: url())
@@ -4720,71 +4712,6 @@ const bundleJsModule = async ({
4720
4712
  });
4721
4713
  return jsModuleBundleUrlInfos;
4722
4714
  };
4723
- const buildWithRollup = async ({
4724
- signal,
4725
- logger,
4726
- rootDirectoryUrl,
4727
- buildDirectoryUrl,
4728
- urlGraph,
4729
- jsModuleUrlInfos,
4730
- runtimeCompat,
4731
- sourcemaps,
4732
- include,
4733
- babelHelpersChunk
4734
- }) => {
4735
- const resultRef = {
4736
- current: null
4737
- };
4738
-
4739
- try {
4740
- await applyRollupPlugins({
4741
- rollupPlugins: [rollupPluginJsenv({
4742
- signal,
4743
- logger,
4744
- rootDirectoryUrl,
4745
- buildDirectoryUrl,
4746
- urlGraph,
4747
- jsModuleUrlInfos,
4748
- runtimeCompat,
4749
- sourcemaps,
4750
- include,
4751
- babelHelpersChunk,
4752
- resultRef
4753
- })],
4754
- inputOptions: {
4755
- input: [],
4756
- onwarn: warning => {
4757
- if (warning.code === "CIRCULAR_DEPENDENCY") {
4758
- return;
4759
- }
4760
-
4761
- if (warning.code === "THIS_IS_UNDEFINED" && pathToFileURL(warning.id).href === globalThisClientFileUrl) {
4762
- return;
4763
- }
4764
-
4765
- if (warning.code === "EVAL") {
4766
- // ideally we should disable only for jsenv files
4767
- return;
4768
- }
4769
-
4770
- logger.warn(String(warning));
4771
- }
4772
- }
4773
- });
4774
- return resultRef.current;
4775
- } catch (e) {
4776
- if (e.code === "MISSING_EXPORT") {
4777
- const detailedMessage = createDetailedMessage(e.message, {
4778
- frame: e.frame
4779
- });
4780
- throw new Error(detailedMessage, {
4781
- cause: e
4782
- });
4783
- }
4784
-
4785
- throw e;
4786
- }
4787
- };
4788
4715
 
4789
4716
  const rollupPluginJsenv = ({
4790
4717
  // logger,
@@ -5010,6 +4937,91 @@ const rollupPluginJsenv = ({
5010
4937
  };
5011
4938
  };
5012
4939
 
4940
+ const buildWithRollup = async ({
4941
+ signal,
4942
+ logger,
4943
+ rootDirectoryUrl,
4944
+ buildDirectoryUrl,
4945
+ urlGraph,
4946
+ jsModuleUrlInfos,
4947
+ runtimeCompat,
4948
+ sourcemaps,
4949
+ include,
4950
+ babelHelpersChunk
4951
+ }) => {
4952
+ const resultRef = {
4953
+ current: null
4954
+ };
4955
+
4956
+ try {
4957
+ await applyRollupPlugins({
4958
+ rollupPlugins: [rollupPluginJsenv({
4959
+ signal,
4960
+ logger,
4961
+ rootDirectoryUrl,
4962
+ buildDirectoryUrl,
4963
+ urlGraph,
4964
+ jsModuleUrlInfos,
4965
+ runtimeCompat,
4966
+ sourcemaps,
4967
+ include,
4968
+ babelHelpersChunk,
4969
+ resultRef
4970
+ })],
4971
+ inputOptions: {
4972
+ input: [],
4973
+ onwarn: warning => {
4974
+ if (warning.code === "CIRCULAR_DEPENDENCY") {
4975
+ return;
4976
+ }
4977
+
4978
+ if (warning.code === "THIS_IS_UNDEFINED" && pathToFileURL(warning.id).href === globalThisClientFileUrl) {
4979
+ return;
4980
+ }
4981
+
4982
+ if (warning.code === "EVAL") {
4983
+ // ideally we should disable only for jsenv files
4984
+ return;
4985
+ }
4986
+
4987
+ logger.warn(String(warning));
4988
+ }
4989
+ }
4990
+ });
4991
+ return resultRef.current;
4992
+ } catch (e) {
4993
+ if (e.code === "MISSING_EXPORT") {
4994
+ const detailedMessage = createDetailedMessage(e.message, {
4995
+ frame: e.frame
4996
+ });
4997
+ throw new Error(detailedMessage, {
4998
+ cause: e
4999
+ });
5000
+ }
5001
+
5002
+ throw e;
5003
+ }
5004
+ };
5005
+
5006
+ const applyRollupPlugins = async ({
5007
+ rollupPlugins,
5008
+ inputOptions = {},
5009
+ outputOptions = {}
5010
+ }) => {
5011
+ const {
5012
+ rollup
5013
+ } = await import("rollup");
5014
+ const {
5015
+ importAssertions
5016
+ } = await import("acorn-import-assertions");
5017
+ const rollupReturnValue = await rollup({ ...inputOptions,
5018
+ plugins: rollupPlugins,
5019
+ acornInjectPlugins: [importAssertions, ...(inputOptions.acornInjectPlugins || [])]
5020
+ });
5021
+ const rollupOutputArray = await rollupReturnValue.generate(outputOptions);
5022
+ return rollupOutputArray;
5023
+ };
5024
+
5013
5025
  const willBeInsideJsDirectory = ({
5014
5026
  chunkInfo,
5015
5027
  fileUrlConverter,
@@ -5252,8 +5264,7 @@ const collectHotDataFromHtmlAst = htmlAst => {
5252
5264
  attributeName,
5253
5265
  hotAccepted
5254
5266
  }) => {
5255
- const attribute = getHtmlNodeAttributeByName(node, attributeName);
5256
- const value = attribute ? attribute.value : undefined;
5267
+ const value = getHtmlNodeAttribute(node, attributeName);
5257
5268
 
5258
5269
  if (value) {
5259
5270
  onSpecifier({
@@ -5300,11 +5311,10 @@ const collectHotDataFromHtmlAst = htmlAst => {
5300
5311
  }
5301
5312
 
5302
5313
  if (nodeNamesWithSrcset.includes(node.nodeName)) {
5303
- const srcsetAttribute = getHtmlNodeAttributeByName(node, "srcset");
5304
- const srcset = srcsetAttribute ? srcsetAttribute.value : undefined;
5314
+ const srcset = getHtmlNodeAttribute(node, "srcset");
5305
5315
 
5306
5316
  if (srcset) {
5307
- const srcCandidates = htmlAttributeSrcSet.parse(srcset);
5317
+ const srcCandidates = parseSrcSet(srcset);
5308
5318
  srcCandidates.forEach(srcCandidate => {
5309
5319
  onSpecifier({
5310
5320
  node,
@@ -5345,15 +5355,15 @@ const nodeNamesWithSrcset = ["img", "source"];
5345
5355
 
5346
5356
  const getNodeContext = node => {
5347
5357
  const context = {};
5348
- const hotAcceptAttribute = getHtmlNodeAttributeByName(node, "hot-accept");
5358
+ const hotAccept = getHtmlNodeAttribute(node, "hot-accept");
5349
5359
 
5350
- if (hotAcceptAttribute) {
5360
+ if (hotAccept !== undefined) {
5351
5361
  context.hotAccepted = true;
5352
5362
  }
5353
5363
 
5354
- const hotDeclineAttribute = getHtmlNodeAttributeByName(node, "hot-decline");
5364
+ const hotDecline = getHtmlNodeAttribute(node, "hot-decline");
5355
5365
 
5356
- if (hotDeclineAttribute) {
5366
+ if (hotDecline !== undefined) {
5357
5367
  context.hotAccepted = false;
5358
5368
  }
5359
5369
 
@@ -5366,7 +5376,7 @@ const htmlNodeCanHotReload = node => {
5366
5376
  isStylesheet,
5367
5377
  isRessourceHint,
5368
5378
  rel
5369
- } = parseLinkNode(node);
5379
+ } = analyzeLinkNode(node);
5370
5380
 
5371
5381
  if (isStylesheet) {
5372
5382
  // stylesheets can be hot replaced by default
@@ -5666,7 +5676,7 @@ const jsenvPluginDevSSEClient = () => {
5666
5676
  expectedType: "js_module",
5667
5677
  specifier: eventSourceClientFileUrl
5668
5678
  });
5669
- injectScriptAsEarlyAsPossible(htmlAst, createHtmlNode({
5679
+ injectScriptNodeAsEarlyAsPossible(htmlAst, createHtmlNode({
5670
5680
  "tagName": "script",
5671
5681
  "type": "module",
5672
5682
  "src": eventSourceClientReference.generatedSpecifier,
@@ -6706,13 +6716,27 @@ const createUrlInfoTransformer = ({
6706
6716
  const sourcemapsEnabled = sourcemaps === "inline" || sourcemaps === "file" || sourcemaps === "programmatic";
6707
6717
 
6708
6718
  const normalizeSourcemap = (urlInfo, sourcemap) => {
6719
+ let {
6720
+ sources
6721
+ } = sourcemap;
6722
+
6723
+ if (sources) {
6724
+ sources = sources.map(source => {
6725
+ if (source && isFileSystemPath(source)) {
6726
+ return String(pathToFileURL(source));
6727
+ }
6728
+
6729
+ return source;
6730
+ });
6731
+ }
6732
+
6709
6733
  const wantSourcesContent = // for inline content (<script> insdide html)
6710
6734
  // chrome won't be able to fetch the file as it does not exists
6711
6735
  // so sourcemap must contain sources
6712
- sourcemapsSourcesContent || urlInfo.isInline || sourcemap.sources && sourcemap.sources.some(source => !source || !source.startsWith("file:"));
6736
+ sourcemapsSourcesContent || urlInfo.isInline || sources && sources.some(source => !source || !source.startsWith("file:"));
6713
6737
 
6714
- if (sourcemap.sources && sourcemap.sources.length > 1) {
6715
- sourcemap.sources = sourcemap.sources.map(source => new URL(source, urlInfo.originalUrl).href);
6738
+ if (sources && sources.length > 1) {
6739
+ sourcemap.sources = sources.map(source => new URL(source, urlInfo.originalUrl).href);
6716
6740
 
6717
6741
  if (!wantSourcesContent) {
6718
6742
  sourcemap.sourcesContent = undefined;
@@ -6748,7 +6772,7 @@ const createUrlInfoTransformer = ({
6748
6772
  // but otherwise it's generatedUrl to be inside .jsenv/ directory
6749
6773
 
6750
6774
 
6751
- urlInfo.sourcemapGeneratedUrl = generateSourcemapUrl(urlInfo.generatedUrl);
6775
+ urlInfo.sourcemapGeneratedUrl = generateSourcemapFileUrl(urlInfo.generatedUrl);
6752
6776
  const [sourcemapReference, sourcemapUrlInfo] = injectSourcemapPlaceholder({
6753
6777
  urlInfo,
6754
6778
  specifier: urlInfo.sourcemapGeneratedUrl
@@ -6854,7 +6878,7 @@ const createUrlInfoTransformer = ({
6854
6878
  sourcemapUrlInfo.content = JSON.stringify(sourcemap, null, " ");
6855
6879
 
6856
6880
  if (sourcemaps === "inline") {
6857
- sourcemapReference.generatedSpecifier = sourcemapToBase64Url(sourcemap);
6881
+ sourcemapReference.generatedSpecifier = generateSourcemapDataUrl(sourcemap);
6858
6882
  }
6859
6883
 
6860
6884
  if (sourcemaps === "file" || sourcemaps === "inline") {
@@ -7759,6 +7783,8 @@ const memoizeCook = cook => {
7759
7783
  const applyReferenceEffectsOnUrlInfo = (reference, urlInfo, context) => {
7760
7784
  if (reference.shouldHandle) {
7761
7785
  urlInfo.shouldHandle = true;
7786
+ } else {
7787
+ urlInfo.shouldHandle = false;
7762
7788
  }
7763
7789
 
7764
7790
  urlInfo.originalUrl = urlInfo.originalUrl || reference.url;
@@ -8368,7 +8394,7 @@ const jsenvPluginExplorer = ({
8368
8394
  meta
8369
8395
  }));
8370
8396
  let html = String(readFileSync(new URL(htmlClientFileUrl)));
8371
- html = html.replace("virtual:FAVICON_HREF", DATA_URL.stringify({
8397
+ html = html.replace("ignore:FAVICON_HREF", DATA_URL.stringify({
8372
8398
  contentType: CONTENT_TYPE.fromUrlExtension(faviconClientFileUrl),
8373
8399
  base64Flag: true,
8374
8400
  data: readFileSync(new URL(faviconClientFileUrl)).toString("base64")
@@ -8521,7 +8547,12 @@ const startDevServer = async ({
8521
8547
  stopWatchingDevServerFiles();
8522
8548
  reloadableWorker.terminate();
8523
8549
  });
8524
- await reloadableWorker.load();
8550
+ const worker = await reloadableWorker.load();
8551
+
8552
+ if (!keepProcessAlive) {
8553
+ worker.unref();
8554
+ }
8555
+
8525
8556
  return {
8526
8557
  origin: `${protocol}://127.0.0.1:${port}`,
8527
8558
  stop: () => {
@@ -9051,7 +9082,7 @@ const normalizeFileByFileCoveragePaths = (fileByFileCoverage, rootDirectoryUrl)
9051
9082
  const {
9052
9083
  path
9053
9084
  } = fileCoverage;
9054
- const url = isFileSystemPath(path) ? fileSystemPathToUrl(path) : resolveUrl(path, rootDirectoryUrl);
9085
+ const url = isFileSystemPath(path) ? fileSystemPathToUrl(path) : new URL(path, rootDirectoryUrl).href;
9055
9086
  const relativeUrl = urlToRelativeUrl(url, rootDirectoryUrl);
9056
9087
  fileByFileNormalized[`./${relativeUrl}`] = { ...fileCoverage,
9057
9088
  path: `./${relativeUrl}`
@@ -12317,7 +12348,7 @@ const injectors = {
12317
12348
  const htmlAst = parseHtmlString(urlInfo.content, {
12318
12349
  storeOriginalPositions: false
12319
12350
  });
12320
- injectScriptAsEarlyAsPossible(htmlAst, createHtmlNode({
12351
+ injectScriptNodeAsEarlyAsPossible(htmlAst, createHtmlNode({
12321
12352
  "tagName": "script",
12322
12353
  "textContent": generateClientCodeForVersionMappings(versionMappings, {
12323
12354
  globalName: "window"
@@ -12348,6 +12379,60 @@ ${globalName}.__v__ = function (specifier) {
12348
12379
  `;
12349
12380
  };
12350
12381
 
12382
+ // https://github.com/rollup/rollup/blob/5a5391971d695c808eed0c5d7d2c6ccb594fc689/src/Chunk.ts#L870
12383
+
12384
+ const createVersionGenerator = () => {
12385
+ const hash = createHash("sha256");
12386
+
12387
+ const augmentWithContent = ({
12388
+ content,
12389
+ contentType = "application/octet-stream",
12390
+ lineBreakNormalization = false
12391
+ }) => {
12392
+ hash.update(lineBreakNormalization && CONTENT_TYPE.isTextual(contentType) ? normalizeLineBreaks(content) : content);
12393
+ };
12394
+
12395
+ const augmentWithDependencyVersion = version => {
12396
+ hash.update(version);
12397
+ };
12398
+
12399
+ return {
12400
+ augmentWithContent,
12401
+ augmentWithDependencyVersion,
12402
+ generate: () => {
12403
+ return hash.digest("hex").slice(0, 8);
12404
+ }
12405
+ };
12406
+ };
12407
+
12408
+ const normalizeLineBreaks = stringOrBuffer => {
12409
+ if (typeof stringOrBuffer === "string") {
12410
+ const stringWithLinuxBreaks = stringOrBuffer.replace(/\r\n/g, "\n");
12411
+ return stringWithLinuxBreaks;
12412
+ }
12413
+
12414
+ return normalizeLineBreaksForBuffer(stringOrBuffer);
12415
+ }; // https://github.com/nodejs/help/issues/1738#issuecomment-458460503
12416
+
12417
+
12418
+ const normalizeLineBreaksForBuffer = buffer => {
12419
+ const int32Array = new Int32Array(buffer, 0, buffer.length);
12420
+ const int32ArrayWithLineBreaksNormalized = int32Array.filter((element, index, typedArray) => {
12421
+ if (element === 0x0d) {
12422
+ if (typedArray[index + 1] === 0x0a) {
12423
+ // Windows -> Unix
12424
+ return false;
12425
+ } // Mac OS -> Unix
12426
+
12427
+
12428
+ typedArray[index] = 0x0a;
12429
+ }
12430
+
12431
+ return true;
12432
+ });
12433
+ return Buffer.from(int32ArrayWithLineBreaksNormalized);
12434
+ };
12435
+
12351
12436
  const injectServiceWorkerUrls = async ({
12352
12437
  finalGraph,
12353
12438
  finalGraphKitchen,
@@ -12445,15 +12530,12 @@ const resyncRessourceHints = async ({
12445
12530
  });
12446
12531
  const actions = [];
12447
12532
 
12448
- const visitLinkWithHref = (linkNode, hrefAttribute) => {
12449
- const href = hrefAttribute.value;
12450
-
12533
+ const visitLinkWithHref = (linkNode, href) => {
12451
12534
  if (!href || href.startsWith("data:")) {
12452
12535
  return;
12453
12536
  }
12454
12537
 
12455
- const relAttribute = getHtmlNodeAttributeByName(linkNode, "rel");
12456
- const rel = relAttribute ? relAttribute.value : undefined;
12538
+ const rel = getHtmlNodeAttribute(linkNode, "rel");
12457
12539
  const isRessourceHint = ["preconnect", "dns-prefetch", "prefetch", "preload", "modulepreload"].includes(rel);
12458
12540
 
12459
12541
  if (!isRessourceHint) {
@@ -12497,22 +12579,20 @@ const resyncRessourceHints = async ({
12497
12579
  }
12498
12580
 
12499
12581
  actions.push(() => {
12500
- hrefAttribute.value = urlInfo.data.buildUrlSpecifier;
12582
+ setHtmlNodeAttributes(linkNode, {
12583
+ href: urlInfo.data.buildUrlSpecifier
12584
+ });
12501
12585
  });
12502
12586
  };
12503
12587
 
12504
- visitHtmlAst(htmlAst, node => {
12505
- if (node.nodeName !== "link") {
12506
- return;
12507
- }
12508
-
12509
- const hrefAttribute = getHtmlNodeAttributeByName(node, "href");
12588
+ visitHtmlNodes(htmlAst, {
12589
+ link: node => {
12590
+ const href = getHtmlNodeAttribute(node, "href");
12510
12591
 
12511
- if (!hrefAttribute) {
12512
- return;
12592
+ if (href !== undefined) {
12593
+ visitLinkWithHref(node, href);
12594
+ }
12513
12595
  }
12514
-
12515
- visitLinkWithHref(node, hrefAttribute);
12516
12596
  });
12517
12597
 
12518
12598
  if (actions.length) {
@@ -13077,7 +13157,7 @@ build ${entryPointKeys.length} entry points`);
13077
13157
 
13078
13158
  if (reference.type === "sourcemap_comment") {
13079
13159
  // inherit parent build url
13080
- return generateSourcemapUrl(reference.parentUrl);
13160
+ return generateSourcemapFileUrl(reference.parentUrl);
13081
13161
  } // files generated during the final graph:
13082
13162
  // - sourcemaps
13083
13163
  // const finalUrlInfo = finalGraph.getUrlInfo(url)
@@ -13803,6 +13883,7 @@ const startBuildServer = async ({
13803
13883
  ip,
13804
13884
  port = 9779,
13805
13885
  services = {},
13886
+ keepProcessAlive = true,
13806
13887
  rootDirectoryUrl,
13807
13888
  buildDirectoryUrl,
13808
13889
  mainBuildFileUrl = "/index.html",
@@ -13892,7 +13973,12 @@ const startBuildServer = async ({
13892
13973
  stopWatchingBuildServerFiles();
13893
13974
  reloadableWorker.terminate();
13894
13975
  });
13895
- await reloadableWorker.load();
13976
+ const worker = await reloadableWorker.load();
13977
+
13978
+ if (!keepProcessAlive) {
13979
+ worker.unref();
13980
+ }
13981
+
13896
13982
  return {
13897
13983
  origin: `${protocol}://127.0.0.1:${port}`,
13898
13984
  stop: () => {
@@ -14150,7 +14236,7 @@ const globalInjectorOnHtml = async (urlInfo, globals) => {
14150
14236
  globals,
14151
14237
  isWebWorker: false
14152
14238
  });
14153
- injectScriptAsEarlyAsPossible(htmlAst, createHtmlNode({
14239
+ injectScriptNodeAsEarlyAsPossible(htmlAst, createHtmlNode({
14154
14240
  "tagName": "script",
14155
14241
  "textContent": clientCode,
14156
14242
  "injected-by": "jsenv:inject_globals"