@jsenv/core 22.5.1 → 23.0.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 (99) hide show
  1. package/dist/jsenv_browser_system.js +203 -49
  2. package/dist/jsenv_browser_system.js.map +39 -8
  3. package/dist/jsenv_compile_proxy.js +186 -108
  4. package/dist/jsenv_compile_proxy.js.map +48 -42
  5. package/dist/jsenv_exploring_redirector.js +148 -71
  6. package/dist/jsenv_exploring_redirector.js.map +26 -20
  7. package/dist/jsenv_toolbar.js +215 -104
  8. package/dist/jsenv_toolbar.js.map +39 -26
  9. package/helpers/new_stylesheet/new_stylesheet.js +411 -0
  10. package/{LICENSE → license} +0 -0
  11. package/main.js +7 -7
  12. package/package.json +23 -19
  13. package/readme.md +5 -4
  14. package/src/execute.js +23 -10
  15. package/src/executeTestPlan.js +0 -4
  16. package/src/importUsingChildProcess.js +36 -32
  17. package/src/internal/{babel-plugin-replace-expressions.js → babel_plugin_replace_expressions.js} +0 -0
  18. package/src/internal/{babel-plugin-transform-import-meta.js → babel_plugin_transform_import_meta.js} +0 -0
  19. package/src/internal/browser-launcher/executeHtmlFile.js +6 -8
  20. package/src/internal/browser-launcher/jsenv-browser-system.js +3 -0
  21. package/src/internal/building/asset_url_versioning.js +5 -9
  22. package/src/internal/building/buildServiceWorker.js +6 -13
  23. package/src/internal/building/buildUsingRollup.js +34 -0
  24. package/src/internal/building/build_logs.js +11 -0
  25. package/src/internal/building/build_stats.js +7 -1
  26. package/src/internal/building/createJsenvRollupPlugin.js +380 -297
  27. package/src/internal/building/css/parseCssRessource.js +67 -71
  28. package/src/internal/building/css/parseCssUrls.js +2 -2
  29. package/src/internal/building/css/{postcss-urlhash-plugin.js → postcss_plugin_url_visitor.js} +43 -21
  30. package/src/internal/building/css/replaceCssUrls.js +17 -14
  31. package/src/internal/building/css_module.js +47 -0
  32. package/src/internal/building/html/parseHtmlRessource.js +44 -43
  33. package/src/internal/building/import_references.js +81 -0
  34. package/src/internal/building/importmap/parseImportmapRessource.js +5 -2
  35. package/src/internal/building/js/minifyJs.js +30 -3
  36. package/src/internal/building/js/parseJsRessource.js +70 -77
  37. package/src/internal/building/json/parseJsonRessource.js +3 -2
  38. package/src/internal/building/parseRessource.js +11 -8
  39. package/src/internal/building/parsing.utils.js +4 -15
  40. package/src/internal/building/ressource_builder.js +142 -114
  41. package/src/internal/building/ressource_builder_util.js +31 -18
  42. package/src/internal/building/{fetchSourcemap.js → sourcemap_loader.js} +29 -27
  43. package/src/internal/building/svg/parseSvgRessource.js +7 -3
  44. package/src/internal/building/url-versioning.js +2 -1
  45. package/src/internal/building/url_fetcher.js +79 -0
  46. package/src/internal/building/url_loader.js +267 -0
  47. package/src/internal/building/webmanifest/parseWebmanifestRessource.js +9 -4
  48. package/src/internal/compiling/{js-compilation-service/ensureGlobalThisImportBabelPlugin.js → babel_plugin_global_this_as_jsenv_import.js} +4 -2
  49. package/src/internal/compiling/babel_plugin_import_assertions.js +100 -0
  50. package/src/internal/compiling/babel_plugin_new_stylesheet_as_jsenv_import.js +109 -0
  51. package/src/internal/compiling/babel_plugin_transform_import_specifier.js +86 -0
  52. package/src/internal/compiling/babel_plugins.js +2 -0
  53. package/src/internal/compiling/createCompiledFileService.js +6 -8
  54. package/src/internal/compiling/html_source_file_service.js +2 -2
  55. package/src/internal/compiling/js-compilation-service/{transformBabelHelperToImportBabelPlugin.js → babel_plugin_babel_helpers_as_jsenv_imports.js} +1 -1
  56. package/src/internal/compiling/js-compilation-service/{ensureRegeneratorRuntimeImportBabelPlugin.js → babel_plugin_regenerator_runtime_as_jsenv_import.js} +1 -1
  57. package/src/internal/compiling/js-compilation-service/jsenvTransform.js +7 -16
  58. package/src/internal/compiling/js-compilation-service/transformJs.js +0 -2
  59. package/src/internal/compiling/rollup_plugin_commonjs_named_exports.js +2 -2
  60. package/src/internal/compiling/startCompileServer.js +11 -4
  61. package/src/internal/escapeTemplateStringSpecialCharacters.js +20 -0
  62. package/src/internal/executing/coverage/{babel-plugin-instrument.js → babel_plugin_instrument.js} +17 -6
  63. package/src/internal/executing/coverage/relativeUrlToEmptyCoverage.js +1 -1
  64. package/src/internal/executing/executeConcurrently.js +2 -2
  65. package/src/internal/executing/executePlan.js +16 -2
  66. package/src/internal/executing/generateFileExecutionSteps.js +2 -1
  67. package/src/internal/executing/launchAndExecute.js +43 -69
  68. package/src/internal/exploring/exploring.css +2 -1
  69. package/src/internal/exploring/exploring.redirector.js +0 -1
  70. package/src/internal/generateGroupMap/generateGroupMap.js +14 -10
  71. package/src/internal/generateGroupMap/jsenvBabelPluginCompatMap.js +30 -0
  72. package/src/internal/generateGroupMap/jsenvPluginCompatMap.js +0 -6
  73. package/src/internal/generateGroupMap/one_runtime_compat.js +9 -38
  74. package/src/internal/generateGroupMap/runtime_compat.js +9 -24
  75. package/src/internal/generateGroupMap/runtime_compat_composition.js +2 -12
  76. package/src/internal/generateGroupMap/runtime_support.js +53 -0
  77. package/src/internal/jsenvInternalFiles.js +0 -1
  78. package/src/internal/node-launcher/createControllableNodeProcess.js +2 -3
  79. package/src/internal/response_validation.js +143 -0
  80. package/src/internal/runtime/createBrowserRuntime/createBrowserRuntime.js +8 -3
  81. package/src/internal/runtime/createBrowserRuntime/createBrowserSystem.js +117 -0
  82. package/src/internal/runtime/createBrowserRuntime/displayErrorInDocument.js +1 -1
  83. package/src/internal/runtime/createBrowserRuntime/scanBrowserRuntimeFeatures.js +124 -68
  84. package/src/internal/runtime/createNodeRuntime/createNodeRuntime.js +8 -86
  85. package/src/internal/runtime/createNodeRuntime/scanNodeRuntimeFeatures.js +115 -0
  86. package/src/internal/runtime/module-registration.js +1 -2
  87. package/src/internal/runtime/resolveGroup.js +2 -3
  88. package/src/internal/toolbar/compilation/toolbar.compilation.js +15 -17
  89. package/src/internal/toolbar/eventsource/toolbar.eventsource.js +35 -8
  90. package/src/internal/toolbar/toolbar.main.js +7 -4
  91. package/src/internal/url_utils.js +20 -0
  92. package/src/launchBrowser.js +47 -34
  93. package/src/launchNode.js +6 -3
  94. package/src/requireUsingChildProcess.js +36 -32
  95. package/src/startExploring.js +23 -11
  96. package/src/internal/building/transformImportMetaUrlReferences.js +0 -71
  97. package/src/internal/runtime/resolveBrowserGroup.js +0 -5
  98. package/src/internal/runtime/resolveNodeGroup.js +0 -5
  99. package/src/internal/validateResponseStatusIsOk.js +0 -91
@@ -2,7 +2,7 @@
2
2
  import { extname } from "node:path"
3
3
  import { normalizeImportMap } from "@jsenv/importmap"
4
4
  import { isSpecifierForNodeCoreModule } from "@jsenv/importmap/src/isSpecifierForNodeCoreModule.js"
5
- import { loggerToLogLevel } from "@jsenv/logger"
5
+ import { createDetailedMessage, loggerToLogLevel } from "@jsenv/logger"
6
6
  import {
7
7
  isFileSystemPath,
8
8
  fileSystemPathToUrl,
@@ -17,14 +17,16 @@ import {
17
17
  } from "@jsenv/filesystem"
18
18
 
19
19
  import { createUrlConverter } from "@jsenv/core/src/internal/url_conversion.js"
20
- import { fetchUrl } from "@jsenv/core/src/internal/fetchUrl.js"
21
- import { validateResponseStatusIsOk } from "@jsenv/core/src/internal/validateResponseStatusIsOk.js"
22
- import { transformJs } from "@jsenv/core/src/internal/compiling/js-compilation-service/transformJs.js"
20
+ import { createUrlFetcher } from "@jsenv/core/src/internal/building/url_fetcher.js"
21
+ import { createUrlLoader } from "@jsenv/core/src/internal/building/url_loader.js"
22
+ import { stringifyUrlTrace } from "@jsenv/core/src/internal/building/url_trace.js"
23
23
  import { sortObjectByPathnames } from "@jsenv/core/src/internal/building/sortObjectByPathnames.js"
24
24
  import { jsenvHelpersDirectoryInfo } from "@jsenv/core/src/internal/jsenvInternalFiles.js"
25
25
  import { infoSign } from "@jsenv/core/src/internal/logs/log_style.js"
26
+ import { setUrlSearchParamsDescriptor } from "@jsenv/core/src/internal/url_utils.js"
26
27
 
27
28
  import {
29
+ formatBuildStartLog,
28
30
  formatUseImportMapFromHtml,
29
31
  formatImportmapOutsideCompileDirectory,
30
32
  formatRessourceHintNeverUsedWarning,
@@ -32,10 +34,13 @@ import {
32
34
  } from "./build_logs.js"
33
35
  import { importMapsFromHtml } from "./html/htmlScan.js"
34
36
  import { parseRessource } from "./parseRessource.js"
35
- import { fetchSourcemap } from "./fetchSourcemap.js"
36
- import { createRessourceBuilder } from "./ressource_builder.js"
37
+ import {
38
+ createRessourceBuilder,
39
+ referenceToCodeForRollup,
40
+ } from "./ressource_builder.js"
41
+
37
42
  import { computeBuildRelativeUrl } from "./url-versioning.js"
38
- import { transformImportMetaUrlReferences } from "./transformImportMetaUrlReferences.js"
43
+ import { visitImportReferences } from "./import_references.js"
39
44
 
40
45
  import { minifyJs } from "./js/minifyJs.js"
41
46
  import { createImportResolverForNode } from "../import-resolution/import-resolver-node.js"
@@ -70,6 +75,7 @@ export const createJsenvRollupPlugin = async ({
70
75
  babelPluginMap,
71
76
  transformTopLevelAwait,
72
77
  node,
78
+ importAssertionsSupport,
73
79
 
74
80
  urlVersioning,
75
81
  lineBreakNormalization,
@@ -82,15 +88,25 @@ export const createJsenvRollupPlugin = async ({
82
88
  minifyHtmlOptions,
83
89
  }) => {
84
90
  const urlImporterMap = {}
85
- const urlResponseBodyMap = {}
86
91
  const inlineModuleScripts = {}
87
- const urlRedirectionMap = {}
88
92
  const jsModulesFromEntry = {}
89
93
  const buildFileContents = {}
90
94
  const buildInlineFileContents = {}
91
95
  let buildStats = {}
92
96
  const buildStartMs = Date.now()
93
97
 
98
+ let lastErrorMessage
99
+ const storeLatestJsenvPluginError = (error) => {
100
+ lastErrorMessage = error.message
101
+ }
102
+
103
+ let ressourceBuilder
104
+ let importResolver
105
+ let rollupEmitFile = () => {}
106
+ let rollupSetAssetSource = () => {}
107
+ let _rollupGetModuleInfo = () => {}
108
+ const rollupGetModuleInfo = (id) => _rollupGetModuleInfo(id)
109
+
94
110
  const {
95
111
  asRollupUrl,
96
112
  asProjectUrl,
@@ -106,10 +122,31 @@ export const createJsenvRollupPlugin = async ({
106
122
  urlMappings,
107
123
  })
108
124
 
109
- let lastErrorMessage
110
- const storeLatestJsenvPluginError = (error) => {
111
- lastErrorMessage = error.message
112
- }
125
+ const urlFetcher = createUrlFetcher({
126
+ asOriginalUrl,
127
+ asProjectUrl,
128
+ applyUrlMappings,
129
+ urlImporterMap,
130
+ beforeThrowingResponseValidationError: (error) => {
131
+ storeLatestJsenvPluginError(error)
132
+ },
133
+ })
134
+
135
+ const urlLoader = createUrlLoader({
136
+ projectDirectoryUrl,
137
+ buildDirectoryUrl,
138
+ babelPluginMap,
139
+ allowJson: acceptsJsonContentType({ node, format }),
140
+ urlImporterMap,
141
+ inlineModuleScripts,
142
+ jsConcatenation,
143
+
144
+ asServerUrl,
145
+ asProjectUrl,
146
+ asOriginalUrl,
147
+
148
+ urlFetcher,
149
+ })
113
150
 
114
151
  const externalUrlPredicate = externalImportUrlPatternsToExternalUrlPredicate(
115
152
  externalImportUrlPatterns,
@@ -164,13 +201,6 @@ export const createJsenvRollupPlugin = async ({
164
201
  compileServerOrigin,
165
202
  )
166
203
 
167
- let ressourceBuilder
168
- let rollupEmitFile = () => {}
169
- let rollupSetAssetSource = () => {}
170
- let _rollupGetModuleInfo = () => {}
171
- const rollupGetModuleInfo = (id) => _rollupGetModuleInfo(id)
172
- let importResolver
173
-
174
204
  const emitAsset = ({ fileName, source }) => {
175
205
  return rollupEmitFile({
176
206
  type: "asset",
@@ -192,21 +222,14 @@ export const createJsenvRollupPlugin = async ({
192
222
  name: "jsenv",
193
223
 
194
224
  async buildStart() {
195
- const entryFileRelativeUrls = Object.keys(entryPointMap)
196
- if (entryFileRelativeUrls.length === 1) {
197
- logger.info(`
198
- building ${entryFileRelativeUrls[0]}...`)
199
- } else {
200
- logger.info(`
201
- building ${entryFileRelativeUrls.length} entry files...`)
202
- }
225
+ logger.info(formatBuildStartLog({ entryPointMap }))
203
226
 
204
227
  const entryPointsPrepared = await prepareEntryPoints(entryPointMap, {
205
228
  logger,
206
229
  projectDirectoryUrl,
207
230
  buildDirectoryUrl,
208
231
  compileServerOrigin,
209
- fetchFile: jsenvFetchUrl,
232
+ urlFetcher,
210
233
  })
211
234
  const htmlEntryPoints = entryPointsPrepared.filter(
212
235
  (entryPointPrepared) => {
@@ -367,26 +390,24 @@ building ${entryFileRelativeUrls.length} entry files...`)
367
390
  })
368
391
  }
369
392
 
370
- // rollup will yell at us telling we did not provide an input option
371
- // if we provide only an html file without any script type module in it
372
- // but this can be desired to produce a build with only assets (html, css, images)
373
- // without any js
374
- // we emit an empty chunk, discards rollup warning about it. This chunk is
375
- // later ignored by in generateBundle hooks
393
+ // rollup expects an input option, if we provide only an html file
394
+ // without any script type module in it, we won't emit "chunk" and rollup will throw.
395
+ // It is valid to build an html not referencing any js (rare but valid)
396
+ // In that case jsenv emits an empty chunk and discards rollup warning about it
397
+ // This chunk is later ignored in "generateBundle" hook
376
398
  let atleastOneChunkEmitted = false
377
399
  ressourceBuilder = createRessourceBuilder(
378
400
  {
379
- parse: async (ressource, notifiers) => {
401
+ urlFetcher,
402
+ urlLoader,
403
+ parseRessource: async (ressource, notifiers) => {
380
404
  return parseRessource(ressource, notifiers, {
381
405
  format,
382
406
  systemJsUrl,
383
407
  projectDirectoryUrl,
384
- urlToOriginalFileUrl: (url) => {
385
- return asOriginalUrl(url)
386
- },
387
- urlToOriginalServerUrl: (url) => {
388
- return asOriginalServerUrl(url)
389
- },
408
+ asProjectUrl,
409
+ asOriginalUrl,
410
+ asOriginalServerUrl,
390
411
  ressourceHintNeverUsedCallback: (linkInfo) => {
391
412
  logger.warn(formatRessourceHintNeverUsedWarning(linkInfo))
392
413
  },
@@ -398,19 +419,13 @@ building ${entryFileRelativeUrls.length} entry files...`)
398
419
  minifyJsOptions,
399
420
  })
400
421
  },
401
- fetch: async (url, importer) => {
402
- const moduleResponse = await jsenvFetchUrl(url, importer)
403
- return moduleResponse
404
- },
405
422
  },
406
423
  {
407
424
  logLevel: loggerToLogLevel(logger),
408
425
  format,
409
- baseUrl: compileServerOrigin,
410
- buildDirectoryRelativeUrl: urlToRelativeUrl(
411
- buildDirectoryUrl,
412
- projectDirectoryUrl,
413
- ),
426
+ // projectDirectoryUrl,
427
+ compileServerOrigin,
428
+ buildDirectoryUrl,
414
429
 
415
430
  asOriginalServerUrl,
416
431
  urlToCompiledServerUrl: (url) => {
@@ -426,7 +441,6 @@ building ${entryFileRelativeUrls.length} entry files...`)
426
441
  }
427
442
  return asOriginalUrl(url) || url
428
443
  },
429
- loadUrl: (url) => urlResponseBodyMap[url],
430
444
  resolveRessourceUrl: ({
431
445
  ressourceSpecifier,
432
446
  isJsModule,
@@ -495,10 +509,14 @@ building ${entryFileRelativeUrls.length} entry files...`)
495
509
  if (jsModuleIsInline) {
496
510
  inlineModuleScripts[jsModuleUrl] = jsModuleSource
497
511
  }
498
- urlImporterMap[jsModuleUrl] = resolveUrl(
499
- entryPointsPrepared[0].entryProjectRelativeUrl,
500
- compileDirectoryRemoteUrl,
501
- )
512
+ urlImporterMap[jsModuleUrl] = {
513
+ url: resolveUrl(
514
+ entryPointsPrepared[0].entryProjectRelativeUrl,
515
+ compileDirectoryRemoteUrl,
516
+ ),
517
+ line: undefined,
518
+ column: undefined,
519
+ }
502
520
  jsModulesFromEntry[asRollupUrl(jsModuleUrl)] = true
503
521
  },
504
522
  lineBreakNormalization,
@@ -553,7 +571,7 @@ building ${entryFileRelativeUrls.length} entry files...`)
553
571
  }
554
572
  },
555
573
 
556
- async resolveId(specifier, importer) {
574
+ async resolveId(specifier, importer, { custom }) {
557
575
  if (importer === undefined) {
558
576
  if (specifier.endsWith(".html")) {
559
577
  importer = compileServerOrigin
@@ -564,15 +582,24 @@ building ${entryFileRelativeUrls.length} entry files...`)
564
582
  importer = asServerUrl(importer)
565
583
  }
566
584
 
585
+ const { importAssertionInfo } = custom
586
+ const onExternal = ({ specifier, reason }) => {
587
+ logger.debug(`${specifier} marked as external (reason: ${reason})`)
588
+ }
589
+
567
590
  if (node && isSpecifierForNodeCoreModule(specifier)) {
568
- logger.debug(`${specifier} is native module -> marked as external`)
591
+ onExternal({
592
+ specifier,
593
+ reason: `node builtin module`,
594
+ })
569
595
  return false
570
596
  }
571
597
 
572
598
  if (externalImportSpecifiers.includes(specifier)) {
573
- logger.debug(
574
- `${specifier} verifies externalImportSpecifiers -> marked as external`,
575
- )
599
+ onExternal({
600
+ specifier,
601
+ reason: `declared in "externalImportSpecifiers"`,
602
+ })
576
603
  return { id: specifier, external: true }
577
604
  }
578
605
 
@@ -585,18 +612,39 @@ building ${entryFileRelativeUrls.length} entry files...`)
585
612
  }
586
613
 
587
614
  const importUrl = await importResolver.resolveImport(specifier, importer)
615
+
588
616
  const existingImporter = urlImporterMap[importUrl]
589
617
  if (!existingImporter) {
590
- urlImporterMap[importUrl] = importer
618
+ urlImporterMap[importUrl] = importAssertionInfo
619
+ ? {
620
+ url: importer,
621
+ column: importAssertionInfo.column,
622
+ line: importAssertionInfo.line,
623
+ }
624
+ : {
625
+ url: importer,
626
+ // rollup do not expose a way to know line and column for the static or dynamic import
627
+ // referencing that file
628
+ column: undefined,
629
+ line: undefined,
630
+ }
591
631
  }
592
632
 
593
633
  // keep external url intact
594
634
  const importProjectUrl = asProjectUrl(importUrl)
595
635
  if (!importProjectUrl) {
636
+ onExternal({
637
+ specifier,
638
+ reason: `outside project directory`,
639
+ })
596
640
  return { id: specifier, external: true }
597
641
  }
598
642
 
599
643
  if (externalUrlPredicate(asOriginalUrl(importProjectUrl))) {
644
+ onExternal({
645
+ specifier,
646
+ reason: `matches "externalUrlPredicate"`,
647
+ })
600
648
  return { id: specifier, external: true }
601
649
  }
602
650
 
@@ -643,89 +691,259 @@ building ${entryFileRelativeUrls.length} entry files...`)
643
691
 
644
692
  // },
645
693
 
646
- async load(id) {
647
- if (id === EMPTY_CHUNK_URL) {
694
+ async load(rollupUrl) {
695
+ if (rollupUrl === EMPTY_CHUNK_URL) {
648
696
  return ""
649
697
  }
650
698
 
651
- const moduleInfo = this.getModuleInfo(id)
652
- const url = asServerUrl(id)
653
- const importerUrl = urlImporterMap[url]
654
-
655
- // logger.debug(`loads ${url}`)
656
- const {
657
- responseUrl,
658
- responseContentType,
659
- contentRaw,
660
- content = "",
661
- map,
662
- } = await loadModule(url, {
663
- moduleInfo,
699
+ let url = asServerUrl(rollupUrl)
700
+ const loadResult = await urlLoader.loadUrl(rollupUrl, {
701
+ cancellationToken,
702
+ logger,
703
+ ressourceBuilder,
664
704
  })
665
705
 
706
+ url = loadResult.url
707
+ const code = loadResult.code
708
+ const map = loadResult.map
709
+
666
710
  // Jsenv helpers are injected as import statements to provide code like babel helpers
667
711
  // For now we just compute the information that the target file is a jsenv helper
668
712
  // without doing anything special with "targetIsJsenvHelperFile" information
669
- const originalUrl = asOriginalUrl(url)
713
+ const originalUrl = asOriginalUrl(rollupUrl)
670
714
  const isJsenvHelperFile = urlIsInsideOf(
671
715
  originalUrl,
672
716
  jsenvHelpersDirectoryInfo.url,
673
717
  )
674
718
 
675
- // Store the fact that this ressource exists
676
- // Happens when entry point references js, also for every static and dynamic imports
719
+ const importer = urlImporterMap[url]
720
+ // Inform ressource builder that this js module exists
721
+ // It can only be a js module and happens when:
722
+ // - entry point (html) references js
723
+ // - js is referenced by static or dynamic imports
724
+ // For import assertions, the imported ressource (css,json,...)
725
+ // is arelady converted to a js module
677
726
  ressourceBuilder.createReferenceFoundByRollup({
678
727
  // we don't want to emit a js chunk for every js file found
679
728
  // (However we want if the file is preload/prefetch by something else)
680
729
  // so we tell asset builder not to emit a chunk for this js reference
681
730
  // otherwise rollup would never concat module together
682
731
  referenceShouldNotEmitChunk: jsConcatenation,
683
- ressourceContentTypeExpected: responseContentType,
684
- referenceUrl: importerUrl,
685
- ressourceSpecifier: responseUrl,
686
- // rollup do not expose a way to know line and column for the static or dynamic import
687
- // referencing that file
688
- referenceColumn: undefined,
689
- referenceLine: undefined,
732
+ contentTypeExpected: "application/javascript",
733
+ referenceUrl: importer.url,
734
+ referenceColumn: importer.column,
735
+ referenceLine: importer.line,
736
+ ressourceSpecifier: url,
690
737
 
691
738
  isJsenvHelperFile,
692
- contentType: responseContentType,
693
- bufferBeforeBuild: Buffer.from(content),
694
- isJsModule: responseContentType === "application/javascript",
739
+ contentType: "application/javascript",
740
+ bufferBeforeBuild: Buffer.from(code),
741
+ isJsModule: true,
695
742
  })
696
743
 
697
- saveUrlResponseBody(responseUrl, contentRaw)
698
- // handle redirection
699
- if (responseUrl !== url) {
700
- saveUrlResponseBody(url, contentRaw)
744
+ return {
745
+ code,
746
+ map,
701
747
  }
702
-
703
- return { code: content, map }
704
748
  },
705
749
 
706
- async transform(codeInput, id) {
750
+ async transform(code, id) {
751
+ let map = null
707
752
  // we should try/catch here?
708
753
  // because this.parse might fail
709
- const ast = this.parse(codeInput, {
754
+ const ast = this.parse(code, {
710
755
  // used to know node line and column
711
756
  locations: true,
712
757
  })
713
758
  // const moduleInfo = this.getModuleInfo(id)
714
759
  const url = asServerUrl(id)
715
- const importerUrl = urlImporterMap[url]
716
- const { code, map } = await transformImportMetaUrlReferences({
717
- url,
718
- importerUrl,
719
- code: codeInput,
760
+
761
+ const mutations = []
762
+ await visitImportReferences({
720
763
  ast,
721
- ressourceBuilder,
722
- markBuildRelativeUrlAsUsedByJs,
764
+ onReferenceWithImportMetaUrlPattern: async ({ importNode }) => {
765
+ const specifier = importNode.arguments[0].value
766
+ const { line, column } = importNode.loc.start
767
+ const reference =
768
+ await ressourceBuilder.createReferenceFoundInJsModule({
769
+ jsUrl: url,
770
+ jsLine: line,
771
+ jsColumn: column,
772
+ ressourceSpecifier: specifier,
773
+ })
774
+ if (!reference) {
775
+ return
776
+ }
777
+ markBuildRelativeUrlAsUsedByJs(reference.ressource.buildRelativeUrl)
778
+ mutations.push((magicString) => {
779
+ magicString.overwrite(
780
+ importNode.start,
781
+ importNode.end,
782
+ referenceToCodeForRollup(reference),
783
+ )
784
+ })
785
+ },
786
+ onReferenceWithImportAssertion: async ({
787
+ importNode,
788
+ typePropertyNode,
789
+ assertions,
790
+ }) => {
791
+ const { source } = importNode
792
+ const importSpecifier = source.value
793
+ const { line, column } = importNode.loc.start
794
+
795
+ // "type" is dynamic on dynamic import such as
796
+ // import("./data.json", {
797
+ // assert: {
798
+ // type: true ? "json" : "css"
799
+ // }
800
+ // })
801
+ if (typePropertyNode) {
802
+ const typePropertyValue = typePropertyNode.value
803
+ if (typePropertyValue.type !== "Literal") {
804
+ if (importAssertionSupportedByRuntime) {
805
+ return // keep untouched
806
+ }
807
+ throw new Error(
808
+ createDetailedMessage(
809
+ `Dynamic "type" not supported for dynamic import assertion`,
810
+ {
811
+ "import assertion trace": stringifyUrlTrace(
812
+ urlLoader.createUrlTrace({ url, line, column }),
813
+ ),
814
+ },
815
+ ),
816
+ )
817
+ }
818
+ assertions = {
819
+ type: typePropertyValue.value,
820
+ }
821
+ }
822
+
823
+ const { type } = assertions
824
+
825
+ // "specifier" is dynamic on dynamic import such as
826
+ // import(true ? "./a.json" : "b.json", {
827
+ // assert: {
828
+ // type: "json"
829
+ // }
830
+ // })
831
+ const importAssertionSupportedByRuntime =
832
+ importAssertionsSupport[type]
833
+ if (source.type !== "Literal") {
834
+ if (importAssertionSupportedByRuntime) {
835
+ return // keep untouched
836
+ }
837
+ throw new Error(
838
+ createDetailedMessage(
839
+ `Dynamic specifier not supported for dynamic import assertion`,
840
+ {
841
+ "import assertion trace": stringifyUrlTrace(
842
+ urlLoader.createUrlTrace({ url, line, column }),
843
+ ),
844
+ },
845
+ ),
846
+ )
847
+ }
848
+
849
+ // There is no strategy for css import assertion on Node.js
850
+ // and that's normal
851
+ if (type === "css" && node) {
852
+ throw new Error(
853
+ createDetailedMessage(
854
+ `{ type: "css" } is not supported when "node" is part of "runtimeSupport"`,
855
+ {
856
+ "import assertion trace": stringifyUrlTrace(
857
+ urlLoader.createUrlTrace({ url, line, column }),
858
+ ),
859
+ },
860
+ ),
861
+ )
862
+ }
863
+
864
+ const { id, external } = normalizeRollupResolveReturnValue(
865
+ await this.resolve(importSpecifier, url, {
866
+ custom: {
867
+ importAssertionInfo: {
868
+ line,
869
+ column,
870
+ type,
871
+ supportedByRuntime: importAssertionSupportedByRuntime,
872
+ },
873
+ },
874
+ }),
875
+ )
876
+
877
+ const ressourceUrl = asServerUrl(id)
878
+ if (external) {
879
+ if (importAssertionSupportedByRuntime) {
880
+ const reference =
881
+ await ressourceBuilder.createReferenceFoundInJsModule({
882
+ isImportAssertion: true,
883
+ jsUrl: url,
884
+ jsLine: line,
885
+ jsColumn: column,
886
+ ressourceSpecifier: ressourceUrl,
887
+ })
888
+ // reference can be null for cross origin urls
889
+ if (!reference) {
890
+ return
891
+ }
892
+ markBuildRelativeUrlAsUsedByJs(
893
+ reference.ressource.buildRelativeUrl,
894
+ )
895
+ return
896
+ }
897
+
898
+ throw new Error(
899
+ createDetailedMessage(
900
+ `import assertion ressource cannot be external when runtime do not support import assertions`,
901
+ {
902
+ "import assertion trace": stringifyUrlTrace(
903
+ urlLoader.createUrlTrace({ url, line, column }),
904
+ ),
905
+ },
906
+ ),
907
+ )
908
+ }
909
+
910
+ // we want to convert the import assertions into a js module
911
+ // to do that we append ?import_type to the url
912
+ // In theory this is not needed anymore:
913
+ // This is already done by the compile server
914
+ const ressourceUrlAsJsModule = setUrlSearchParamsDescriptor(
915
+ ressourceUrl,
916
+ {
917
+ import_type: type,
918
+ },
919
+ )
920
+
921
+ mutations.push((magicString) => {
922
+ magicString.overwrite(
923
+ importNode.source.start,
924
+ importNode.source.end,
925
+ `"${ressourceUrlAsJsModule}"`,
926
+ )
927
+ if (typePropertyNode) {
928
+ magicString.remove(typePropertyNode.start, typePropertyNode.end)
929
+ }
930
+ })
931
+ },
723
932
  })
933
+ if (mutations.length > 0) {
934
+ const { default: MagicString } = await import("magic-string")
935
+ const magicString = new MagicString(code)
936
+ mutations.forEach((mutation) => {
937
+ mutation(magicString)
938
+ })
939
+ code = magicString.toString()
940
+ map = magicString.generateMap({ hires: true })
941
+ }
942
+
724
943
  return { code, map }
725
944
  },
726
945
 
727
946
  // resolveImportMeta: () => {}
728
-
729
947
  outputOptions: (outputOptions) => {
730
948
  const extension = extname(entryPointMap[Object.keys(entryPointMap)[0]])
731
949
  const outputExtension = extension === ".html" ? ".js" : extension
@@ -760,9 +978,7 @@ building ${entryFileRelativeUrls.length} entry files...`)
760
978
  const url = relativePathToUrl(relativePath, sourcemapUrl)
761
979
  const serverUrl = asServerUrl(url)
762
980
  const finalUrl =
763
- serverUrl in urlRedirectionMap
764
- ? urlRedirectionMap[serverUrl]
765
- : serverUrl
981
+ urlFetcher.getUrlBeforeRedirection(serverUrl) || serverUrl
766
982
  const projectUrl = asProjectUrl(finalUrl)
767
983
 
768
984
  if (projectUrl) {
@@ -792,12 +1008,10 @@ building ${entryFileRelativeUrls.length} entry files...`)
792
1008
  return null
793
1009
  }
794
1010
 
795
- // TODO: maybe replace chunk.fileName with chunk.facadeModuleId?
796
- const result = await minifyJs(code, chunk.fileName, {
797
- sourceMap: {
798
- ...(map ? { content: JSON.stringify(map) } : {}),
799
- asObject: true,
800
- },
1011
+ const result = await minifyJs({
1012
+ url: asOriginalUrl(chunk.facadeModuleId),
1013
+ code,
1014
+ map,
801
1015
  ...(format === "global" ? { toplevel: false } : { toplevel: true }),
802
1016
  ...minifyJsOptions,
803
1017
  })
@@ -883,10 +1097,8 @@ building ${entryFileRelativeUrls.length} entry files...`)
883
1097
  const jsRessource = ressourceBuilder.findRessource(
884
1098
  (ressource) => ressource.url === file.url,
885
1099
  )
886
- // avant buildEnd il se peut que certaines ressources ne soit pas encore inline
887
- // donc dans inlinedCallback on voudras ptet delete ces ressources?
888
1100
  if (jsRessource && jsRessource.isInline) {
889
- buildInlineFileContents[fileName] = file.code
1101
+ buildInlineFileContents[buildRelativeUrl] = file.code
890
1102
  } else {
891
1103
  markBuildRelativeUrlAsUsedByJs(buildRelativeUrl)
892
1104
  buildManifest[fileName] = buildRelativeUrl
@@ -915,6 +1127,11 @@ building ${entryFileRelativeUrls.length} entry files...`)
915
1127
  return
916
1128
  }
917
1129
 
1130
+ const { facadeModuleId } = file
1131
+ if (facadeModuleId === EMPTY_CHUNK_URL) {
1132
+ return
1133
+ }
1134
+
918
1135
  const assetRessource = ressourceBuilder.findRessource(
919
1136
  (ressource) => ressource.relativeUrl === rollupFileId,
920
1137
  )
@@ -935,10 +1152,22 @@ building ${entryFileRelativeUrls.length} entry files...`)
935
1152
  return
936
1153
  }
937
1154
 
1155
+ // Ignore file only referenced by import assertions
1156
+ // - if file is referenced by import assertion and html or import meta url
1157
+ // then source file is duplicated. If concatenation is disabled
1158
+ // and import assertions are supported, the file is still converted to js module
1159
+ const isReferencedOnlyByImportAssertions =
1160
+ assetRessource.references.every((reference) => {
1161
+ return reference.isImportAssertion
1162
+ })
1163
+ if (isReferencedOnlyByImportAssertions) {
1164
+ return
1165
+ }
1166
+
938
1167
  const buildRelativeUrl = assetRessource.buildRelativeUrl
939
1168
  const fileName = rollupFileNameWithoutHash(buildRelativeUrl)
940
1169
  if (assetRessource.isInline) {
941
- buildInlineFileContents[fileName] = file.source
1170
+ buildInlineFileContents[buildRelativeUrl] = file.source
942
1171
  } else {
943
1172
  const originalProjectUrl = asOriginalUrl(assetRessource.url)
944
1173
  const originalProjectRelativeUrl = urlToRelativeUrl(
@@ -964,19 +1193,24 @@ building ${entryFileRelativeUrls.length} entry files...`)
964
1193
 
965
1194
  // update rollupBuild, buildInlineFilesContents, buildManifest and buildMappings
966
1195
  // in case some ressources where inlined by ressourceBuilder.rollupBuildEnd
967
- Object.keys(buildManifest).forEach((fileName) => {
968
- const buildRelativeUrl = buildManifest[fileName]
969
- const ressource = ressourceBuilder.findRessource(
970
- (ressource) => ressource.buildRelativeUrl === buildRelativeUrl,
971
- )
1196
+ Object.keys(rollupBuild).forEach((buildRelativeUrl) => {
1197
+ const rollupFileInfo = rollupBuild[buildRelativeUrl]
1198
+ const ressource = ressourceBuilder.findRessource((ressource) => {
1199
+ return (
1200
+ ressource.buildRelativeUrl === buildRelativeUrl ||
1201
+ rollupFileInfo.facadeModuleId === ressource.url
1202
+ )
1203
+ })
972
1204
  if (ressource && ressource.isInline) {
973
- delete buildManifest[fileName]
1205
+ const fileName = buildRelativeUrlToFileName(buildRelativeUrl)
1206
+ if (fileName) {
1207
+ delete buildManifest[fileName]
1208
+ }
974
1209
  const originalProjectUrl = asOriginalUrl(ressource.url)
975
1210
  delete buildMappings[
976
1211
  urlToRelativeUrl(originalProjectUrl, projectDirectoryUrl)
977
1212
  ]
978
- buildInlineFileContents[buildRelativeUrl] =
979
- rollupBuild[buildRelativeUrl].code
1213
+ buildInlineFileContents[buildRelativeUrl] = rollupFileInfo.code
980
1214
  delete rollupBuild[buildRelativeUrl]
981
1215
  }
982
1216
  })
@@ -1028,141 +1262,11 @@ building ${entryFileRelativeUrls.length} entry files...`)
1028
1262
  },
1029
1263
  }
1030
1264
 
1031
- const saveUrlResponseBody = (url, responseBody) => {
1032
- urlResponseBodyMap[url] = responseBody
1033
- const projectUrl = asProjectUrl(url)
1034
- if (projectUrl && projectUrl !== url) {
1035
- urlResponseBodyMap[projectUrl] = responseBody
1036
- }
1037
- }
1038
-
1039
- const loadModule = async (
1040
- moduleUrl,
1041
- // {
1042
- // moduleInfo
1043
- // },
1044
- ) => {
1045
- if (moduleUrl in inlineModuleScripts) {
1046
- const { code, map } = await transformJs({
1047
- code: inlineModuleScripts[moduleUrl],
1048
- url: asProjectUrl(moduleUrl), // transformJs expect a file:// url
1049
- projectDirectoryUrl,
1050
-
1051
- babelPluginMap,
1052
- // moduleOutFormat: format // we are compiling for rollup output must be "esmodule"
1053
- })
1054
-
1055
- return {
1056
- responseUrl: moduleUrl,
1057
- contentRaw: code,
1058
- content: code,
1059
- map,
1060
- }
1061
- }
1062
-
1063
- const importerUrl = urlImporterMap[moduleUrl]
1064
- const moduleResponse = await jsenvFetchUrl(moduleUrl, () => {
1065
- return createImportTrace({
1066
- importer: asProjectUrl(importerUrl) || importerUrl,
1067
- asServerUrl,
1068
- asOriginalUrl,
1069
- asProjectUrl,
1070
- urlImporterMap,
1071
- })
1072
- })
1073
- const responseContentType = moduleResponse.headers["content-type"] || ""
1074
- const commonData = {
1075
- responseContentType,
1076
- responseUrl: moduleResponse.url,
1077
- }
1078
-
1079
- // keep this in sync with module-registration.js
1080
- if (
1081
- responseContentType === "application/javascript" ||
1082
- responseContentType === "text/javascript"
1083
- ) {
1084
- const jsModuleString = await moduleResponse.text()
1085
- const map = await fetchSourcemap(moduleUrl, jsModuleString, {
1086
- cancellationToken,
1087
- logger,
1088
- })
1089
-
1090
- return {
1091
- ...commonData,
1092
- responseContentType: "application/javascript", // normalize
1093
- contentRaw: jsModuleString,
1094
- content: jsModuleString,
1095
- map,
1096
- }
1097
- }
1098
-
1099
- if (
1100
- acceptsJsonContentType({ node, format }) &&
1101
- (responseContentType === "application/json" ||
1102
- responseContentType.endsWith("+json"))
1103
- ) {
1104
- const responseBodyAsString = await moduleResponse.text()
1105
- // there is no need to minify the json string
1106
- // because it becomes valid javascript
1107
- // that will be minified by minifyJs inside renderChunk
1108
- const json = responseBodyAsString
1109
- return {
1110
- ...commonData,
1111
- contentRaw: json,
1112
- content: `export default ${json}`,
1113
- }
1114
- }
1115
-
1116
- const urlRelativeToImporter = urlToRelativeUrl(moduleUrl, importerUrl)
1117
- const jsenvPluginError = new Error(
1118
- `"${responseContentType}" is not a valid type for JavaScript module
1119
- --- js module url ---
1120
- ${asOriginalUrl(moduleUrl)}
1121
- --- importer url ---
1122
- ${asOriginalUrl(importerUrl)}
1123
- --- suggestion ---
1124
- non-js ressources can be used with new URL("${urlRelativeToImporter}", import.meta.url)`,
1125
- )
1126
- storeLatestJsenvPluginError(jsenvPluginError)
1127
- throw jsenvPluginError
1128
- }
1129
-
1130
- const jsenvFetchUrl = async (url, importer) => {
1131
- const urlToFetch = applyUrlMappings(url)
1132
-
1133
- const response = await fetchUrl(urlToFetch, {
1134
- cancellationToken,
1135
- ignoreHttpsError: true,
1136
- })
1137
- const responseUrl = response.url
1138
-
1139
- const okValidation = await validateResponseStatusIsOk(response, {
1140
- originalUrl:
1141
- asOriginalUrl(responseUrl) || asProjectUrl(responseUrl) || responseUrl,
1142
- importer,
1143
- })
1144
- if (!okValidation.valid) {
1145
- const jsenvPluginError = new Error(okValidation.message)
1146
- storeLatestJsenvPluginError(jsenvPluginError)
1147
- throw jsenvPluginError
1148
- }
1149
-
1150
- if (url !== responseUrl) {
1151
- urlRedirectionMap[url] = responseUrl
1152
- }
1153
-
1154
- return response
1155
- }
1156
-
1157
1265
  const fetchImportMapFromUrl = async (importMapUrl, importer) => {
1158
- const importMapResponse = await jsenvFetchUrl(importMapUrl, importer)
1159
- const okValidation = await validateResponseStatusIsOk(importMapResponse, {
1160
- originalUrl: asOriginalUrl(importMapUrl),
1161
- traceImport: () => [importer],
1266
+ const importMapResponse = await urlFetcher.fetchUrl(importMapUrl, {
1267
+ urlTrace: importer,
1268
+ contentTypeExpected: "application/importmap+json",
1162
1269
  })
1163
- if (!okValidation.valid) {
1164
- throw new Error(okValidation.message)
1165
- }
1166
1270
  const importMap = await importMapResponse.json()
1167
1271
  const importMapNormalized = normalizeImportMap(
1168
1272
  importMap,
@@ -1177,7 +1281,7 @@ non-js ressources can be used with new URL("${urlRelativeToImporter}", import.me
1177
1281
  getResult: () => {
1178
1282
  return {
1179
1283
  rollupBuild,
1180
- urlResponseBodyMap,
1284
+ urlResponseBodyMap: urlLoader.getUrlResponseBodyMap(),
1181
1285
  buildMappings,
1182
1286
  buildManifest,
1183
1287
  buildImportMap: createImportMapForFilesUsedInJs(),
@@ -1219,7 +1323,7 @@ const prepareEntryPoints = async (
1219
1323
  projectDirectoryUrl,
1220
1324
  buildDirectoryUrl,
1221
1325
  compileServerOrigin,
1222
- fetchFile,
1326
+ urlFetcher,
1223
1327
  },
1224
1328
  ) => {
1225
1329
  const entryFileRelativeUrls = Object.keys(entryPointMap)
@@ -1252,7 +1356,9 @@ const prepareEntryPoints = async (
1252
1356
  compileServerOrigin,
1253
1357
  )
1254
1358
 
1255
- const entryResponse = await fetchFile(entryServerUrl, `entryPointMap`)
1359
+ const entryResponse = await urlFetcher.fetchUrl(entryServerUrl, {
1360
+ urlTrace: `entryPointMap`,
1361
+ })
1256
1362
  const entryContentType = entryResponse.headers["content-type"]
1257
1363
  const isHtml = entryContentType === "text/html"
1258
1364
 
@@ -1272,40 +1378,6 @@ const prepareEntryPoints = async (
1272
1378
  return entryPointsPrepared
1273
1379
  }
1274
1380
 
1275
- const createImportTrace = ({
1276
- importer,
1277
- asServerUrl,
1278
- asOriginalUrl,
1279
- asProjectUrl,
1280
- urlImporterMap,
1281
- }) => {
1282
- const trace = [
1283
- {
1284
- type: "entry",
1285
- url: asOriginalUrl(importer) || asProjectUrl(importer) || importer,
1286
- },
1287
- ]
1288
-
1289
- const next = (importerUrl) => {
1290
- const previousImporterUrl = urlImporterMap[importerUrl]
1291
- if (!previousImporterUrl) {
1292
- return
1293
- }
1294
- const previousImporterOriginalUrl =
1295
- asOriginalUrl(previousImporterUrl) ||
1296
- asProjectUrl(previousImporterUrl) ||
1297
- previousImporterUrl
1298
- trace.push({
1299
- type: "import",
1300
- url: previousImporterOriginalUrl,
1301
- })
1302
- next(previousImporterUrl)
1303
- }
1304
- next(asServerUrl(importer))
1305
-
1306
- return trace
1307
- }
1308
-
1309
1381
  const fixRollupUrl = (rollupUrl) => {
1310
1382
  // fix rollup not supporting source being http
1311
1383
  const httpIndex = rollupUrl.indexOf(`http:/`, 1)
@@ -1326,6 +1398,17 @@ const fixRollupUrl = (rollupUrl) => {
1326
1398
  return rollupUrl
1327
1399
  }
1328
1400
 
1401
+ const normalizeRollupResolveReturnValue = (resolveReturnValue) => {
1402
+ if (resolveReturnValue === null) {
1403
+ return { id: null, external: true }
1404
+ }
1405
+ if (typeof resolveReturnValue === "string") {
1406
+ return { id: resolveReturnValue, external: false }
1407
+ }
1408
+
1409
+ return resolveReturnValue
1410
+ }
1411
+
1329
1412
  const rollupFileNameWithoutHash = (fileName) => {
1330
1413
  return fileName.replace(/-[a-z0-9]{8,}(\..*?)?$/, (_, afterHash = "") => {
1331
1414
  return afterHash