diff --git a/README.md b/README.md index f0a3403..24f5bc3 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,20 @@ The import-map-injector.js file must execute before any ES module is loaded by t ### Via `` elements. + +```html + + + + + + + + + + +``` ### Via npm @@ -39,7 +52,7 @@ It's better for performance to put the import statement at the top of your bundl ## Usage -import-map-injector combines multiple `` elements into a final `` element, which is injected into the `` element of the web page. The browser spec for import maps requires the `` to be injected before any ES modules are loaded via `` elements into a final `` element, which is injected into the `` element of the web page. The browser spec for import maps requires the `` to be injected before any ES modules are loaded via ` + + + + + +``` + +#### Asynchronous import map installation + +When using external import maps, import-map-injector must wait for the network request(s) loading external import map(s) to complete before it can install the browser-native import map. This means you cannot use ` + + + ``` - -### ` + + + + + + + + + + diff --git a/src/import-map-injector.ts b/src/import-map-injector.ts index 4e38c7e..5e12027 100644 --- a/src/import-map-injector.ts +++ b/src/import-map-injector.ts @@ -5,84 +5,110 @@ interface ImportMap { type SpecifierMap = Record; -const jsonPromises: Promise[] = []; +const importMapJsons: (Promise | ImportMap)[] = []; const errPrefix = "import-map-injector:"; -document - .querySelectorAll("script[type=injector-importmap]") - .forEach((scriptEl) => { - if (scriptEl.src) { - jsonPromises.push( - fetch(scriptEl.src) - .then((r: Response) => { - if (r.ok) { - if ( - r.headers.get("content-type").toLowerCase() !== - "application/importmap+json" - ) { - throw Error( - `${errPrefix} Import map at url '${scriptEl.src}' does not have the required content-type http response header. Must be 'application/importmap+json'`, - ); - } +const injectorImportMaps = document.querySelectorAll( + "script[type=injector-importmap]", +); - return r.json(); - } else { +injectorImportMaps.forEach((scriptEl) => { + if (scriptEl.src) { + importMapJsons.push( + fetch(scriptEl.src) + .then((r: Response) => { + if (r.ok) { + if ( + r.headers.get("content-type").toLowerCase() !== + "application/importmap+json" + ) { throw Error( - `${errPrefix} import map at url '${scriptEl.src}' must respond with a success HTTP status, but responded with HTTP ${r.status} ${r.statusText}`, + `${errPrefix} Import map at url '${scriptEl.src}' does not have the required content-type http response header. Must be 'application/importmap+json'`, ); } - }) - .catch((err) => { - console.error( - `${errPrefix} Error loading import map from URL '${scriptEl.src}'`, + + return r.json(); + } else { + throw Error( + `${errPrefix} import map at url '${scriptEl.src}' must respond with a success HTTP status, but responded with HTTP ${r.status} ${r.statusText}`, ); - throw err; - }), - ); - } else if (scriptEl.textContent.length > 0) { - jsonPromises.push( - Promise.resolve().then(() => JSON.parse(scriptEl.textContent)), - ); - } else { + } + }) + .catch((err) => { + console.error( + `${errPrefix} Error loading import map from URL '${scriptEl.src}'`, + ); + throw err; + }), + ); + } else if (scriptEl.textContent.length > 0) { + let json; + try { + json = JSON.parse(scriptEl.textContent); + } catch (err) { + console.error(err); throw Error( - `${errPrefix} Script with type "injector-importmap" does not contain an importmap`, + `${errPrefix} A