@@ -17,11 +17,13 @@ const {
17
17
RegExpPrototypeExec,
18
18
SafeArrayIterator,
19
19
SafeWeakMap,
20
+ StringPrototypeStartsWith,
20
21
globalThis,
21
22
} = primordials ;
22
23
const { MessageChannel } = require ( 'internal/worker/io' ) ;
23
24
24
25
const {
26
+ ERR_INTERNAL_ASSERTION ,
25
27
ERR_INVALID_ARG_TYPE ,
26
28
ERR_INVALID_ARG_VALUE ,
27
29
ERR_INVALID_RETURN_PROPERTY_VALUE ,
@@ -47,6 +49,9 @@ const { defaultLoad } = require('internal/modules/esm/load');
47
49
const { translators } = require (
48
50
'internal/modules/esm/translators' ) ;
49
51
const { getOptionValue } = require ( 'internal/options' ) ;
52
+ const {
53
+ fetchModule,
54
+ } = require ( 'internal/modules/esm/fetch_module' ) ;
50
55
51
56
/**
52
57
* An ESMLoader instance is used as the main entry point for loading ES modules.
@@ -209,7 +214,9 @@ class ESMLoader {
209
214
const module = new ModuleWrap ( url , undefined , source , 0 , 0 ) ;
210
215
callbackMap . set ( module , {
211
216
importModuleDynamically : ( specifier , { url } , importAssertions ) => {
212
- return this . import ( specifier , url , importAssertions ) ;
217
+ return this . import ( specifier ,
218
+ this . getBaseURL ( url ) ,
219
+ importAssertions ) ;
213
220
}
214
221
} ) ;
215
222
@@ -225,6 +232,43 @@ class ESMLoader {
225
232
} ;
226
233
}
227
234
235
+ /**
236
+ * Returns the url to use for the resolution of a given cache key url
237
+ * These are not guaranteed to be the same.
238
+ *
239
+ * In WHATWG HTTP spec for ESM the cache key is the non-I/O bound
240
+ * synchronous resolution using only string operations
241
+ * ~= resolveImportMap(new URL(specifier, importerHREF))
242
+ *
243
+ * The url used for subsequent resolution is the response URL after
244
+ * all redirects have been resolved.
245
+ *
246
+ * https://example.com/foo redirecting to https://example.com/bar
247
+ * would have a cache key of https://example.com/foo and baseURL
248
+ * of https://example.com/bar
249
+ *
250
+ * MUST BE SYNCHRONOUS for import.meta initialization
251
+ * MUST BE CALLED AFTER receiving the url body due to I/O
252
+ * @param {string } url
253
+ * @returns {string }
254
+ */
255
+ getBaseURL ( url ) {
256
+ if (
257
+ StringPrototypeStartsWith ( url , 'http:' ) ||
258
+ StringPrototypeStartsWith ( url , 'https:' )
259
+ ) {
260
+ // The request & response have already settled, so they are in
261
+ // fetchModule's cache, in which case, fetchModule returns
262
+ // immediately and synchronously
263
+ url = fetchModule ( new URL ( url ) , { parentURL : url } ) . resolvedHREF ;
264
+ // This should only occur if the module hasn't been fetched yet
265
+ if ( typeof url !== 'string' ) {
266
+ throw new ERR_INTERNAL_ASSERTION ( `Base url for module ${ url } not loaded.` ) ;
267
+ }
268
+ }
269
+ return url ;
270
+ }
271
+
228
272
/**
229
273
* Get a (possibly still pending) module job from the cache,
230
274
* or create one and return its Promise.
0 commit comments