@jsenv/core 25.0.1 → 25.2.1

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 (63) hide show
  1. package/dist/browser_runtime/browser_runtime_91c5a3b8.js.map +2 -2
  2. package/dist/build_manifest.js +4 -4
  3. package/dist/compile_proxy/asset-manifest.json +2 -2
  4. package/dist/compile_proxy/{compile_proxy_e3b0c442_809f35f7.js.map → compile_proxy.html__inline__20_809f35f7.js.map} +0 -0
  5. package/dist/compile_proxy/{compile_proxy_7ad5faa6.html → compile_proxy_8dfaee51.html} +3 -4
  6. package/dist/redirector/asset-manifest.json +2 -2
  7. package/dist/redirector/{redirector_e3b0c442_e391410e.js.map → redirector.html__inline__15_e391410e.js.map} +0 -0
  8. package/dist/redirector/{redirector_eb92e8a7.html → redirector_3e9a97b9.html} +3 -4
  9. package/dist/toolbar/asset-manifest.json +1 -1
  10. package/dist/toolbar/{toolbar_f7b8a263.html → toolbar_361afb84.html} +2 -3
  11. package/dist/toolbar_injector/asset-manifest.json +2 -2
  12. package/dist/toolbar_injector/{toolbar_injector_49e4756e.js → toolbar_injector_fac1e995.js} +2 -2
  13. package/dist/toolbar_injector/{toolbar_injector_49e4756e.js.map → toolbar_injector_fac1e995.js.map} +2 -2
  14. package/package.json +9 -10
  15. package/readme.md +60 -51
  16. package/src/buildProject.js +21 -13
  17. package/src/commonJsToJavaScriptModule.js +8 -7
  18. package/src/dev_server.js +2 -0
  19. package/src/execute.js +2 -0
  20. package/src/executeTestPlan.js +14 -0
  21. package/src/internal/building/buildUsingRollup.js +4 -2
  22. package/src/internal/building/build_stats.js +3 -0
  23. package/src/internal/building/build_url_generator.js +153 -0
  24. package/src/internal/building/css/parseCssRessource.js +32 -26
  25. package/src/internal/building/html/parseHtmlRessource.js +109 -91
  26. package/src/internal/building/js/parseJsRessource.js +5 -13
  27. package/src/internal/building/parseRessource.js +3 -0
  28. package/src/internal/building/ressource_builder.js +72 -64
  29. package/src/internal/building/ressource_builder_util.js +17 -5
  30. package/src/internal/building/rollup_plugin_jsenv.js +262 -189
  31. package/src/internal/building/url_fetcher.js +16 -7
  32. package/src/internal/building/url_loader.js +1 -5
  33. package/src/internal/building/url_versioning.js +0 -173
  34. package/src/internal/compiling/babel_plugin_import_metadata.js +7 -11
  35. package/src/internal/compiling/babel_plugin_proxy_external_imports.js +31 -0
  36. package/src/internal/compiling/compile-directory/compile-asset.js +8 -4
  37. package/src/internal/compiling/compile-directory/getOrGenerateCompiledFile.js +43 -8
  38. package/src/internal/compiling/compile-directory/updateMeta.js +4 -8
  39. package/src/internal/compiling/compile-directory/validateCache.js +1 -2
  40. package/src/internal/compiling/compileFile.js +22 -10
  41. package/src/internal/compiling/compileHtml.js +15 -28
  42. package/src/internal/compiling/createCompiledFileService.js +22 -24
  43. package/src/internal/compiling/html_source_file_service.js +18 -19
  44. package/src/internal/compiling/js-compilation-service/babelHelper.js +10 -13
  45. package/src/internal/compiling/js-compilation-service/babel_plugin_babel_helpers_as_jsenv_imports.js +4 -2
  46. package/src/internal/compiling/js-compilation-service/jsenvTransform.js +16 -7
  47. package/src/internal/compiling/js-compilation-service/transformJs.js +9 -5
  48. package/src/internal/compiling/jsenvCompilerForHtml.js +536 -262
  49. package/src/internal/compiling/jsenvCompilerForJavaScript.js +15 -11
  50. package/src/internal/compiling/startCompileServer.js +83 -20
  51. package/src/internal/compiling/transformResultToCompilationResult.js +47 -25
  52. package/src/internal/executing/executePlan.js +2 -0
  53. package/src/internal/fetchUrl.js +3 -2
  54. package/src/internal/integrity/integrity_algorithms.js +26 -0
  55. package/src/internal/integrity/integrity_parsing.js +50 -0
  56. package/src/internal/integrity/integrity_update.js +23 -0
  57. package/src/internal/integrity/integrity_validation.js +49 -0
  58. package/src/internal/jsenvCoreDirectoryUrl.js +2 -0
  59. package/src/internal/jsenv_remote_directory.js +156 -0
  60. package/src/internal/origin_directory_converter.js +62 -0
  61. package/src/internal/response_validation.js +11 -24
  62. package/src/internal/sourceMappingURLUtils.js +10 -0
  63. package/src/internal/url_conversion.js +1 -0
@@ -1,7 +1,13 @@
1
- import { resolveUrl, urlToRelativeUrl } from "@jsenv/filesystem"
1
+ import {
2
+ resolveUrl,
3
+ urlToFilename,
4
+ urlToRelativeUrl,
5
+ urlIsInsideOf,
6
+ } from "@jsenv/filesystem"
2
7
  import { moveImportMap, composeTwoImportMaps } from "@jsenv/importmap"
3
8
  import { createDetailedMessage } from "@jsenv/logger"
4
9
 
10
+ import { jsenvDistDirectoryUrl } from "@jsenv/core/src/internal/jsenvCoreDirectoryUrl.js"
5
11
  import {
6
12
  BROWSER_RUNTIME_BUILD_URL,
7
13
  EVENT_SOURCE_CLIENT_BUILD_URL,
@@ -11,6 +17,7 @@ import { fetchUrl } from "@jsenv/core/src/internal/fetchUrl.js"
11
17
  import { getDefaultImportmap } from "@jsenv/core/src/internal/import-resolution/importmap_default.js"
12
18
 
13
19
  import {
20
+ generateSourcemapUrl,
14
21
  setJavaScriptSourceMappingUrl,
15
22
  sourcemapToBase64Url,
16
23
  } from "../sourceMappingURLUtils.js"
@@ -23,47 +30,46 @@ import {
23
30
  stringifyHtmlAst,
24
31
  getHtmlNodeAttributeByName,
25
32
  getHtmlNodeTextNode,
26
- getUniqueNameForInlineHtmlNode,
33
+ getIdForInlineHtmlNode,
27
34
  removeHtmlNodeAttribute,
28
35
  setHtmlNodeText,
29
36
  visitHtmlAst,
30
- replaceHtmlNode,
37
+ addHtmlNodeAttribute,
31
38
  } from "./compileHtml.js"
32
- import { generateCompiledFileAssetUrl } from "./compile-directory/compile-asset.js"
39
+ import { generateCompilationAssetUrl } from "./compile-directory/compile-asset.js"
33
40
 
34
41
  export const compileHtml = async ({
35
42
  // cancellationToken,
36
43
  logger,
37
- // request,
38
- code,
39
44
  url,
40
45
  compiledUrl,
41
46
  projectDirectoryUrl,
47
+ jsenvRemoteDirectory,
48
+ compileServerOrigin,
42
49
  outDirectoryRelativeUrl,
43
- compileId,
44
50
 
51
+ compileId,
45
52
  babelPluginMap,
46
53
  moduleOutFormat,
47
54
  importMetaFormat,
48
55
  topLevelAwait,
49
-
50
- sourcemapMethod,
51
-
52
56
  jsenvScriptInjection = true,
53
57
  jsenvEventSourceClientInjection,
54
58
  jsenvToolbarInjection,
55
59
  onHtmlImportmapInfo,
60
+
61
+ sourcemapMethod,
62
+ code,
56
63
  }) => {
64
+ const compileDirectoryUrl = `${projectDirectoryUrl}${outDirectoryRelativeUrl}${compileId}/`
57
65
  const browserRuntimeBuildUrlRelativeToProject = urlToRelativeUrl(
58
66
  BROWSER_RUNTIME_BUILD_URL,
59
67
  projectDirectoryUrl,
60
68
  )
61
-
62
69
  const eventSourceClientBuildRelativeUrlForProject = urlToRelativeUrl(
63
70
  EVENT_SOURCE_CLIENT_BUILD_URL,
64
71
  projectDirectoryUrl,
65
72
  )
66
-
67
73
  const toolbarInjectorBuildRelativeUrlForProject = urlToRelativeUrl(
68
74
  TOOLBAR_INJECTOR_BUILD_URL,
69
75
  projectDirectoryUrl,
@@ -71,11 +77,6 @@ export const compileHtml = async ({
71
77
 
72
78
  // ideally we should try/catch html syntax error
73
79
  const htmlAst = parseHtmlString(code)
74
-
75
- if (moduleOutFormat !== "esmodule") {
76
- await mutateRessourceHints(htmlAst)
77
- }
78
-
79
80
  manipulateHtmlAst(htmlAst, {
80
81
  scriptInjections: [
81
82
  ...(jsenvScriptInjection
@@ -104,255 +105,562 @@ export const compileHtml = async ({
104
105
  ],
105
106
  })
106
107
 
107
- let sources = []
108
- let sourcesContent = []
108
+ const sources = []
109
+ const sourcesContent = []
110
+ const assets = []
111
+ const assetsContent = []
112
+
113
+ const addHtmlSourceFile = ({ url, content }) => {
114
+ sources.push(url)
115
+ sourcesContent.push(content)
116
+ }
117
+ addHtmlSourceFile({ url, content: code })
118
+
109
119
  const { scripts } = parseHtmlAstRessources(htmlAst)
110
- let importmapInfo = null
111
- scripts.forEach((script) => {
112
- const typeAttribute = getHtmlNodeAttributeByName(script, "type")
113
- if (typeAttribute && typeAttribute.value === "importmap") {
114
- if (importmapInfo) {
115
- console.error("HTML file must contain max 1 importmap")
116
- } else {
117
- const srcAttribute = getHtmlNodeAttributeByName(script, "src")
118
- const src = srcAttribute ? srcAttribute.value : ""
119
- if (src) {
120
- importmapInfo = {
121
- script,
122
- url: resolveUrl(src, url),
123
- load: async () => {
124
- const importMapResponse = await fetchUrl(importmapInfo.url)
125
- if (importMapResponse.status === 200) {
126
- const importmapAsText = await importMapResponse.text()
127
- let htmlImportmap = JSON.parse(importmapAsText)
128
- htmlImportmap = moveImportMap(
129
- htmlImportmap,
130
- importmapInfo.url,
131
- url,
132
- )
133
- sources.push(importmapInfo.url)
134
- sourcesContent.push(importmapAsText)
135
- return htmlImportmap
136
- }
137
- logger.warn(
138
- createDetailedMessage(
139
- importMapResponse.status === 404
140
- ? `importmap script file cannot be found.`
141
- : `importmap script file unexpected response status (${importMapResponse.status}).`,
142
- {
143
- "importmap url": importmapInfo.url,
144
- "html url": url,
145
- },
146
- ),
147
- )
148
- return {}
149
- },
150
- }
151
- } else {
152
- importmapInfo = {
153
- script,
154
- url: compiledUrl,
155
- load: () => {
156
- const jsenvImportmap = getDefaultImportmap(compiledUrl, {
157
- projectDirectoryUrl,
158
- compileDirectoryUrl: `${projectDirectoryUrl}${compileId}/${outDirectoryRelativeUrl}`,
159
- })
160
- const htmlImportmap = JSON.parse(
161
- getHtmlNodeTextNode(script).value,
162
- )
163
- const importmap = composeTwoImportMaps(
164
- jsenvImportmap,
165
- htmlImportmap,
166
- )
167
- return importmap
168
- },
120
+ const htmlDependencies = collectHtmlDependenciesFromAst(htmlAst)
121
+
122
+ const htmlAssetGenerators = []
123
+ const htmlMutations = []
124
+ const addHtmlAssetGenerator = (htmlAssetGenerator) => {
125
+ htmlAssetGenerators.push(htmlAssetGenerator)
126
+ }
127
+ const addHtmlMutation = (htmlMutation) => {
128
+ htmlMutations.push(htmlMutation)
129
+ }
130
+ const addHtmlDependency = ({ htmlNode, specifier }) => {
131
+ htmlDependencies.push({
132
+ htmlNode,
133
+ specifier,
134
+ })
135
+ }
136
+ if (moduleOutFormat !== "esmodule") {
137
+ const ressourceHints = collectRessourceHints(htmlAst)
138
+ await visitRessourceHints({
139
+ ressourceHints,
140
+ addHtmlMutation,
141
+ })
142
+ }
143
+ await visitImportmapScript({
144
+ htmlAst,
145
+ logger,
146
+ url,
147
+ compiledUrl,
148
+ projectDirectoryUrl,
149
+ compileDirectoryUrl,
150
+ moduleOutFormat,
151
+ scripts,
152
+ addHtmlMutation,
153
+ addHtmlSourceFile,
154
+ onHtmlImportmapInfo,
155
+ })
156
+ await visitScripts({
157
+ logger,
158
+ projectDirectoryUrl,
159
+ jsenvRemoteDirectory,
160
+ compileServerOrigin,
161
+ url,
162
+ compiledUrl,
163
+
164
+ babelPluginMap,
165
+ moduleOutFormat,
166
+ importMetaFormat,
167
+ topLevelAwait,
168
+ sourcemapMethod,
169
+
170
+ scripts,
171
+ addHtmlSourceFile,
172
+ addHtmlAssetGenerator,
173
+ addHtmlMutation,
174
+ addHtmlDependency,
175
+ })
176
+ await Promise.all(
177
+ htmlAssetGenerators.map(async (htmlAssetGenerator) => {
178
+ const assetInfos = await htmlAssetGenerator()
179
+ assetInfos.forEach((assetInfo) => {
180
+ assets.push(assetInfo.url)
181
+ assetsContent.push(assetInfo.content)
182
+ })
183
+ }),
184
+ )
185
+ htmlAssetGenerators.length = 0
186
+ htmlMutations.forEach((htmlMutation) => {
187
+ htmlMutation()
188
+ })
189
+ htmlMutations.length = 0
190
+ const htmlAfterTransformation = stringifyHtmlAst(htmlAst)
191
+ return {
192
+ contentType: "text/html",
193
+ compiledSource: htmlAfterTransformation,
194
+ sources,
195
+ sourcesContent,
196
+ assets,
197
+ assetsContent,
198
+ dependencies: htmlDependencies.map(({ specifier }) => {
199
+ return specifier
200
+ }),
201
+ }
202
+ }
203
+
204
+ // transform <link type="modulepreload"> into <link type="preload">
205
+ // also remove integrity attributes because we don't know in advance
206
+ // the result of the file compilation
207
+ const visitRessourceHints = async ({ ressourceHints, addHtmlMutation }) => {
208
+ await Promise.all(
209
+ ressourceHints.map(async (ressourceHint) => {
210
+ const hrefAttribute = getHtmlNodeAttributeByName(ressourceHint, "href")
211
+ const href = hrefAttribute ? hrefAttribute.value : ""
212
+ if (!href) {
213
+ return
214
+ }
215
+ const integrityAttribute = getHtmlNodeAttributeByName(
216
+ ressourceHint,
217
+ "integrity",
218
+ )
219
+ if (integrityAttribute) {
220
+ addHtmlMutation(() => {
221
+ removeHtmlNodeAttribute(ressourceHint, integrityAttribute)
222
+ })
223
+ }
224
+ const relAttribute = getHtmlNodeAttributeByName(ressourceHint, "rel")
225
+ const asAttribute = getHtmlNodeAttributeByName(ressourceHint, "as")
226
+ // - "modulepreload" -> "preload" because it's now regular js script
227
+ if (ressourceHint.rel === "modulepreload") {
228
+ addHtmlMutation(() => {
229
+ relAttribute.value = "preload"
230
+ if (asAttribute) {
231
+ asAttribute.value = "script"
232
+ } else {
233
+ addHtmlNodeAttribute(ressourceHint, { name: "as", value: "script" })
169
234
  }
170
- }
235
+ })
236
+ return
171
237
  }
172
- }
238
+ // if (asAttribute && asAttribute.value === "script") {
239
+ // addHtmlMutation(() => {
240
+ // replaceHtmlNode(htmlNode, `<link as="script" />`)
241
+ // })
242
+ // return
243
+ // }
244
+ }),
245
+ )
246
+ }
247
+
248
+ const visitImportmapScript = async ({
249
+ htmlAst,
250
+ logger,
251
+ url,
252
+ compiledUrl,
253
+ projectDirectoryUrl,
254
+ compileDirectoryUrl,
255
+ moduleOutFormat,
256
+ scripts,
257
+ addHtmlMutation,
258
+ addHtmlSourceFile,
259
+ onHtmlImportmapInfo,
260
+ }) => {
261
+ const importmapScripts = scripts.filter((script) => {
262
+ const typeAttribute = getHtmlNodeAttributeByName(script, "type")
263
+ const type = typeAttribute ? typeAttribute.value : "application/javascript"
264
+ return type === "importmap"
173
265
  })
174
- if (importmapInfo) {
175
- const importmap = await importmapInfo.load()
176
- const importmapAsText = JSON.stringify(importmap, null, " ")
177
- replaceHtmlNode(
178
- importmapInfo.script,
179
- `<script type="${
180
- moduleOutFormat === "systemjs" ? "systemjs-importmap" : "importmap"
181
- }">${importmapAsText}</script>`,
182
- {
183
- attributesToIgnore: ["src"],
184
- },
185
- )
186
- importmapInfo.inlinedFrom = importmapInfo.url
187
- importmapInfo.text = importmapAsText
188
- } else {
266
+ // in case there is no importmap, force the presence
267
+ // so that '@jsenv/core/' are still remapped
268
+ if (importmapScripts.length === 0) {
189
269
  const defaultImportMap = getDefaultImportmap(compiledUrl, {
190
270
  projectDirectoryUrl,
191
- compileDirectoryUrl: `${projectDirectoryUrl}${outDirectoryRelativeUrl}${compileId}/`,
271
+ compileDirectoryUrl,
192
272
  })
193
- const importmapAsText = JSON.stringify(defaultImportMap, null, " ")
194
- manipulateHtmlAst(htmlAst, {
195
- scriptInjections: [
196
- {
197
- type:
198
- moduleOutFormat === "systemjs" ? "systemjs-importmap" : "importmap",
199
- // in case there is no importmap, force the presence
200
- // so that '@jsenv/core/' are still remapped
201
- text: importmapAsText,
202
- },
203
- ],
204
- })
205
- importmapInfo = {
273
+ const defaultImportMapAsText = JSON.stringify(defaultImportMap, null, " ")
274
+ onHtmlImportmapInfo({
206
275
  url: compiledUrl,
207
- text: importmapAsText,
276
+ text: defaultImportMapAsText,
277
+ })
278
+ addHtmlMutation(() => {
279
+ manipulateHtmlAst(htmlAst, {
280
+ scriptInjections: [
281
+ {
282
+ type:
283
+ moduleOutFormat === "systemjs"
284
+ ? "systemjs-importmap"
285
+ : "importmap",
286
+ text: defaultImportMapAsText,
287
+ },
288
+ ],
289
+ })
290
+ })
291
+ return
292
+ }
293
+ if (importmapScripts.length > 1) {
294
+ logger.error("HTML file must contain max 1 importmap")
295
+ }
296
+ const firstImportmapScript = importmapScripts[0]
297
+ const srcAttribute = getHtmlNodeAttributeByName(firstImportmapScript, "src")
298
+ const src = srcAttribute ? srcAttribute.value : ""
299
+ if (src) {
300
+ const importmapUrl = resolveUrl(src, url)
301
+ const importMapResponse = await fetchUrl(importmapUrl)
302
+ let importmap
303
+ if (importMapResponse.status === 200) {
304
+ const importmapAsText = await importMapResponse.text()
305
+ addHtmlSourceFile({
306
+ url: importmapUrl,
307
+ content: importmapAsText,
308
+ })
309
+ let htmlImportmap = JSON.parse(importmapAsText)
310
+ importmap = moveImportMap(htmlImportmap, importmapUrl, url)
311
+ } else {
312
+ logger.warn(
313
+ createDetailedMessage(
314
+ importMapResponse.status === 404
315
+ ? `importmap script file cannot be found.`
316
+ : `importmap script file unexpected response status (${importMapResponse.status}).`,
317
+ {
318
+ "importmap url": importmapUrl,
319
+ "html url": url,
320
+ },
321
+ ),
322
+ )
323
+ importmap = {}
208
324
  }
325
+ const importmapAsText = JSON.stringify(importmap, null, " ")
326
+ onHtmlImportmapInfo({
327
+ url: importmapUrl,
328
+ text: importmapAsText,
329
+ })
330
+ addHtmlMutation(() => {
331
+ removeHtmlNodeAttribute(firstImportmapScript, srcAttribute)
332
+ setHtmlNodeText(firstImportmapScript, importmapAsText)
333
+ if (moduleOutFormat === "systemjs") {
334
+ const typeAttribute = getHtmlNodeAttributeByName(
335
+ firstImportmapScript,
336
+ "type",
337
+ )
338
+ typeAttribute.value = "systemjs-importmap"
339
+ }
340
+ })
341
+ return
209
342
  }
343
+
344
+ const jsenvImportmap = getDefaultImportmap(compiledUrl, {
345
+ projectDirectoryUrl,
346
+ compileDirectoryUrl,
347
+ })
348
+ const htmlImportmap = JSON.parse(
349
+ getHtmlNodeTextNode(firstImportmapScript).value,
350
+ )
351
+ const importmap = composeTwoImportMaps(jsenvImportmap, htmlImportmap)
352
+ const importmapAsText = JSON.stringify(importmap, null, " ")
210
353
  onHtmlImportmapInfo({
211
- htmlUrl: url,
212
- importmapInfo,
354
+ url: compiledUrl,
355
+ text: importmapAsText,
356
+ })
357
+ addHtmlMutation(() => {
358
+ removeHtmlNodeAttribute(firstImportmapScript, srcAttribute)
359
+ setHtmlNodeText(firstImportmapScript, importmapAsText)
360
+ if (moduleOutFormat === "systemjs") {
361
+ const typeAttribute = getHtmlNodeAttributeByName(
362
+ firstImportmapScript,
363
+ "type",
364
+ )
365
+ typeAttribute.value = "systemjs-importmap"
366
+ }
213
367
  })
368
+ return
369
+ }
214
370
 
215
- const htmlDependencies = collectHtmlDependenciesFromAst(htmlAst)
216
- const inlineScriptsContentMap = {}
371
+ const visitScripts = async ({
372
+ logger,
373
+ projectDirectoryUrl,
374
+ compileServerOrigin,
375
+ jsenvRemoteDirectory,
376
+ url,
377
+ compiledUrl,
378
+
379
+ babelPluginMap,
380
+ moduleOutFormat,
381
+ importMetaFormat,
382
+ topLevelAwait,
383
+ sourcemapMethod,
384
+
385
+ scripts,
386
+ addHtmlSourceFile,
387
+ addHtmlAssetGenerator,
388
+ addHtmlMutation,
389
+ addHtmlDependency,
390
+ }) => {
217
391
  scripts.forEach((script) => {
218
392
  const typeAttribute = getHtmlNodeAttributeByName(script, "type")
393
+ const type = typeAttribute ? typeAttribute.value : "application/javascript"
219
394
  const srcAttribute = getHtmlNodeAttributeByName(script, "src")
220
395
  const src = srcAttribute ? srcAttribute.value : ""
221
- // remote module script
222
- if (typeAttribute && typeAttribute.value === "module" && src) {
223
- if (moduleOutFormat === "systemjs") {
224
- removeHtmlNodeAttribute(script, typeAttribute)
225
- }
226
- removeHtmlNodeAttribute(script, srcAttribute)
227
- const jsenvMethod =
228
- moduleOutFormat === "systemjs"
229
- ? "executeFileUsingSystemJs"
230
- : "executeFileUsingDynamicImport"
231
- setHtmlNodeText(
232
- script,
233
- `window.__jsenv__.${jsenvMethod}(${JSON.stringify(src)})`,
234
- )
235
- return
236
- }
237
-
238
- // inline module script
396
+ const integrityAttribute = getHtmlNodeAttributeByName(script, "integrity")
239
397
  const textNode = getHtmlNodeTextNode(script)
240
- if (typeAttribute && typeAttribute.value === "module" && textNode) {
241
- if (moduleOutFormat === "systemjs") {
242
- removeHtmlNodeAttribute(script, typeAttribute)
398
+ if (type === "module") {
399
+ if (src) {
400
+ addHtmlMutation(() => {
401
+ if (moduleOutFormat === "systemjs") {
402
+ removeHtmlNodeAttribute(script, typeAttribute)
403
+ }
404
+ if (integrityAttribute) {
405
+ removeHtmlNodeAttribute(script, integrityAttribute)
406
+ }
407
+ removeHtmlNodeAttribute(script, srcAttribute)
408
+ const jsenvMethod =
409
+ moduleOutFormat === "systemjs"
410
+ ? "executeFileUsingSystemJs"
411
+ : "executeFileUsingDynamicImport"
412
+ let specifier
413
+ if (
414
+ jsenvRemoteDirectory.isRemoteUrl(src) &&
415
+ !jsenvRemoteDirectory.isPreservedUrl(src)
416
+ ) {
417
+ const fileUrl = jsenvRemoteDirectory.fileUrlFromRemoteUrl(src)
418
+ const fileUrlRelativeToHtml = urlToRelativeUrl(fileUrl, url)
419
+ specifier = `./${fileUrlRelativeToHtml}`
420
+ } else {
421
+ specifier = src
422
+ }
423
+ setHtmlNodeText(
424
+ script,
425
+ `window.__jsenv__.${jsenvMethod}(${JSON.stringify(specifier)})`,
426
+ )
427
+ })
428
+ return
243
429
  }
244
- removeHtmlNodeAttribute(script, srcAttribute)
245
- const scriptAssetUrl = generateCompiledFileAssetUrl(
430
+ const scriptId = getIdForInlineHtmlNode(script, scripts)
431
+ const inlineScriptName = `${scriptId}.js`
432
+ const scriptOriginalUrl = resolveUrl(inlineScriptName, url)
433
+ const scriptCompiledUrl = generateCompilationAssetUrl(
246
434
  compiledUrl,
247
- getUniqueNameForInlineHtmlNode(script, scripts, `[id].js`),
248
- )
249
- const specifier = `./${urlToRelativeUrl(scriptAssetUrl, compiledUrl)}`
250
- inlineScriptsContentMap[specifier] = textNode.value
251
- const jsenvMethod =
252
- moduleOutFormat === "systemjs"
253
- ? "executeFileUsingSystemJs"
254
- : "executeFileUsingDynamicImport"
255
- setHtmlNodeText(
256
- script,
257
- `window.__jsenv__.${jsenvMethod}(${JSON.stringify(specifier)})`,
435
+ inlineScriptName,
258
436
  )
259
- htmlDependencies.push({
437
+ addHtmlAssetGenerator(async () => {
438
+ return transformHtmlScript({
439
+ projectDirectoryUrl,
440
+ jsenvRemoteDirectory,
441
+ url: scriptOriginalUrl,
442
+ compiledUrl: scriptCompiledUrl,
443
+
444
+ type: "module",
445
+ babelPluginMap,
446
+ moduleOutFormat,
447
+ importMetaFormat,
448
+ topLevelAwait,
449
+
450
+ sourcemapMethod,
451
+ code: textNode.value,
452
+ })
453
+ })
454
+ const specifier = `./${urlToRelativeUrl(scriptCompiledUrl, compiledUrl)}`
455
+ addHtmlMutation(() => {
456
+ if (moduleOutFormat === "systemjs") {
457
+ removeHtmlNodeAttribute(script, typeAttribute)
458
+ }
459
+ removeHtmlNodeAttribute(script, srcAttribute)
460
+ const jsenvMethod =
461
+ moduleOutFormat === "systemjs"
462
+ ? "executeFileUsingSystemJs"
463
+ : "executeFileUsingDynamicImport"
464
+ setHtmlNodeText(
465
+ script,
466
+ `window.__jsenv__.${jsenvMethod}(${JSON.stringify(specifier)})`,
467
+ )
468
+ })
469
+ addHtmlDependency({
260
470
  htmlNode: script,
261
471
  specifier,
262
472
  })
263
473
  return
264
474
  }
265
- })
266
- const htmlAfterTransformation = stringifyHtmlAst(htmlAst)
475
+ if (type === "application/javascript" || type === "text/javascript") {
476
+ if (src) {
477
+ const htmlServerUrl = url.replace(
478
+ projectDirectoryUrl,
479
+ `${compileServerOrigin}/`,
480
+ )
481
+ const scriptOriginalServerUrl = resolveUrl(src, htmlServerUrl)
482
+ const scriptOriginalUrl = scriptOriginalServerUrl.replace(
483
+ `${compileServerOrigin}/`,
484
+ projectDirectoryUrl,
485
+ )
486
+ const fileIsInsideJsenvDistDirectory = urlIsInsideOf(
487
+ scriptOriginalUrl,
488
+ jsenvDistDirectoryUrl,
489
+ )
490
+ if (fileIsInsideJsenvDistDirectory) {
491
+ return
492
+ }
493
+ const isRemoteUrl = jsenvRemoteDirectory.isRemoteUrl(src)
494
+ if (isRemoteUrl && jsenvRemoteDirectory.isPreservedUrl(src)) {
495
+ return
496
+ }
497
+ const scriptCompiledUrl = generateCompilationAssetUrl(
498
+ compiledUrl,
499
+ urlToFilename(scriptOriginalUrl),
500
+ )
501
+ addHtmlAssetGenerator(async () => {
502
+ // we fetch scriptOriginalUrl on purpose because we do
503
+ // the transformation here and not in compile server
504
+ // (because compile server would think it's a module script
505
+ // and add things like systemjs)
506
+ // we could take into account the integrity her
507
+ const scriptResponse = await fetchUrl(scriptOriginalUrl)
508
+ if (scriptResponse.status !== 200) {
509
+ logger.warn(
510
+ createDetailedMessage(
511
+ scriptResponse.status === 404
512
+ ? `script file cannot be found.`
513
+ : `script file unexpected response status (${scriptResponse.status}).`,
514
+ {
515
+ "script url": script.url,
516
+ "html url": url,
517
+ },
518
+ ),
519
+ )
520
+ return []
521
+ }
522
+ const scriptAsText = await scriptResponse.text()
523
+ addHtmlSourceFile({
524
+ url: scriptOriginalUrl,
525
+ content: scriptAsText,
526
+ })
527
+ return transformHtmlScript({
528
+ projectDirectoryUrl,
529
+ jsenvRemoteDirectory,
530
+ url: scriptOriginalUrl,
531
+ compiledUrl: scriptCompiledUrl,
267
532
 
268
- let assets = []
269
- let assetsContent = []
270
- await Promise.all(
271
- Object.keys(inlineScriptsContentMap).map(async (scriptSrc) => {
272
- const scriptAssetUrl = resolveUrl(scriptSrc, compiledUrl)
273
- const scriptBasename = urlToRelativeUrl(scriptAssetUrl, compiledUrl)
274
- const scriptOriginalFileUrl = resolveUrl(scriptBasename, url)
275
- const scriptCompiledFileUrl = resolveUrl(scriptBasename, compiledUrl)
533
+ type: "classic",
534
+ babelPluginMap,
535
+ moduleOutFormat,
536
+ importMetaFormat,
537
+ topLevelAwait,
276
538
 
277
- const scriptBeforeCompilation = inlineScriptsContentMap[scriptSrc]
278
- let scriptTransformResult
279
- try {
280
- scriptTransformResult = await transformJs({
281
- code: scriptBeforeCompilation,
282
- url: scriptOriginalFileUrl,
283
- compiledUrl: scriptCompiledFileUrl,
539
+ sourcemapMethod,
540
+ code: scriptAsText,
541
+ })
542
+ })
543
+ addHtmlMutation(() => {
544
+ if (integrityAttribute) {
545
+ removeHtmlNodeAttribute(script, integrityAttribute)
546
+ }
547
+ srcAttribute.value = `./${urlToRelativeUrl(
548
+ scriptCompiledUrl,
549
+ compiledUrl,
550
+ )}`
551
+ })
552
+ return
553
+ }
554
+ const scriptId = getIdForInlineHtmlNode(script, scripts)
555
+ const inlineScriptName = `${scriptId}.js`
556
+ const scriptOriginalUrl = resolveUrl(inlineScriptName, url)
557
+ const scriptCompiledUrl = generateCompilationAssetUrl(
558
+ compiledUrl,
559
+ inlineScriptName,
560
+ )
561
+ addHtmlAssetGenerator(async () => {
562
+ const htmlAssets = await transformHtmlScript({
284
563
  projectDirectoryUrl,
564
+ jsenvRemoteDirectory,
565
+ url: scriptOriginalUrl,
566
+ compiledUrl: scriptCompiledUrl,
285
567
 
568
+ type: "classic",
286
569
  babelPluginMap,
287
570
  moduleOutFormat,
288
571
  importMetaFormat,
289
572
  topLevelAwait,
573
+
574
+ code: textNode.value,
575
+ sourcemapMethod,
290
576
  })
291
- } catch (e) {
292
- // If there is a syntax error in inline script
293
- // we put the raw script without transformation.
294
- // when systemjs will try to instantiate to script it
295
- // will re-throw this syntax error.
296
- // Thanks to this we see the syntax error in the
297
- // document and livereloading still works
298
- // because we gracefully handle this error
299
- if (e.code === "PARSE_ERROR") {
300
- const code = scriptBeforeCompilation
301
- assets = [...assets, scriptAssetUrl]
302
- assetsContent = [...assetsContent, code]
303
- return
304
- }
305
- throw e
306
- }
307
- const sourcemapFileUrl = resolveUrl(
308
- `${scriptBasename}.map`,
309
- scriptCompiledFileUrl,
310
- )
577
+ addHtmlMutation(() => {
578
+ setHtmlNodeText(script, htmlAssets[0].content)
579
+ })
580
+ return htmlAssets
581
+ })
582
+ return
583
+ }
584
+ })
585
+ }
311
586
 
312
- let { code, map } = scriptTransformResult
313
- const sourcemapFileRelativePathForModule = urlToRelativeUrl(
314
- sourcemapFileUrl,
315
- compiledUrl,
316
- )
587
+ const transformHtmlScript = async ({
588
+ projectDirectoryUrl,
589
+ jsenvRemoteDirectory,
590
+ url,
591
+ compiledUrl,
317
592
 
318
- if (sourcemapMethod === "inline") {
319
- code = setJavaScriptSourceMappingUrl(code, sourcemapToBase64Url(map))
320
- } else {
321
- // TODO: respect "sourcemapMethod" parameter
322
- code = setJavaScriptSourceMappingUrl(
323
- code,
324
- sourcemapFileRelativePathForModule,
325
- )
326
- assets = [...assets, scriptAssetUrl, sourcemapFileUrl]
327
- assetsContent = [
328
- ...assetsContent,
329
- code,
330
- JSON.stringify(map, null, " "),
331
- ]
332
- }
333
- }),
334
- )
335
- sources.push(url)
336
- sourcesContent.push(code)
593
+ type,
594
+ babelPluginMap,
595
+ moduleOutFormat,
596
+ importMetaFormat,
597
+ topLevelAwait,
337
598
 
338
- return {
339
- contentType: "text/html",
340
- compiledSource: htmlAfterTransformation,
341
- sources,
342
- sourcesContent,
343
- assets,
344
- assetsContent,
345
- dependencies: htmlDependencies.map(({ specifier }) => {
346
- return specifier
347
- }),
599
+ code,
600
+ sourcemapMethod,
601
+ }) => {
602
+ let transformResult
603
+ try {
604
+ transformResult = await transformJs({
605
+ projectDirectoryUrl,
606
+ jsenvRemoteDirectory,
607
+ url,
608
+ compiledUrl,
609
+
610
+ babelPluginMap,
611
+ moduleOutFormat: type === "module" ? moduleOutFormat : "global",
612
+ importMetaFormat,
613
+ topLevelAwait: type === "module" ? topLevelAwait : false,
614
+ babelHelpersInjectionAsImport: type === "module" ? undefined : false,
615
+
616
+ code,
617
+ })
618
+ } catch (e) {
619
+ // If there is a syntax error in inline script
620
+ // we put the raw script without transformation.
621
+ // when systemjs will try to instantiate to script it
622
+ // will re-throw this syntax error.
623
+ // Thanks to this we see the syntax error in the
624
+ // document and livereloading still works
625
+ // because we gracefully handle this error
626
+ if (e.code === "PARSE_ERROR") {
627
+ return [{ url, content: code }]
628
+ }
629
+ throw e
348
630
  }
631
+
632
+ code = transformResult.code
633
+ let map = transformResult.map
634
+ const sourcemapUrl = generateSourcemapUrl(compiledUrl)
635
+ if (sourcemapMethod === "inline") {
636
+ code = setJavaScriptSourceMappingUrl(code, sourcemapToBase64Url(map))
637
+ return [
638
+ {
639
+ url: compiledUrl,
640
+ content: code,
641
+ },
642
+ ]
643
+ }
644
+ const sourcemapSpecifier = urlToRelativeUrl(sourcemapUrl, compiledUrl)
645
+ code = setJavaScriptSourceMappingUrl(code, sourcemapSpecifier)
646
+ return [
647
+ {
648
+ url: compiledUrl,
649
+ content: code,
650
+ },
651
+ {
652
+ url: sourcemapUrl,
653
+ content: JSON.stringify(map, null, " "),
654
+ },
655
+ ]
349
656
  }
350
657
 
351
- // transform <link type="modulepreload"> into <link type="preload">
352
- const mutateRessourceHints = async (htmlAst) => {
658
+ const collectRessourceHints = (htmlAst) => {
353
659
  const ressourceHints = []
354
660
  visitHtmlAst(htmlAst, (htmlNode) => {
355
- if (htmlNode.nodeName !== "link") return
661
+ if (htmlNode.nodeName !== "link") {
662
+ return
663
+ }
356
664
  const relAttribute = getHtmlNodeAttributeByName(htmlNode, "rel")
357
665
  const rel = relAttribute ? relAttribute.value : ""
358
666
  const isRessourceHint = [
@@ -362,44 +670,10 @@ const mutateRessourceHints = async (htmlAst) => {
362
670
  "preload",
363
671
  "modulepreload",
364
672
  ].includes(rel)
365
- if (!isRessourceHint) return
366
-
367
- ressourceHints.push({ rel, htmlNode })
673
+ if (!isRessourceHint) {
674
+ return
675
+ }
676
+ ressourceHints.push(htmlNode)
368
677
  })
369
-
370
- const mutations = []
371
- await Promise.all(
372
- ressourceHints.map(async (ressourceHint) => {
373
- const hrefAttribute = getHtmlNodeAttributeByName(
374
- ressourceHint.htmlNode,
375
- "href",
376
- )
377
- const href = hrefAttribute ? hrefAttribute.value : ""
378
- if (!href) return
379
-
380
- // - "modulepreload" -> "preload" because it's now regular js script
381
- const asAttribute = getHtmlNodeAttributeByName(
382
- ressourceHint.htmlNode,
383
- "as",
384
- )
385
-
386
- if (ressourceHint.rel === "modulepreload") {
387
- mutations.push(() => {
388
- replaceHtmlNode(
389
- ressourceHint.htmlNode,
390
- `<link rel="preload" as="script" />`,
391
- )
392
- })
393
- return
394
- }
395
-
396
- if (asAttribute && asAttribute.value === "script") {
397
- mutations.push(() => {
398
- replaceHtmlNode(ressourceHint.htmlNode, `<link as="script" />`)
399
- })
400
- return
401
- }
402
- }),
403
- )
404
- mutations.forEach((mutation) => mutation())
678
+ return ressourceHints
405
679
  }