@jsenv/core 28.1.3 → 28.2.2

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 (47) hide show
  1. package/dist/js/script_type_module_supervisor.js +8 -13
  2. package/dist/js/supervisor.js +690 -504
  3. package/dist/main.js +13332 -13179
  4. package/package.json +5 -5
  5. package/readme.md +3 -3
  6. package/src/build/build.js +980 -713
  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 +6 -3
  10. package/src/omega/compat/runtime_compat.js +9 -6
  11. package/src/omega/errors.js +3 -0
  12. package/src/omega/fetched_content_compliance.js +2 -2
  13. package/src/omega/kitchen.js +191 -146
  14. package/src/omega/server/file_service.js +104 -71
  15. package/src/omega/url_graph/url_graph_loader.js +77 -0
  16. package/src/omega/url_graph/url_info_transformations.js +12 -15
  17. package/src/omega/url_graph.js +118 -101
  18. package/src/plugins/autoreload/jsenv_plugin_autoreload_client.js +1 -0
  19. package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +34 -36
  20. package/src/plugins/autoreload/jsenv_plugin_hmr.js +3 -2
  21. package/src/plugins/bundling/js_module/{bundle_js_module.js → bundle_js_modules.js} +51 -14
  22. package/src/plugins/bundling/jsenv_plugin_bundling.js +2 -2
  23. package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +11 -0
  24. package/src/plugins/inline/jsenv_plugin_html_inline_content.js +73 -62
  25. package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +77 -89
  26. package/src/plugins/plugin_controller.js +26 -22
  27. package/src/plugins/server_events/jsenv_plugin_server_events_client_injection.js +1 -0
  28. package/src/plugins/supervisor/client/script_type_module_supervisor.js +7 -9
  29. package/src/plugins/supervisor/client/supervisor.js +99 -52
  30. package/src/plugins/supervisor/jsenv_plugin_supervisor.js +2 -4
  31. package/src/plugins/transpilation/as_js_classic/async-to-promises.js +16 -0
  32. package/src/plugins/transpilation/as_js_classic/convert_js_module_to_js_classic.js +85 -0
  33. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +48 -190
  34. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_conversion.js +104 -0
  35. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +161 -240
  36. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_library.js +92 -0
  37. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_workers.js +19 -12
  38. package/src/plugins/transpilation/babel/jsenv_plugin_babel.js +1 -24
  39. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +82 -52
  40. package/src/plugins/transpilation/jsenv_plugin_transpilation.js +12 -13
  41. package/src/plugins/url_analysis/html/html_urls.js +91 -34
  42. package/src/plugins/url_analysis/js/js_urls.js +5 -4
  43. package/src/plugins/url_resolution/jsenv_plugin_url_resolution.js +1 -0
  44. package/src/test/execute_plan.js +3 -8
  45. package/src/build/inject_service_worker_urls.js +0 -78
  46. package/src/build/resync_resource_hints.js +0 -112
  47. package/src/omega/url_graph/url_graph_load.js +0 -74
@@ -1,32 +1,37 @@
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) => {
13
12
  const urlInfo = urlInfoMap.get(url)
14
13
  if (urlInfo) {
15
14
  urlInfoMap.delete(url)
15
+ urlInfo.dependencies.forEach((dependencyUrl) => {
16
+ getUrlInfo(dependencyUrl).dependents.delete(url)
17
+ })
16
18
  if (urlInfo.sourcemapReference) {
17
19
  deleteUrlInfo(urlInfo.sourcemapReference.url)
18
20
  }
19
21
  }
20
22
  }
21
-
22
23
  const reuseOrCreateUrlInfo = (url) => {
23
24
  const existingUrlInfo = getUrlInfo(url)
24
25
  if (existingUrlInfo) return existingUrlInfo
25
26
  const urlInfo = createUrlInfo(url)
26
27
  urlInfoMap.set(url, urlInfo)
27
- onCreateUrlInfo(urlInfo)
28
+ createUrlInfoCallbackRef.current(urlInfo)
28
29
  return urlInfo
29
30
  }
31
+ const getParentIfInline = (urlInfo) => {
32
+ return urlInfo.isInline ? getUrlInfo(urlInfo.inlineUrlSite.url) : urlInfo
33
+ }
34
+
30
35
  const inferReference = (specifier, parentUrl) => {
31
36
  const parentUrlInfo = getUrlInfo(parentUrl)
32
37
  if (!parentUrlInfo) {
@@ -39,36 +44,33 @@ export const createUrlGraph = ({
39
44
  )
40
45
  return firstReferenceOnThatUrl
41
46
  }
42
- const findDependent = (url, predicate) => {
43
- const urlInfo = getUrlInfo(url)
44
- if (!urlInfo) {
45
- return null
47
+ const visitDependents = (urlInfo, visitor) => {
48
+ const seen = [urlInfo.url]
49
+ let stopped = false
50
+ const stop = () => {
51
+ stopped = true
46
52
  }
47
- const visitDependents = (urlInfo) => {
48
- for (const dependentUrl of urlInfo.dependents) {
49
- const dependent = getUrlInfo(dependentUrl)
50
- if (predicate(dependent)) {
51
- return dependent
53
+ const iterate = (currentUrlInfo) => {
54
+ for (const dependentUrl of currentUrlInfo.dependents) {
55
+ if (seen.includes(dependentUrl)) {
56
+ continue
52
57
  }
53
- return visitDependents(dependent)
58
+ seen.push(dependentUrl)
59
+ const dependentUrlInfo = getUrlInfo(dependentUrl)
60
+ visitor(dependentUrlInfo, stop)
61
+ if (stopped) {
62
+ return dependentUrlInfo
63
+ }
64
+ iterate(dependentUrlInfo)
54
65
  }
55
66
  return null
56
67
  }
57
- return visitDependents(urlInfo)
68
+ return iterate(urlInfo)
58
69
  }
59
70
 
60
71
  const updateReferences = (urlInfo, references) => {
61
72
  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
-
73
+ const setOfImplicitUrls = new Set()
72
74
  references.forEach((reference) => {
73
75
  if (reference.isResourceHint) {
74
76
  // resource hint are a special kind of reference.
@@ -79,67 +81,83 @@ export const createUrlGraph = ({
79
81
  // by <link> as dependency and it's fine
80
82
  return
81
83
  }
82
- setOfDependencyUrls.add(reference.url)
84
+ const dependencyUrl = reference.url
85
+ setOfDependencyUrls.add(dependencyUrl)
86
+ // an implicit reference do not appear in the file but a non-explicited file have an impact on it
87
+ // (package.json on import resolution for instance)
88
+ // in that case:
89
+ // - file depends on the implicit file (it must autoreload if package.json is modified)
90
+ // - cache validity for the file depends on the implicit file (it must be re-cooked in package.json is modified)
91
+ if (reference.isImplicit) {
92
+ setOfImplicitUrls.add(dependencyUrl)
93
+ }
83
94
  })
84
- const urlsToRemove = Array.from(urlInfo.dependencies).filter(
85
- (dep) => !setOfDependencyUrls.has(dep),
86
- )
87
- pruneDependencies(urlInfo, urlsToRemove)
88
- urlInfo.references = references
89
95
  setOfDependencyUrls.forEach((dependencyUrl) => {
90
- const dependencyUrlInfo = reuseOrCreateUrlInfo(dependencyUrl)
91
96
  urlInfo.dependencies.add(dependencyUrl)
97
+ const dependencyUrlInfo = reuseOrCreateUrlInfo(dependencyUrl)
92
98
  dependencyUrlInfo.dependents.add(urlInfo.url)
93
99
  })
94
- return urlInfo
95
- }
96
- const pruneDependencies = (firstUrlInfo, urlsToRemove) => {
100
+ setOfImplicitUrls.forEach((implicitUrl) => {
101
+ urlInfo.implicitUrls.add(implicitUrl)
102
+ if (urlInfo.isInline) {
103
+ const parentUrlInfo = getUrlInfo(urlInfo.inlineUrlSite.url)
104
+ parentUrlInfo.implicitUrls.add(implicitUrl)
105
+ }
106
+ })
97
107
  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
108
+ const pruneDependency = (urlInfo, urlToClean) => {
109
+ urlInfo.dependencies.delete(urlToClean)
110
+ const dependencyUrlInfo = getUrlInfo(urlToClean)
111
+ if (!dependencyUrlInfo) {
112
+ return
113
+ }
114
+ dependencyUrlInfo.dependents.delete(urlInfo.url)
115
+ if (dependencyUrlInfo.dependents.size === 0) {
116
+ dependencyUrlInfo.dependencies.forEach((dependencyUrl) => {
117
+ pruneDependency(dependencyUrlInfo, dependencyUrl)
118
+ })
119
+ prunedUrlInfos.push(dependencyUrlInfo)
120
+ }
118
121
  }
119
- prunedUrlInfos.forEach((prunedUrlInfo) => {
120
- prunedUrlInfo.modifiedTimestamp = Date.now()
121
- if (prunedUrlInfo.isInline) {
122
- // should we always delete?
123
- deleteUrlInfo(prunedUrlInfo.url)
122
+ urlInfo.dependencies.forEach((dependencyUrl) => {
123
+ if (!setOfDependencyUrls.has(dependencyUrl)) {
124
+ pruneDependency(urlInfo, dependencyUrl)
124
125
  }
125
126
  })
126
- if (clientFilesPruneCallbackList) {
127
- clientFilesPruneCallbackList.forEach((callback) => {
128
- callback({
129
- firstUrlInfo,
130
- prunedUrlInfos,
131
- })
127
+ if (prunedUrlInfos.length) {
128
+ prunedUrlInfos.forEach((prunedUrlInfo) => {
129
+ prunedUrlInfo.modifiedTimestamp = Date.now()
130
+ if (prunedUrlInfo.isInline) {
131
+ // should we always delete?
132
+ deleteUrlInfo(prunedUrlInfo.url)
133
+ }
132
134
  })
135
+ prunedUrlInfosCallbackRef.current(prunedUrlInfos, urlInfo)
133
136
  }
134
- }
135
-
136
- if (clientFileChangeCallbackList) {
137
- clientFileChangeCallbackList.push(({ url }) => {
138
- const urlInfo = getUrlInfo(url)
139
- if (urlInfo) {
140
- considerModified(urlInfo, Date.now())
137
+ urlInfo.implicitUrls.forEach((implicitUrl) => {
138
+ if (!setOfDependencyUrls.has(implicitUrl)) {
139
+ let implicitUrlComesFromInlineContent = false
140
+ for (const dependencyUrl of urlInfo.dependencies) {
141
+ const dependencyUrlInfo = getUrlInfo(dependencyUrl)
142
+ if (
143
+ dependencyUrlInfo.isInline &&
144
+ dependencyUrlInfo.implicitUrls.has(implicitUrl)
145
+ ) {
146
+ implicitUrlComesFromInlineContent = true
147
+ break
148
+ }
149
+ }
150
+ if (!implicitUrlComesFromInlineContent) {
151
+ urlInfo.implicitUrls.delete(implicitUrl)
152
+ }
153
+ if (urlInfo.isInline) {
154
+ const parentUrlInfo = getUrlInfo(urlInfo.inlineUrlSite.url)
155
+ parentUrlInfo.implicitUrls.delete(implicitUrl)
156
+ }
141
157
  }
142
158
  })
159
+ urlInfo.references = references
160
+ return urlInfo
143
161
  }
144
162
 
145
163
  const considerModified = (urlInfo, modifiedTimestamp = Date.now()) => {
@@ -150,6 +168,7 @@ export const createUrlGraph = ({
150
168
  }
151
169
  seen.push(urlInfo.url)
152
170
  urlInfo.modifiedTimestamp = modifiedTimestamp
171
+ urlInfo.originalContentEtag = undefined
153
172
  urlInfo.contentEtag = undefined
154
173
  urlInfo.dependents.forEach((dependentUrl) => {
155
174
  const dependentUrlInfo = getUrlInfo(dependentUrl)
@@ -168,30 +187,20 @@ export const createUrlGraph = ({
168
187
  iterate(urlInfo)
169
188
  }
170
189
 
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
190
  return {
191
+ createUrlInfoCallbackRef,
192
+ prunedUrlInfosCallbackRef,
193
+
186
194
  urlInfoMap,
187
195
  reuseOrCreateUrlInfo,
188
196
  getUrlInfo,
189
197
  deleteUrlInfo,
198
+ getParentIfInline,
199
+
190
200
  inferReference,
191
- findDependent,
192
201
  updateReferences,
193
202
  considerModified,
194
- getRelatedUrlInfos,
203
+ visitDependents,
195
204
 
196
205
  toObject: () => {
197
206
  const data = {}
@@ -217,26 +226,25 @@ export const createUrlGraph = ({
217
226
  }
218
227
 
219
228
  const createUrlInfo = (url) => {
220
- return {
229
+ const urlInfo = {
221
230
  error: null,
222
231
  modifiedTimestamp: 0,
232
+ originalContentEtag: null,
223
233
  contentEtag: null,
224
- dependsOnPackageJson: false,
225
- isValid,
234
+ isWatched: false,
235
+ isValid: () => false,
226
236
  data: {}, // plugins can put whatever they want here
227
237
  references: [],
228
238
  dependencies: new Set(),
229
239
  dependents: new Set(),
240
+ implicitUrls: new Set(),
230
241
  type: undefined, // "html", "css", "js_classic", "js_module", "importmap", "json", "webmanifest", ...
231
242
  subtype: undefined, // "worker", "service_worker", "shared_worker" for js, otherwise undefined
232
243
  contentType: "", // "text/html", "text/css", "text/javascript", "application/json", ...
233
244
  url,
234
245
  originalUrl: undefined,
235
- generatedUrl: null,
236
246
  filename: "",
237
247
  isEntryPoint: false,
238
- isInline: false,
239
- inlineUrlSite: null,
240
248
  shouldHandle: undefined,
241
249
  originalContent: undefined,
242
250
  content: undefined,
@@ -244,9 +252,18 @@ const createUrlInfo = (url) => {
244
252
  sourcemap: null,
245
253
  sourcemapReference: null,
246
254
  sourcemapIsWrong: false,
255
+
256
+ generatedUrl: null,
257
+ sourcemapGeneratedUrl: null,
258
+ injected: false,
259
+
260
+ isInline: false,
261
+ inlineUrlSite: null,
262
+ jsQuote: null, // maybe move to inlineUrlSite?
263
+
247
264
  timing: {},
248
265
  headers: {},
249
266
  }
267
+ // Object.preventExtensions(urlInfo) // useful to ensure all properties are declared here
268
+ return urlInfo
250
269
  }
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,