@jsenv/core 25.0.0 → 25.2.0

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 (60) 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 +8 -8
  15. package/readme.md +61 -52
  16. package/src/buildProject.js +21 -13
  17. package/src/commonJsToJavaScriptModule.js +8 -7
  18. package/src/execute.js +2 -0
  19. package/src/executeTestPlan.js +14 -0
  20. package/src/internal/building/buildUsingRollup.js +4 -2
  21. package/src/internal/building/build_stats.js +3 -0
  22. package/src/internal/building/build_url_generator.js +153 -0
  23. package/src/internal/building/css/parseCssRessource.js +32 -26
  24. package/src/internal/building/html/parseHtmlRessource.js +109 -91
  25. package/src/internal/building/js/parseJsRessource.js +5 -13
  26. package/src/internal/building/parseRessource.js +3 -0
  27. package/src/internal/building/ressource_builder.js +72 -64
  28. package/src/internal/building/ressource_builder_util.js +17 -5
  29. package/src/internal/building/rollup_plugin_jsenv.js +262 -189
  30. package/src/internal/building/url_fetcher.js +16 -7
  31. package/src/internal/building/url_loader.js +1 -5
  32. package/src/internal/building/url_versioning.js +0 -173
  33. package/src/internal/compiling/babel_plugin_import_metadata.js +7 -11
  34. package/src/internal/compiling/babel_plugin_proxy_external_imports.js +31 -0
  35. package/src/internal/compiling/compile-directory/compile-asset.js +8 -4
  36. package/src/internal/compiling/compile-directory/getOrGenerateCompiledFile.js +43 -8
  37. package/src/internal/compiling/compile-directory/updateMeta.js +2 -8
  38. package/src/internal/compiling/compile-directory/validateCache.js +1 -2
  39. package/src/internal/compiling/compileFile.js +22 -10
  40. package/src/internal/compiling/compileHtml.js +15 -28
  41. package/src/internal/compiling/createCompiledFileService.js +22 -24
  42. package/src/internal/compiling/html_source_file_service.js +18 -19
  43. package/src/internal/compiling/js-compilation-service/jsenvTransform.js +14 -4
  44. package/src/internal/compiling/js-compilation-service/transformJs.js +9 -5
  45. package/src/internal/compiling/jsenvCompilerForHtml.js +534 -263
  46. package/src/internal/compiling/jsenvCompilerForJavaScript.js +15 -11
  47. package/src/internal/compiling/startCompileServer.js +83 -20
  48. package/src/internal/compiling/transformResultToCompilationResult.js +47 -25
  49. package/src/internal/executing/executePlan.js +2 -0
  50. package/src/internal/fetchUrl.js +3 -2
  51. package/src/internal/integrity/integrity_algorithms.js +26 -0
  52. package/src/internal/integrity/integrity_parsing.js +50 -0
  53. package/src/internal/integrity/integrity_update.js +23 -0
  54. package/src/internal/integrity/integrity_validation.js +49 -0
  55. package/src/internal/jsenvCoreDirectoryUrl.js +2 -0
  56. package/src/internal/jsenv_remote_directory.js +156 -0
  57. package/src/internal/origin_directory_converter.js +62 -0
  58. package/src/internal/response_validation.js +11 -24
  59. package/src/internal/sourceMappingURLUtils.js +10 -0
  60. 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,559 @@ 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}/`,
192
- })
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
- ],
271
+ compileDirectoryUrl,
204
272
  })
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,
213
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
+ }
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
+
431
+ const scriptId = getIdForInlineHtmlNode(script, scripts)
432
+ const inlineScriptName = `${scriptId}.js`
433
+ const scriptOriginalUrl = resolveUrl(inlineScriptName, url)
434
+ const scriptCompiledUrl = generateCompilationAssetUrl(
246
435
  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)})`,
436
+ inlineScriptName,
258
437
  )
259
- htmlDependencies.push({
438
+ addHtmlAssetGenerator(async () => {
439
+ return transformHtmlScript({
440
+ projectDirectoryUrl,
441
+ jsenvRemoteDirectory,
442
+ url: scriptOriginalUrl,
443
+ compiledUrl: scriptCompiledUrl,
444
+
445
+ type: "module",
446
+ babelPluginMap,
447
+ moduleOutFormat,
448
+ importMetaFormat,
449
+ topLevelAwait,
450
+
451
+ sourcemapMethod,
452
+ code: textNode.value,
453
+ })
454
+ })
455
+ const specifier = `./${urlToRelativeUrl(scriptCompiledUrl, compiledUrl)}`
456
+ addHtmlMutation(() => {
457
+ if (moduleOutFormat === "systemjs") {
458
+ removeHtmlNodeAttribute(script, typeAttribute)
459
+ }
460
+ removeHtmlNodeAttribute(script, srcAttribute)
461
+ const jsenvMethod =
462
+ moduleOutFormat === "systemjs"
463
+ ? "executeFileUsingSystemJs"
464
+ : "executeFileUsingDynamicImport"
465
+ setHtmlNodeText(
466
+ script,
467
+ `window.__jsenv__.${jsenvMethod}(${JSON.stringify(specifier)})`,
468
+ )
469
+ })
470
+ addHtmlDependency({
260
471
  htmlNode: script,
261
472
  specifier,
262
473
  })
263
474
  return
264
475
  }
265
- })
266
- const htmlAfterTransformation = stringifyHtmlAst(htmlAst)
476
+ if (type === "application/javascript" || type === "text/javascript") {
477
+ if (src) {
478
+ const htmlServerUrl = url.replace(
479
+ projectDirectoryUrl,
480
+ `${compileServerOrigin}/`,
481
+ )
482
+ const scriptOriginalServerUrl = resolveUrl(src, htmlServerUrl)
483
+ const scriptOriginalUrl = scriptOriginalServerUrl.replace(
484
+ `${compileServerOrigin}/`,
485
+ projectDirectoryUrl,
486
+ )
487
+ const fileIsInsideJsenvDistDirectory = urlIsInsideOf(
488
+ scriptOriginalUrl,
489
+ jsenvDistDirectoryUrl,
490
+ )
491
+ if (fileIsInsideJsenvDistDirectory) {
492
+ return
493
+ }
267
494
 
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)
276
-
277
- const scriptBeforeCompilation = inlineScriptsContentMap[scriptSrc]
278
- let scriptTransformResult
279
- try {
280
- scriptTransformResult = await transformJs({
281
- code: scriptBeforeCompilation,
282
- url: scriptOriginalFileUrl,
283
- compiledUrl: scriptCompiledFileUrl,
495
+ const scriptCompiledUrl = generateCompilationAssetUrl(
496
+ compiledUrl,
497
+ urlToFilename(scriptOriginalUrl),
498
+ )
499
+ addHtmlAssetGenerator(async () => {
500
+ // we fetch scriptOriginalUrl on purpose because we do
501
+ // the transformation here and not in compile server
502
+ // (because compile server would think it's a module script
503
+ // and add things like systemjs)
504
+ const scriptResponse = await fetchUrl(scriptOriginalUrl)
505
+ if (scriptResponse.status !== 200) {
506
+ logger.warn(
507
+ createDetailedMessage(
508
+ scriptResponse.status === 404
509
+ ? `script file cannot be found.`
510
+ : `script file unexpected response status (${scriptResponse.status}).`,
511
+ {
512
+ "script url": script.url,
513
+ "html url": url,
514
+ },
515
+ ),
516
+ )
517
+ return []
518
+ }
519
+ const scriptAsText = await scriptResponse.text()
520
+ addHtmlSourceFile({
521
+ url: scriptOriginalUrl,
522
+ content: scriptAsText,
523
+ })
524
+ return transformHtmlScript({
525
+ projectDirectoryUrl,
526
+ jsenvRemoteDirectory,
527
+ url: scriptOriginalUrl,
528
+ compiledUrl: scriptCompiledUrl,
529
+
530
+ type: "classic",
531
+ babelPluginMap,
532
+ moduleOutFormat,
533
+ importMetaFormat,
534
+ topLevelAwait,
535
+
536
+ sourcemapMethod,
537
+ code: scriptAsText,
538
+ })
539
+ })
540
+ addHtmlMutation(() => {
541
+ if (integrityAttribute) {
542
+ removeHtmlNodeAttribute(script, integrityAttribute)
543
+ }
544
+ srcAttribute.value = `./${urlToRelativeUrl(
545
+ scriptCompiledUrl,
546
+ compiledUrl,
547
+ )}`
548
+ })
549
+ return
550
+ }
551
+ const scriptId = getIdForInlineHtmlNode(script, scripts)
552
+ const inlineScriptName = `${scriptId}.js`
553
+ const scriptOriginalUrl = resolveUrl(inlineScriptName, url)
554
+ const scriptCompiledUrl = generateCompilationAssetUrl(
555
+ compiledUrl,
556
+ inlineScriptName,
557
+ )
558
+ addHtmlAssetGenerator(async () => {
559
+ const htmlAssets = await transformHtmlScript({
284
560
  projectDirectoryUrl,
561
+ jsenvRemoteDirectory,
562
+ url: scriptOriginalUrl,
563
+ compiledUrl: scriptCompiledUrl,
285
564
 
565
+ type: "classic",
286
566
  babelPluginMap,
287
567
  moduleOutFormat,
288
568
  importMetaFormat,
289
569
  topLevelAwait,
570
+
571
+ code: textNode.value,
572
+ sourcemapMethod,
290
573
  })
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
- )
574
+ addHtmlMutation(() => {
575
+ setHtmlNodeText(script, htmlAssets[0].content)
576
+ })
577
+ return htmlAssets
578
+ })
579
+ return
580
+ }
581
+ })
582
+ }
311
583
 
312
- let { code, map } = scriptTransformResult
313
- const sourcemapFileRelativePathForModule = urlToRelativeUrl(
314
- sourcemapFileUrl,
315
- compiledUrl,
316
- )
584
+ const transformHtmlScript = async ({
585
+ projectDirectoryUrl,
586
+ jsenvRemoteDirectory,
587
+ url,
588
+ compiledUrl,
317
589
 
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)
590
+ type,
591
+ babelPluginMap,
592
+ moduleOutFormat,
593
+ importMetaFormat,
594
+ topLevelAwait,
337
595
 
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
- }),
596
+ code,
597
+ sourcemapMethod,
598
+ }) => {
599
+ let transformResult
600
+ try {
601
+ transformResult = await transformJs({
602
+ projectDirectoryUrl,
603
+ jsenvRemoteDirectory,
604
+ url,
605
+ compiledUrl,
606
+
607
+ babelPluginMap,
608
+ moduleOutFormat: type === "module" ? moduleOutFormat : "global",
609
+ importMetaFormat,
610
+ topLevelAwait: type === "module" ? topLevelAwait : false,
611
+ babelHelpersInjectionAsImport: type === "module" ? undefined : false,
612
+
613
+ code,
614
+ })
615
+ } catch (e) {
616
+ // If there is a syntax error in inline script
617
+ // we put the raw script without transformation.
618
+ // when systemjs will try to instantiate to script it
619
+ // will re-throw this syntax error.
620
+ // Thanks to this we see the syntax error in the
621
+ // document and livereloading still works
622
+ // because we gracefully handle this error
623
+ if (e.code === "PARSE_ERROR") {
624
+ return [{ url, content: code }]
625
+ }
626
+ throw e
348
627
  }
628
+
629
+ code = transformResult.code
630
+ let map = transformResult.map
631
+ const sourcemapUrl = generateSourcemapUrl(compiledUrl)
632
+ if (sourcemapMethod === "inline") {
633
+ code = setJavaScriptSourceMappingUrl(code, sourcemapToBase64Url(map))
634
+ return [
635
+ {
636
+ url: compiledUrl,
637
+ content: code,
638
+ },
639
+ ]
640
+ }
641
+ const sourcemapSpecifier = urlToRelativeUrl(sourcemapUrl, compiledUrl)
642
+ code = setJavaScriptSourceMappingUrl(code, sourcemapSpecifier)
643
+ return [
644
+ {
645
+ url: compiledUrl,
646
+ content: code,
647
+ },
648
+ {
649
+ url: sourcemapUrl,
650
+ content: JSON.stringify(map, null, " "),
651
+ },
652
+ ]
349
653
  }
350
654
 
351
- // transform <link type="modulepreload"> into <link type="preload">
352
- const mutateRessourceHints = async (htmlAst) => {
655
+ const collectRessourceHints = (htmlAst) => {
353
656
  const ressourceHints = []
354
657
  visitHtmlAst(htmlAst, (htmlNode) => {
355
- if (htmlNode.nodeName !== "link") return
658
+ if (htmlNode.nodeName !== "link") {
659
+ return
660
+ }
356
661
  const relAttribute = getHtmlNodeAttributeByName(htmlNode, "rel")
357
662
  const rel = relAttribute ? relAttribute.value : ""
358
663
  const isRessourceHint = [
@@ -362,44 +667,10 @@ const mutateRessourceHints = async (htmlAst) => {
362
667
  "preload",
363
668
  "modulepreload",
364
669
  ].includes(rel)
365
- if (!isRessourceHint) return
366
-
367
- ressourceHints.push({ rel, htmlNode })
670
+ if (!isRessourceHint) {
671
+ return
672
+ }
673
+ ressourceHints.push(htmlNode)
368
674
  })
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())
675
+ return ressourceHints
405
676
  }