@@ -17,11 +17,13 @@ const {
1717 RegExpPrototypeExec,
1818 SafeArrayIterator,
1919 SafeWeakMap,
20+ StringPrototypeStartsWith,
2021 globalThis,
2122} = primordials ;
2223const { MessageChannel } = require ( 'internal/worker/io' ) ;
2324
2425const {
26+ ERR_INTERNAL_ASSERTION ,
2527 ERR_INVALID_ARG_TYPE ,
2628 ERR_INVALID_ARG_VALUE ,
2729 ERR_INVALID_RETURN_PROPERTY_VALUE ,
@@ -47,6 +49,9 @@ const { defaultLoad } = require('internal/modules/esm/load');
4749const { translators } = require (
4850 'internal/modules/esm/translators' ) ;
4951const { getOptionValue } = require ( 'internal/options' ) ;
52+ const {
53+ fetchModule,
54+ } = require ( 'internal/modules/esm/fetch_module' ) ;
5055
5156/**
5257 * An ESMLoader instance is used as the main entry point for loading ES modules.
@@ -209,7 +214,9 @@ class ESMLoader {
209214 const module = new ModuleWrap ( url , undefined , source , 0 , 0 ) ;
210215 callbackMap . set ( module , {
211216 importModuleDynamically : ( specifier , { url } , importAssertions ) => {
212- return this . import ( specifier , url , importAssertions ) ;
217+ return this . import ( specifier ,
218+ this . getBaseURL ( url ) ,
219+ importAssertions ) ;
213220 }
214221 } ) ;
215222
@@ -225,6 +232,43 @@ class ESMLoader {
225232 } ;
226233 }
227234
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+
228272 /**
229273 * Get a (possibly still pending) module job from the cache,
230274 * or create one and return its Promise.
0 commit comments