@jsenv/core 28.1.1 → 28.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 (51) hide show
  1. package/dist/js/script_type_module_supervisor.js +8 -13
  2. package/dist/js/supervisor.js +702 -534
  3. package/dist/main.js +13275 -13164
  4. package/package.json +5 -5
  5. package/readme.md +3 -3
  6. package/src/build/build.js +960 -712
  7. package/src/build/inject_global_version_mappings.js +5 -20
  8. package/src/build/start_build_server.js +2 -2
  9. package/src/dev/start_dev_server.js +3 -2
  10. package/src/execute/run.js +1 -1
  11. package/src/execute/runtimes/browsers/from_playwright.js +1 -1
  12. package/src/omega/compat/runtime_compat.js +9 -6
  13. package/src/omega/errors.js +3 -0
  14. package/src/omega/fetched_content_compliance.js +2 -2
  15. package/src/omega/kitchen.js +189 -145
  16. package/src/omega/server/file_service.js +104 -71
  17. package/src/omega/url_graph/url_graph_loader.js +77 -0
  18. package/src/omega/url_graph/url_info_transformations.js +12 -15
  19. package/src/omega/url_graph.js +115 -101
  20. package/src/plugins/autoreload/jsenv_plugin_autoreload_client.js +1 -0
  21. package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +34 -36
  22. package/src/plugins/autoreload/jsenv_plugin_hmr.js +3 -2
  23. package/src/plugins/bundling/js_module/{bundle_js_module.js → bundle_js_modules.js} +51 -14
  24. package/src/plugins/bundling/jsenv_plugin_bundling.js +2 -2
  25. package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +11 -0
  26. package/src/plugins/inline/jsenv_plugin_html_inline_content.js +73 -62
  27. package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +77 -89
  28. package/src/plugins/plugin_controller.js +26 -22
  29. package/src/plugins/server_events/jsenv_plugin_server_events_client_injection.js +1 -0
  30. package/src/plugins/supervisor/client/script_type_module_supervisor.js +7 -9
  31. package/src/plugins/supervisor/client/supervisor.js +125 -96
  32. package/src/plugins/supervisor/jsenv_plugin_supervisor.js +2 -4
  33. package/src/plugins/toolbar/client/execution/toolbar_execution.js +1 -1
  34. package/src/plugins/transpilation/as_js_classic/async-to-promises.js +16 -0
  35. package/src/plugins/transpilation/as_js_classic/convert_js_module_to_js_classic.js +85 -0
  36. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +48 -190
  37. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_conversion.js +102 -0
  38. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +161 -240
  39. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_library.js +84 -0
  40. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_workers.js +19 -12
  41. package/src/plugins/transpilation/babel/jsenv_plugin_babel.js +1 -24
  42. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +82 -52
  43. package/src/plugins/transpilation/jsenv_plugin_transpilation.js +12 -13
  44. package/src/plugins/url_analysis/html/html_urls.js +91 -34
  45. package/src/plugins/url_analysis/js/js_urls.js +5 -4
  46. package/src/plugins/url_resolution/jsenv_plugin_url_resolution.js +1 -0
  47. package/src/test/execute_plan.js +3 -8
  48. package/src/test/execute_test_plan.js +1 -1
  49. package/src/build/inject_service_worker_urls.js +0 -78
  50. package/src/build/resync_resource_hints.js +0 -112
  51. package/src/omega/url_graph/url_graph_load.js +0 -74
@@ -1,12 +1,11 @@
1
1
  import { urlToRelativeUrl } from "@jsenv/urls"
2
+
2
3
  import { urlSpecifierEncoding } from "./url_specifier_encoding.js"
3
4
 
4
- export const createUrlGraph = ({
5
- clientFileChangeCallbackList,
6
- clientFilesPruneCallbackList,
7
- onCreateUrlInfo = () => {},
8
- includeOriginalUrls,
9
- } = {}) => {
5
+ export const createUrlGraph = () => {
6
+ const createUrlInfoCallbackRef = { current: () => {} }
7
+ const prunedUrlInfosCallbackRef = { current: () => {} }
8
+
10
9
  const urlInfoMap = new Map()
11
10
  const getUrlInfo = (url) => urlInfoMap.get(url)
12
11
  const deleteUrlInfo = (url) => {
@@ -18,15 +17,18 @@ export const createUrlGraph = ({
18
17
  }
19
18
  }
20
19
  }
21
-
22
20
  const reuseOrCreateUrlInfo = (url) => {
23
21
  const existingUrlInfo = getUrlInfo(url)
24
22
  if (existingUrlInfo) return existingUrlInfo
25
23
  const urlInfo = createUrlInfo(url)
26
24
  urlInfoMap.set(url, urlInfo)
27
- onCreateUrlInfo(urlInfo)
25
+ createUrlInfoCallbackRef.current(urlInfo)
28
26
  return urlInfo
29
27
  }
28
+ const getParentIfInline = (urlInfo) => {
29
+ return urlInfo.isInline ? getUrlInfo(urlInfo.inlineUrlSite.url) : urlInfo
30
+ }
31
+
30
32
  const inferReference = (specifier, parentUrl) => {
31
33
  const parentUrlInfo = getUrlInfo(parentUrl)
32
34
  if (!parentUrlInfo) {
@@ -39,36 +41,33 @@ export const createUrlGraph = ({
39
41
  )
40
42
  return firstReferenceOnThatUrl
41
43
  }
42
- const findDependent = (url, predicate) => {
43
- const urlInfo = getUrlInfo(url)
44
- if (!urlInfo) {
45
- return null
44
+ const visitDependents = (urlInfo, visitor) => {
45
+ const seen = [urlInfo.url]
46
+ let stopped = false
47
+ const stop = () => {
48
+ stopped = true
46
49
  }
47
- const visitDependents = (urlInfo) => {
48
- for (const dependentUrl of urlInfo.dependents) {
49
- const dependent = getUrlInfo(dependentUrl)
50
- if (predicate(dependent)) {
51
- return dependent
50
+ const iterate = (currentUrlInfo) => {
51
+ for (const dependentUrl of currentUrlInfo.dependents) {
52
+ if (seen.includes(dependentUrl)) {
53
+ continue
54
+ }
55
+ seen.push(dependentUrl)
56
+ const dependentUrlInfo = getUrlInfo(dependentUrl)
57
+ visitor(dependentUrlInfo, stop)
58
+ if (stopped) {
59
+ return dependentUrlInfo
52
60
  }
53
- return visitDependents(dependent)
61
+ iterate(dependentUrlInfo)
54
62
  }
55
63
  return null
56
64
  }
57
- return visitDependents(urlInfo)
65
+ return iterate(urlInfo)
58
66
  }
59
67
 
60
68
  const updateReferences = (urlInfo, references) => {
61
69
  const setOfDependencyUrls = new Set()
62
-
63
- // for import assertion "file.css?as_css_module" depends on "file.css"
64
- // this is enabled only for dev where there is autoreload
65
- // during build the css file must be considered as not referenced
66
- // (except if referenced explicitely by something else) so that
67
- // the css file does not appear in the build directory
68
- if (includeOriginalUrls && urlInfo.originalUrl !== urlInfo.url) {
69
- setOfDependencyUrls.add(urlInfo.originalUrl)
70
- }
71
-
70
+ const setOfImplicitUrls = new Set()
72
71
  references.forEach((reference) => {
73
72
  if (reference.isResourceHint) {
74
73
  // resource hint are a special kind of reference.
@@ -79,67 +78,83 @@ export const createUrlGraph = ({
79
78
  // by <link> as dependency and it's fine
80
79
  return
81
80
  }
82
- setOfDependencyUrls.add(reference.url)
81
+ const dependencyUrl = reference.url
82
+ setOfDependencyUrls.add(dependencyUrl)
83
+ // an implicit reference do not appear in the file but a non-explicited file have an impact on it
84
+ // (package.json on import resolution for instance)
85
+ // in that case:
86
+ // - file depends on the implicit file (it must autoreload if package.json is modified)
87
+ // - cache validity for the file depends on the implicit file (it must be re-cooked in package.json is modified)
88
+ if (reference.isImplicit) {
89
+ setOfImplicitUrls.add(dependencyUrl)
90
+ }
83
91
  })
84
- const urlsToRemove = Array.from(urlInfo.dependencies).filter(
85
- (dep) => !setOfDependencyUrls.has(dep),
86
- )
87
- pruneDependencies(urlInfo, urlsToRemove)
88
- urlInfo.references = references
89
92
  setOfDependencyUrls.forEach((dependencyUrl) => {
90
- const dependencyUrlInfo = reuseOrCreateUrlInfo(dependencyUrl)
91
93
  urlInfo.dependencies.add(dependencyUrl)
94
+ const dependencyUrlInfo = reuseOrCreateUrlInfo(dependencyUrl)
92
95
  dependencyUrlInfo.dependents.add(urlInfo.url)
93
96
  })
94
- return urlInfo
95
- }
96
- const pruneDependencies = (firstUrlInfo, urlsToRemove) => {
97
+ setOfImplicitUrls.forEach((implicitUrl) => {
98
+ urlInfo.implicitUrls.add(implicitUrl)
99
+ if (urlInfo.isInline) {
100
+ const parentUrlInfo = getUrlInfo(urlInfo.inlineUrlSite.url)
101
+ parentUrlInfo.implicitUrls.add(implicitUrl)
102
+ }
103
+ })
97
104
  const prunedUrlInfos = []
98
- const removeDependencies = (urlInfo, urlsToPrune) => {
99
- urlsToPrune.forEach((urlToPrune) => {
100
- urlInfo.dependencies.delete(urlToPrune)
101
- const dependencyUrlInfo = getUrlInfo(urlToPrune)
102
- if (!dependencyUrlInfo) {
103
- return
104
- }
105
- dependencyUrlInfo.dependents.delete(urlInfo.url)
106
- if (dependencyUrlInfo.dependents.size === 0) {
107
- removeDependencies(
108
- dependencyUrlInfo,
109
- Array.from(dependencyUrlInfo.dependencies),
110
- )
111
- prunedUrlInfos.push(dependencyUrlInfo)
112
- }
113
- })
114
- }
115
- removeDependencies(firstUrlInfo, urlsToRemove)
116
- if (prunedUrlInfos.length === 0) {
117
- return
105
+ const pruneDependency = (urlInfo, urlToClean) => {
106
+ urlInfo.dependencies.delete(urlToClean)
107
+ const dependencyUrlInfo = getUrlInfo(urlToClean)
108
+ if (!dependencyUrlInfo) {
109
+ return
110
+ }
111
+ dependencyUrlInfo.dependents.delete(urlInfo.url)
112
+ if (dependencyUrlInfo.dependents.size === 0) {
113
+ dependencyUrlInfo.dependencies.forEach((dependencyUrl) => {
114
+ pruneDependency(dependencyUrlInfo, dependencyUrl)
115
+ })
116
+ prunedUrlInfos.push(dependencyUrlInfo)
117
+ }
118
118
  }
119
- prunedUrlInfos.forEach((prunedUrlInfo) => {
120
- prunedUrlInfo.modifiedTimestamp = Date.now()
121
- if (prunedUrlInfo.isInline) {
122
- // should we always delete?
123
- deleteUrlInfo(prunedUrlInfo.url)
119
+ urlInfo.dependencies.forEach((dependencyUrl) => {
120
+ if (!setOfDependencyUrls.has(dependencyUrl)) {
121
+ pruneDependency(urlInfo, dependencyUrl)
124
122
  }
125
123
  })
126
- if (clientFilesPruneCallbackList) {
127
- clientFilesPruneCallbackList.forEach((callback) => {
128
- callback({
129
- firstUrlInfo,
130
- prunedUrlInfos,
131
- })
124
+ if (prunedUrlInfos.length) {
125
+ prunedUrlInfos.forEach((prunedUrlInfo) => {
126
+ prunedUrlInfo.modifiedTimestamp = Date.now()
127
+ if (prunedUrlInfo.isInline) {
128
+ // should we always delete?
129
+ deleteUrlInfo(prunedUrlInfo.url)
130
+ }
132
131
  })
132
+ prunedUrlInfosCallbackRef.current(prunedUrlInfos, urlInfo)
133
133
  }
134
- }
135
-
136
- if (clientFileChangeCallbackList) {
137
- clientFileChangeCallbackList.push(({ url }) => {
138
- const urlInfo = getUrlInfo(url)
139
- if (urlInfo) {
140
- considerModified(urlInfo, Date.now())
134
+ urlInfo.implicitUrls.forEach((implicitUrl) => {
135
+ if (!setOfDependencyUrls.has(implicitUrl)) {
136
+ let implicitUrlComesFromInlineContent = false
137
+ for (const dependencyUrl of urlInfo.dependencies) {
138
+ const dependencyUrlInfo = getUrlInfo(dependencyUrl)
139
+ if (
140
+ dependencyUrlInfo.isInline &&
141
+ dependencyUrlInfo.implicitUrls.has(implicitUrl)
142
+ ) {
143
+ implicitUrlComesFromInlineContent = true
144
+ break
145
+ }
146
+ }
147
+ if (!implicitUrlComesFromInlineContent) {
148
+ urlInfo.implicitUrls.delete(implicitUrl)
149
+ }
150
+ if (urlInfo.isInline) {
151
+ const parentUrlInfo = getUrlInfo(urlInfo.inlineUrlSite.url)
152
+ parentUrlInfo.implicitUrls.delete(implicitUrl)
153
+ }
141
154
  }
142
155
  })
156
+ urlInfo.references = references
157
+ return urlInfo
143
158
  }
144
159
 
145
160
  const considerModified = (urlInfo, modifiedTimestamp = Date.now()) => {
@@ -150,6 +165,7 @@ export const createUrlGraph = ({
150
165
  }
151
166
  seen.push(urlInfo.url)
152
167
  urlInfo.modifiedTimestamp = modifiedTimestamp
168
+ urlInfo.originalContentEtag = undefined
153
169
  urlInfo.contentEtag = undefined
154
170
  urlInfo.dependents.forEach((dependentUrl) => {
155
171
  const dependentUrlInfo = getUrlInfo(dependentUrl)
@@ -168,30 +184,20 @@ export const createUrlGraph = ({
168
184
  iterate(urlInfo)
169
185
  }
170
186
 
171
- const getRelatedUrlInfos = (url) => {
172
- const urlInfosUntilNotInline = []
173
- const parentUrlInfo = getUrlInfo(url)
174
- if (parentUrlInfo) {
175
- urlInfosUntilNotInline.push(parentUrlInfo)
176
- if (parentUrlInfo.inlineUrlSite) {
177
- urlInfosUntilNotInline.push(
178
- ...getRelatedUrlInfos(parentUrlInfo.inlineUrlSite.url),
179
- )
180
- }
181
- }
182
- return urlInfosUntilNotInline
183
- }
184
-
185
187
  return {
188
+ createUrlInfoCallbackRef,
189
+ prunedUrlInfosCallbackRef,
190
+
186
191
  urlInfoMap,
187
192
  reuseOrCreateUrlInfo,
188
193
  getUrlInfo,
189
194
  deleteUrlInfo,
195
+ getParentIfInline,
196
+
190
197
  inferReference,
191
- findDependent,
192
198
  updateReferences,
193
199
  considerModified,
194
- getRelatedUrlInfos,
200
+ visitDependents,
195
201
 
196
202
  toObject: () => {
197
203
  const data = {}
@@ -217,26 +223,25 @@ export const createUrlGraph = ({
217
223
  }
218
224
 
219
225
  const createUrlInfo = (url) => {
220
- return {
226
+ const urlInfo = {
221
227
  error: null,
222
228
  modifiedTimestamp: 0,
229
+ originalContentEtag: null,
223
230
  contentEtag: null,
224
- dependsOnPackageJson: false,
225
- isValid,
231
+ isWatched: false,
232
+ isValid: () => false,
226
233
  data: {}, // plugins can put whatever they want here
227
234
  references: [],
228
235
  dependencies: new Set(),
229
236
  dependents: new Set(),
237
+ implicitUrls: new Set(),
230
238
  type: undefined, // "html", "css", "js_classic", "js_module", "importmap", "json", "webmanifest", ...
231
239
  subtype: undefined, // "worker", "service_worker", "shared_worker" for js, otherwise undefined
232
240
  contentType: "", // "text/html", "text/css", "text/javascript", "application/json", ...
233
241
  url,
234
242
  originalUrl: undefined,
235
- generatedUrl: null,
236
243
  filename: "",
237
244
  isEntryPoint: false,
238
- isInline: false,
239
- inlineUrlSite: null,
240
245
  shouldHandle: undefined,
241
246
  originalContent: undefined,
242
247
  content: undefined,
@@ -244,9 +249,18 @@ const createUrlInfo = (url) => {
244
249
  sourcemap: null,
245
250
  sourcemapReference: null,
246
251
  sourcemapIsWrong: false,
252
+
253
+ generatedUrl: null,
254
+ sourcemapGeneratedUrl: null,
255
+ injected: false,
256
+
257
+ isInline: false,
258
+ inlineUrlSite: null,
259
+ jsQuote: null, // maybe move to inlineUrlSite?
260
+
247
261
  timing: {},
248
262
  headers: {},
249
263
  }
264
+ // Object.preventExtensions(urlInfo) // useful to ensure all properties are declared here
265
+ return urlInfo
250
266
  }
251
-
252
- const isValid = () => false
@@ -19,6 +19,7 @@ export const jsenvPluginAutoreloadClient = () => {
19
19
  const htmlAst = parseHtmlString(htmlUrlInfo.content)
20
20
  const [autoreloadClientReference] = context.referenceUtils.inject({
21
21
  type: "script_src",
22
+ subtype: "js_module",
22
23
  expectedType: "js_module",
23
24
  specifier: autoreloadClientFileUrl,
24
25
  })
@@ -134,48 +134,46 @@ export const jsenvPluginAutoreloadServer = ({
134
134
  })
135
135
  }
136
136
  })
137
- clientFilesPruneCallbackList.push(
138
- ({ prunedUrlInfos, firstUrlInfo }) => {
139
- const mainHotUpdate = propagateUpdate(firstUrlInfo)
140
- const cause = `following files are no longer referenced: ${prunedUrlInfos.map(
141
- (prunedUrlInfo) => formatUrlForClient(prunedUrlInfo.url),
142
- )}`
143
- // now check if we can hot update the main resource
144
- // then if we can hot update all dependencies
145
- if (mainHotUpdate.declined) {
137
+ clientFilesPruneCallbackList.push((prunedUrlInfos, firstUrlInfo) => {
138
+ const mainHotUpdate = propagateUpdate(firstUrlInfo)
139
+ const cause = `following files are no longer referenced: ${prunedUrlInfos.map(
140
+ (prunedUrlInfo) => formatUrlForClient(prunedUrlInfo.url),
141
+ )}`
142
+ // now check if we can hot update the main resource
143
+ // then if we can hot update all dependencies
144
+ if (mainHotUpdate.declined) {
145
+ notifyDeclined({
146
+ cause,
147
+ reason: mainHotUpdate.reason,
148
+ declinedBy: mainHotUpdate.declinedBy,
149
+ })
150
+ return
151
+ }
152
+ // main can hot update
153
+ let i = 0
154
+ const instructions = []
155
+ while (i < prunedUrlInfos.length) {
156
+ const prunedUrlInfo = prunedUrlInfos[i++]
157
+ if (prunedUrlInfo.data.hotDecline) {
146
158
  notifyDeclined({
147
159
  cause,
148
- reason: mainHotUpdate.reason,
149
- declinedBy: mainHotUpdate.declinedBy,
160
+ reason: `a pruned file declines hot reload`,
161
+ declinedBy: formatUrlForClient(prunedUrlInfo.url),
150
162
  })
151
163
  return
152
164
  }
153
- // main can hot update
154
- let i = 0
155
- const instructions = []
156
- while (i < prunedUrlInfos.length) {
157
- const prunedUrlInfo = prunedUrlInfos[i++]
158
- if (prunedUrlInfo.data.hotDecline) {
159
- notifyDeclined({
160
- cause,
161
- reason: `a pruned file declines hot reload`,
162
- declinedBy: formatUrlForClient(prunedUrlInfo.url),
163
- })
164
- return
165
- }
166
- instructions.push({
167
- type: "prune",
168
- boundary: formatUrlForClient(prunedUrlInfo.url),
169
- acceptedBy: formatUrlForClient(firstUrlInfo.url),
170
- })
171
- }
172
- notifyAccepted({
173
- cause,
174
- reason: mainHotUpdate.reason,
175
- instructions,
165
+ instructions.push({
166
+ type: "prune",
167
+ boundary: formatUrlForClient(prunedUrlInfo.url),
168
+ acceptedBy: formatUrlForClient(firstUrlInfo.url),
176
169
  })
177
- },
178
- )
170
+ }
171
+ notifyAccepted({
172
+ cause,
173
+ reason: mainHotUpdate.reason,
174
+ instructions,
175
+ })
176
+ })
179
177
  },
180
178
  },
181
179
  serve: (request, { rootDirectoryUrl, urlGraph }) => {
@@ -3,12 +3,13 @@ export const jsenvPluginHmr = () => {
3
3
  name: "jsenv:hmr",
4
4
  appliesDuring: "dev",
5
5
  redirectUrl: (reference) => {
6
- const urlObject = new URL(reference.url)
7
- if (!urlObject.searchParams.has("hmr")) {
6
+ if (!reference.searchParams.has("hmr")) {
8
7
  reference.data.hmr = false
9
8
  return null
10
9
  }
10
+
11
11
  reference.data.hmr = true
12
+ const urlObject = new URL(reference.url)
12
13
  // "hmr" search param goal is to mark url as enabling hmr:
13
14
  // this goal is achieved when we reach this part of the code
14
15
  // We get rid of this params so that urlGraph and other parts of the code
@@ -21,7 +21,7 @@ const regeneratorRuntimeClientFileUrl = new URL(
21
21
  import.meta.url,
22
22
  ).href
23
23
 
24
- export const bundleJsModule = async ({
24
+ export const bundleJsModules = async ({
25
25
  jsModuleUrlInfos,
26
26
  context,
27
27
  options,
@@ -35,7 +35,11 @@ export const bundleJsModule = async ({
35
35
  runtimeCompat,
36
36
  sourcemaps,
37
37
  } = context
38
- const { babelHelpersChunk = true, include } = options
38
+ const {
39
+ babelHelpersChunk = true,
40
+ include,
41
+ preserveDynamicImport = false,
42
+ } = options
39
43
  const { jsModuleBundleUrlInfos } = await buildWithRollup({
40
44
  signal,
41
45
  logger,
@@ -49,6 +53,7 @@ export const bundleJsModule = async ({
49
53
 
50
54
  include,
51
55
  babelHelpersChunk,
56
+ preserveDynamicImport,
52
57
  })
53
58
  return jsModuleBundleUrlInfos
54
59
  }
@@ -63,12 +68,18 @@ const rollupPluginJsenv = ({
63
68
 
64
69
  include,
65
70
  babelHelpersChunk,
71
+ preserveDynamicImport,
66
72
 
67
73
  resultRef,
68
74
  }) => {
69
75
  let _rollupEmitFile = () => {
70
76
  throw new Error("not implemented")
71
77
  }
78
+ const format = jsModuleUrlInfos.some((jsModuleUrlInfo) =>
79
+ jsModuleUrlInfo.filename.endsWith(".cjs"),
80
+ )
81
+ ? "cjs"
82
+ : "esm"
72
83
  const emitChunk = (chunk) => {
73
84
  return _rollupEmitFile({
74
85
  type: "chunk",
@@ -118,7 +129,16 @@ const rollupPluginJsenv = ({
118
129
  const rollupFileInfo = rollupResult[fileName]
119
130
  // there is 3 types of file: "placeholder", "asset", "chunk"
120
131
  if (rollupFileInfo.type === "chunk") {
132
+ let url
133
+ if (rollupFileInfo.facadeModuleId) {
134
+ url = fileUrlConverter.asFileUrl(rollupFileInfo.facadeModuleId)
135
+ } else {
136
+ url = new URL(rollupFileInfo.fileName, buildDirectoryUrl).href
137
+ }
121
138
  const jsModuleBundleUrlInfo = {
139
+ url,
140
+ originalUrl: url,
141
+ type: format === "esm" ? "js_module" : "common_js",
122
142
  data: {
123
143
  generatedBy: "rollup",
124
144
  bundleRelativeUrl: rollupFileInfo.fileName,
@@ -126,16 +146,13 @@ const rollupPluginJsenv = ({
126
146
  rollupFileInfo.imports.length > 0 ||
127
147
  rollupFileInfo.dynamicImports.length > 0,
128
148
  },
149
+ sourceUrls: Object.keys(rollupFileInfo.modules).map((id) =>
150
+ fileUrlConverter.asFileUrl(id),
151
+ ),
129
152
  contentType: "text/javascript",
130
153
  content: rollupFileInfo.code,
131
154
  sourcemap: rollupFileInfo.map,
132
155
  }
133
- let url
134
- if (rollupFileInfo.facadeModuleId) {
135
- url = fileUrlConverter.asFileUrl(rollupFileInfo.facadeModuleId)
136
- } else {
137
- url = new URL(rollupFileInfo.fileName, buildDirectoryUrl).href
138
- }
139
156
  jsModuleBundleUrlInfos[url] = jsModuleBundleUrlInfo
140
157
  }
141
158
  })
@@ -146,11 +163,7 @@ const rollupPluginJsenv = ({
146
163
  outputOptions: (outputOptions) => {
147
164
  // const sourcemapFile = buildDirectoryUrl
148
165
  Object.assign(outputOptions, {
149
- format: jsModuleUrlInfos.some((jsModuleUrlInfo) =>
150
- jsModuleUrlInfo.filename.endsWith(".cjs"),
151
- )
152
- ? "cjs"
153
- : "esm",
166
+ format,
154
167
  dir: fileUrlConverter.asFilePath(buildDirectoryUrl),
155
168
  sourcemap: sourcemaps === "file" || sourcemaps === "inline",
156
169
  // sourcemapFile,
@@ -210,11 +223,33 @@ const rollupPluginJsenv = ({
210
223
  // },
211
224
  })
212
225
  },
226
+ // https://rollupjs.org/guide/en/#resolvedynamicimport
227
+ resolveDynamicImport: (specifier, importer) => {
228
+ if (preserveDynamicImport) {
229
+ let urlObject
230
+ if (specifier[0] === "/") {
231
+ urlObject = new URL(specifier.slice(1), rootDirectoryUrl)
232
+ } else {
233
+ if (isFileSystemPath(importer)) {
234
+ importer = fileUrlConverter.asFileUrl(importer)
235
+ }
236
+ urlObject = new URL(specifier, importer)
237
+ }
238
+ urlObject.searchParams.set("as_js_classic_library", "")
239
+ return { external: true, id: urlObject.href }
240
+ }
241
+ return null
242
+ },
213
243
  resolveId: (specifier, importer = rootDirectoryUrl) => {
214
244
  if (isFileSystemPath(importer)) {
215
245
  importer = fileUrlConverter.asFileUrl(importer)
216
246
  }
217
- const url = new URL(specifier, importer).href
247
+ let url
248
+ if (specifier[0] === "/") {
249
+ url = new URL(specifier.slice(1), rootDirectoryUrl).href
250
+ } else {
251
+ url = new URL(specifier, importer).href
252
+ }
218
253
  const existingImporter = urlImporters[url]
219
254
  if (!existingImporter) {
220
255
  urlImporters[url] = importer
@@ -262,6 +297,7 @@ const buildWithRollup = async ({
262
297
 
263
298
  include,
264
299
  babelHelpersChunk,
300
+ preserveDynamicImport,
265
301
  }) => {
266
302
  const resultRef = { current: null }
267
303
  try {
@@ -279,6 +315,7 @@ const buildWithRollup = async ({
279
315
  sourcemaps,
280
316
  include,
281
317
  babelHelpersChunk,
318
+ preserveDynamicImport,
282
319
  resultRef,
283
320
  }),
284
321
  ],
@@ -1,6 +1,6 @@
1
1
  import { bundleCss } from "./css/bundle_css.js"
2
2
  import { bundleJsClassicWorkers } from "./js_classic_workers/bundle_js_classic_workers.js"
3
- import { bundleJsModule } from "./js_module/bundle_js_module.js"
3
+ import { bundleJsModules } from "./js_module/bundle_js_modules.js"
4
4
 
5
5
  export const jsenvPluginBundling = (bundling) => {
6
6
  if (typeof bundling === "boolean") {
@@ -40,7 +40,7 @@ export const jsenvPluginBundling = (bundling) => {
40
40
  : undefined,
41
41
  js_module: bundling.js_module
42
42
  ? (jsModuleUrlInfos, context) => {
43
- return bundleJsModule({
43
+ return bundleJsModules({
44
44
  jsModuleUrlInfos,
45
45
  context,
46
46
  options: bundling.js_module,
@@ -25,6 +25,17 @@ export const jsenvPluginImportMetaHot = () => {
25
25
  htmlUrlInfo.data.hotAcceptSelf = false
26
26
  htmlUrlInfo.data.hotAcceptDependencies = hotReferences.map(
27
27
  ({ type, specifier }) => {
28
+ const existingReference = context.referenceUtils.find(
29
+ (existingReference) => {
30
+ return (
31
+ existingReference.type === type &&
32
+ existingReference.specifier === specifier
33
+ )
34
+ },
35
+ )
36
+ if (existingReference) {
37
+ return existingReference.url
38
+ }
28
39
  const [reference] = context.referenceUtils.found({
29
40
  type,
30
41
  specifier,