es-module-shims 1.5.4 → 1.5.7

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.
package/README.md CHANGED
@@ -29,7 +29,7 @@ Because we are still using the native module loader the edge cases work out comp
29
29
  Include ES Module Shims with a `async` attribute on the script, then include an import map and module scripts normally:
30
30
 
31
31
  ```html
32
- <script async src="https://ga.jspm.io/npm:es-module-shims@1.5.4/dist/es-module-shims.js"></script>
32
+ <script async src="https://ga.jspm.io/npm:es-module-shims@1.5.7/dist/es-module-shims.js"></script>
33
33
 
34
34
  <!-- https://generator.jspm.io/#U2NhYGBkDM0rySzJSU1hKEpNTC5xMLTQM9Az0C1K1jMAAKFS5w0gAA -->
35
35
  <script type="importmap">
@@ -292,11 +292,22 @@ While in polyfill mode the same restrictions apply that multiple import maps, im
292
292
  To make it easy to keep track of import map state, es-module-shims provides a `importShim.getImportMap` utility function, available only in shim mode.
293
293
 
294
294
  ```js
295
- const importMap = importShim.getImportMap()
295
+ const importMap = importShim.getImportMap();
296
296
 
297
297
  // importMap will be an object in the same shape as the json in a importmap script
298
298
  ```
299
299
 
300
+ #### Setting current import map state
301
+ To make it easy to set the import map state, es-module-shims provides a `importShim.addImportMap` utility function, available only in shim mode.
302
+
303
+ ```js
304
+ // importMap will be an object in the same shape as the json in a importmap script
305
+ const importMap = { imports: {/*...*/}, scopes: {/*...*/} };
306
+
307
+ importShim.addImportMap(importMap);
308
+ ```
309
+
310
+
300
311
  ### Dynamic Import
301
312
 
302
313
  > Stability: Stable browser standard
@@ -423,6 +434,34 @@ var resolvedUrl = import.meta.resolve('dep', 'https://site.com/another/scope');
423
434
 
424
435
  Node.js also implements a similar API, although it's in the process of shifting to a synchronous resolver.
425
436
 
437
+ ### Module Workers
438
+ ES Module Shims can be used in module workers in browsers that provide dynamic import in worker environments, which at the moment are Chrome(80+), Edge(80+), Safari(15+).
439
+
440
+ An example of ES Module Shims usage in web workers is provided below:
441
+ ```js
442
+ /**
443
+ *
444
+ * @param {string} aURL a string representing the URL of the module script the worker will execute.
445
+ * @returns {string} The string representing the URL of the script the worker will execute.
446
+ */
447
+ function getWorkerScriptURL(aURL) {
448
+ // baseURL, esModuleShimsURL are considered to be known in advance
449
+ // esModuleShimsURL - must point to the non-CSP build of ES Module Shims,
450
+ // namely the `es-module-shim.wasm.js` output: es-module-shims/dist/es-module-shims.wasm.js
451
+
452
+ return URL.createObjectURL(new Blob(
453
+ [
454
+ `importScripts('${new URL(esModuleShimsURL, baseURL).href}');
455
+ importShim.addImportMap(${JSON.stringify(importShim.getImportMap())});
456
+ importShim('${new URL(aURL, baseURL).href}').catch(e => setTimeout(() => { throw e; }))`
457
+ ],
458
+ { type: 'application/javascript' }))
459
+ }
460
+
461
+ const worker = new Worker(getWorkerScriptURL('myEsModule.js'));
462
+ ```
463
+ > For now, in web workers must be used the non-CSP build of ES Module Shims, namely the `es-module-shim.wasm.js` output: es-module-shims/dist/es-module-shims.wasm.js.
464
+
426
465
  ## Init Options
427
466
 
428
467
  Provide a `esmsInitOptions` on the global scope before `es-module-shims` is loaded to configure various aspects of the module loading process:
@@ -675,6 +714,8 @@ window.polyfilling = () => console.log('The polyfill is actively applying');
675
714
 
676
715
  The default hook will log a message to the console with `console.info` noting that polyfill mode is enabled and that the native error can be ignored.
677
716
 
717
+ Overriding this hook with an empty function will disable the default polyfill log output.
718
+
678
719
  In the above, running in latest Chromium browsers, nothing will be logged, while running in an older browser that does not support newer features like import maps the console log will be output.
679
720
 
680
721
  #### Error hook
@@ -1,14 +1,17 @@
1
- /* ES Module Shims 1.5.4 */
1
+ /* ES Module Shims 1.5.7 */
2
2
  (function () {
3
3
 
4
+ const hasWindow = typeof window !== 'undefined';
5
+ const hasDocument = typeof document !== 'undefined';
6
+
4
7
  const noop = () => {};
5
8
 
6
- const optionsScript = document.querySelector('script[type=esms-options]');
9
+ const optionsScript = hasDocument ? document.querySelector('script[type=esms-options]') : undefined;
7
10
 
8
11
  const esmsInitOptions = optionsScript ? JSON.parse(optionsScript.innerHTML) : {};
9
12
  Object.assign(esmsInitOptions, self.esmsInitOptions || {});
10
13
 
11
- let shimMode = !!esmsInitOptions.shimMode;
14
+ let shimMode = hasDocument ? !!esmsInitOptions.shimMode : true;
12
15
 
13
16
  const importHook = globalHook(shimMode && esmsInitOptions.onimport);
14
17
  const resolveHook = globalHook(shimMode && esmsInitOptions.resolve);
@@ -17,18 +20,19 @@
17
20
 
18
21
  const skip = esmsInitOptions.skip ? new RegExp(esmsInitOptions.skip) : null;
19
22
 
20
- let nonce = esmsInitOptions.nonce;
21
-
22
23
  const mapOverrides = esmsInitOptions.mapOverrides;
23
24
 
24
- if (!nonce) {
25
+ let nonce = esmsInitOptions.nonce;
26
+ if (!nonce && hasDocument) {
25
27
  const nonceElement = document.querySelector('script[nonce]');
26
28
  if (nonceElement)
27
29
  nonce = nonceElement.nonce || nonceElement.getAttribute('nonce');
28
30
  }
29
31
 
30
32
  const onerror = globalHook(esmsInitOptions.onerror || noop);
31
- const onpolyfill = esmsInitOptions.onpolyfill ? globalHook(esmsInitOptions.onpolyfill) : () => console.log('%c^^ Module TypeError above is polyfilled and can be ignored ^^', 'font-weight:900;color:#391');
33
+ const onpolyfill = esmsInitOptions.onpolyfill ? globalHook(esmsInitOptions.onpolyfill) : () => {
34
+ console.log('%c^^ Module TypeError above is polyfilled and can be ignored ^^', 'font-weight:900;color:#391');
35
+ };
32
36
 
33
37
  const { revokeBlobURLs, noLoadEventRetriggers, enforceIntegrity } = esmsInitOptions;
34
38
 
@@ -46,7 +50,11 @@
46
50
 
47
51
  const edge = !navigator.userAgentData && !!navigator.userAgent.match(/Edge\/\d+\.\d+/);
48
52
 
49
- const baseUrl = document.baseURI;
53
+ const baseUrl = hasDocument
54
+ ? document.baseURI
55
+ : `${location.protocol}//${location.host}${location.pathname.includes('/')
56
+ ? location.pathname.slice(0, location.pathname.lastIndexOf('/') + 1)
57
+ : location.pathname}`;
50
58
 
51
59
  function createBlob (source, type = 'text/javascript') {
52
60
  return URL.createObjectURL(new Blob([source], { type }));
@@ -54,7 +62,7 @@
54
62
 
55
63
  const eoop = err => setTimeout(() => { throw err });
56
64
 
57
- const throwError = err => { (window.reportError || window.safari && console.error || eoop)(err), void onerror(err); };
65
+ const throwError = err => { (self.reportError || hasWindow && window.safari && console.error || eoop)(err), void onerror(err); };
58
66
 
59
67
  function fromParent (parent) {
60
68
  return parent ? ` imported from ${parent}` : '';
@@ -271,32 +279,43 @@
271
279
  let supportsJsonAssertions = false;
272
280
  let supportsCssAssertions = false;
273
281
 
274
- let supportsImportMeta = false;
275
- let supportsImportMaps = false;
276
-
282
+ let supportsImportMaps = hasDocument && HTMLScriptElement.supports ? HTMLScriptElement.supports('importmap') : false;
283
+ let supportsImportMeta = supportsImportMaps;
277
284
  let supportsDynamicImport = false;
278
285
 
279
- const featureDetectionPromise = Promise.resolve(supportsDynamicImportCheck).then(_supportsDynamicImport => {
286
+ const featureDetectionPromise = Promise.resolve(supportsImportMaps || supportsDynamicImportCheck).then(_supportsDynamicImport => {
280
287
  if (!_supportsDynamicImport)
281
288
  return;
282
289
  supportsDynamicImport = true;
283
290
 
284
291
  return Promise.all([
285
- dynamicImport(createBlob('import.meta')).then(() => supportsImportMeta = true, noop),
286
- cssModulesEnabled && dynamicImport(createBlob('import"data:text/css,{}"assert{type:"css"}')).then(() => supportsCssAssertions = true, noop),
287
- jsonModulesEnabled && dynamicImport(createBlob('import"data:text/json,{}"assert{type:"json"}')).then(() => supportsJsonAssertions = true, noop),
288
- new Promise(resolve => {
289
- self._$s = v => {
290
- document.head.removeChild(iframe);
291
- if (v) supportsImportMaps = true;
292
- delete self._$s;
293
- resolve();
294
- };
292
+ supportsImportMaps || dynamicImport(createBlob('import.meta')).then(() => supportsImportMeta = true, noop),
293
+ cssModulesEnabled && dynamicImport(createBlob(`import"${createBlob('', 'text/css')}"assert{type:"css"}`)).then(() => supportsCssAssertions = true, noop),
294
+ jsonModulesEnabled && dynamicImport(createBlob(`import"${createBlob('{}', 'text/json')}"assert{type:"json"}`)).then(() => supportsJsonAssertions = true, noop),
295
+ supportsImportMaps || hasDocument && (HTMLScriptElement.supports || new Promise(resolve => {
295
296
  const iframe = document.createElement('iframe');
296
297
  iframe.style.display = 'none';
297
- iframe.srcdoc = `<script type=importmap nonce="${nonce}">{"imports":{"x":"data:text/javascript,"}}<${''}/script><script nonce="${nonce}">import('x').then(()=>1,()=>0).then(v=>parent._$s(v))<${''}/script>`;
298
+ iframe.setAttribute('nonce', nonce);
299
+ // setting src to a blob URL results in a navigation event in webviews
300
+ // setting srcdoc is not supported in React native webviews on iOS
301
+ // therefore, we need to first feature detect srcdoc support
302
+ iframe.srcdoc = `<!doctype html><script nonce="${nonce}"><${''}/script>`;
298
303
  document.head.appendChild(iframe);
299
- })
304
+ iframe.contentWindow.addEventListener('DOMContentLoaded', () => {
305
+ self._$s = v => {
306
+ document.head.removeChild(iframe);
307
+ supportsImportMaps = v;
308
+ delete self._$s;
309
+ resolve();
310
+ };
311
+ const supportsSrcDoc = iframe.contentDocument.head.childNodes.length > 0;
312
+ const importMapTest = `<!doctype html><script type=importmap nonce="${nonce}">{"imports":{"x":"${createBlob('')}"}<${''}/script><script nonce="${nonce}">import('x').catch(() => {}).then(v=>parent._$s(!!v))<${''}/script>`;
313
+ if (supportsSrcDoc)
314
+ iframe.srcdoc = importMapTest;
315
+ else
316
+ iframe.contentDocument.write(importMapTest);
317
+ });
318
+ }))
300
319
  ]);
301
320
  });
302
321
 
@@ -333,7 +352,9 @@
333
352
  await initPromise;
334
353
  if (importHook) await importHook(id, typeof args[1] !== 'string' ? args[1] : {}, parentUrl);
335
354
  if (acceptingImportMaps || shimMode || !baselinePassthrough) {
336
- processImportMaps();
355
+ if (hasDocument)
356
+ processImportMaps();
357
+
337
358
  if (!shimMode)
338
359
  acceptingImportMaps = false;
339
360
  }
@@ -363,6 +384,10 @@
363
384
 
364
385
  importShim.resolve = resolveSync;
365
386
  importShim.getImportMap = () => JSON.parse(JSON.stringify(importMap));
387
+ importShim.addImportMap = importMapIn => {
388
+ if (!shimMode) throw new Error('Unsupported in polyfill mode.');
389
+ importMap = resolveAndComposeImportMap(importMapIn, baseUrl, importMap);
390
+ };
366
391
 
367
392
  const registry = importShim._r = {};
368
393
 
@@ -401,26 +426,47 @@
401
426
  }
402
427
  }
403
428
  baselinePassthrough = esmsInitOptions.polyfillEnable !== true && supportsDynamicImport && supportsImportMeta && supportsImportMaps && (!jsonModulesEnabled || supportsJsonAssertions) && (!cssModulesEnabled || supportsCssAssertions) && !importMapSrcOrLazy && !false;
404
- if (shimMode || !baselinePassthrough) {
405
- new MutationObserver(mutations => {
406
- for (const mutation of mutations) {
407
- if (mutation.type !== 'childList') continue;
408
- for (const node of mutation.addedNodes) {
409
- if (node.tagName === 'SCRIPT') {
410
- if (node.type === (shimMode ? 'module-shim' : 'module'))
411
- processScript(node);
412
- if (node.type === (shimMode ? 'importmap-shim' : 'importmap'))
413
- processImportMap(node);
429
+ if (hasDocument) {
430
+ if (!supportsImportMaps) {
431
+ const supports = HTMLScriptElement.supports || (type => type === 'classic' || type === 'module');
432
+ HTMLScriptElement.supports = type => type === 'importmap' || supports(type);
433
+ }
434
+
435
+ if (shimMode || !baselinePassthrough) {
436
+ new MutationObserver(mutations => {
437
+ for (const mutation of mutations) {
438
+ if (mutation.type !== 'childList') continue;
439
+ for (const node of mutation.addedNodes) {
440
+ if (node.tagName === 'SCRIPT') {
441
+ if (node.type === (shimMode ? 'module-shim' : 'module'))
442
+ processScript(node);
443
+ if (node.type === (shimMode ? 'importmap-shim' : 'importmap'))
444
+ processImportMap(node);
445
+ }
446
+ else if (node.tagName === 'LINK' && node.rel === (shimMode ? 'modulepreload-shim' : 'modulepreload'))
447
+ processPreload(node);
448
+ }
449
+ }
450
+ }).observe(document, {childList: true, subtree: true});
451
+ processImportMaps();
452
+ processScriptsAndPreloads();
453
+ if (document.readyState === 'complete') {
454
+ readyStateCompleteCheck();
455
+ }
456
+ else {
457
+ async function readyListener() {
458
+ await initPromise;
459
+ processImportMaps();
460
+ if (document.readyState === 'complete') {
461
+ readyStateCompleteCheck();
462
+ document.removeEventListener('readystatechange', readyListener);
414
463
  }
415
- else if (node.tagName === 'LINK' && node.rel === (shimMode ? 'modulepreload-shim' : 'modulepreload'))
416
- processPreload(node);
417
464
  }
465
+ document.addEventListener('readystatechange', readyListener);
418
466
  }
419
- }).observe(document, { childList: true, subtree: true });
420
- processImportMaps();
421
- processScriptsAndPreloads();
422
- return undefined;
467
+ }
423
468
  }
469
+ return undefined;
424
470
  });
425
471
  let importMapPromise = initPromise;
426
472
  let firstPolyfillLoad = true;
@@ -500,7 +546,7 @@
500
546
  const source = load.S;
501
547
 
502
548
  // edge doesnt execute sibling in order, so we fix this up by ensuring all previous executions are explicit dependencies
503
- let resolvedSource = edge && lastLoad ? `import '${lastLoad}';` : '';
549
+ let resolvedSource = edge && lastLoad ? `import '${lastLoad}';` : '';
504
550
 
505
551
  if (!imports.length) {
506
552
  resolvedSource += source;
@@ -541,7 +587,7 @@
541
587
  resolvedSource += `/*${source.slice(start - 1, statementEnd)}*/${urlJsString(blobUrl)}`;
542
588
 
543
589
  // circular shell execution
544
- if (!cycleShell && depLoad.s) {
590
+ if (!cycleShell && depLoad.s) {
545
591
  resolvedSource += `;import*as m$_${depIndex} from'${depLoad.b}';import{u$_ as u$_${depIndex}}from'${depLoad.s}';u$_${depIndex}(m$_${depIndex})`;
546
592
  depLoad.s = undefined;
547
593
  }
@@ -628,8 +674,8 @@
628
674
  return { r: res.url, s: `export default ${await res.text()}`, t: 'json' };
629
675
  else if (cssContentType.test(contentType)) {
630
676
  return { r: res.url, s: `var s=new CSSStyleSheet();s.replaceSync(${
631
- JSON.stringify((await res.text()).replace(cssUrlRegEx, (_match, quotes = '', relUrl1, relUrl2) => `url(${quotes}${resolveUrl(relUrl1 || relUrl2, url)}${quotes})`))
632
- });export default s;`, t: 'css' };
677
+ JSON.stringify((await res.text()).replace(cssUrlRegEx, (_match, quotes = '', relUrl1, relUrl2) => `url(${quotes}${resolveUrl(relUrl1 || relUrl2, url)}${quotes})`))
678
+ });export default s;`, t: 'css' };
633
679
  }
634
680
  else
635
681
  throw Error(`Unsupported Content-Type "${contentType}" loading ${url}${fromParent(parent)}. Modules must be served with a valid MIME type like application/javascript.`);
@@ -699,7 +745,7 @@
699
745
  load.L = load.f.then(async () => {
700
746
  let childFetchOpts = fetchOpts;
701
747
  load.d = (await Promise.all(load.a[0].map(async ({ n, d }) => {
702
- if (d >= 0 && !supportsDynamicImport || d === 2 && !supportsImportMeta)
748
+ if (d >= 0 && !supportsDynamicImport || d === -2 && !supportsImportMeta)
703
749
  load.n = true;
704
750
  if (d !== -1 || !n) return;
705
751
  const { r, b } = await resolve(n, load.r || load.u);
@@ -750,26 +796,18 @@
750
796
  document.dispatchEvent(new Event('DOMContentLoaded'));
751
797
  }
752
798
  // this should always trigger because we assume es-module-shims is itself a domcontentloaded requirement
753
- document.addEventListener('DOMContentLoaded', async () => {
754
- await initPromise;
755
- domContentLoadedCheck();
756
- if (shimMode || !baselinePassthrough) {
757
- processImportMaps();
758
- processScriptsAndPreloads();
759
- }
760
- });
761
-
762
- let readyStateCompleteCnt = 1;
763
- if (document.readyState === 'complete') {
764
- readyStateCompleteCheck();
765
- }
766
- else {
767
- document.addEventListener('readystatechange', async () => {
768
- processImportMaps();
799
+ if (hasDocument) {
800
+ document.addEventListener('DOMContentLoaded', async () => {
769
801
  await initPromise;
770
- readyStateCompleteCheck();
802
+ domContentLoadedCheck();
803
+ if (shimMode || !baselinePassthrough) {
804
+ processImportMaps();
805
+ processScriptsAndPreloads();
806
+ }
771
807
  });
772
808
  }
809
+
810
+ let readyStateCompleteCnt = 1;
773
811
  function readyStateCompleteCheck () {
774
812
  if (--readyStateCompleteCnt === 0 && !noLoadEventRetriggers)
775
813
  document.dispatchEvent(new Event('readystatechange'));
@@ -809,14 +847,13 @@
809
847
  return;
810
848
  script.ep = true;
811
849
  // does this load block readystate complete
812
- const isReadyScript = readyStateCompleteCnt > 0;
850
+ const isBlockingReadyScript = script.getAttribute('async') === null && readyStateCompleteCnt > 0;
813
851
  // does this load block DOMContentLoaded
814
852
  const isDomContentLoadedScript = domContentLoadedCnt > 0;
815
- if (isReadyScript) readyStateCompleteCnt++;
853
+ if (isBlockingReadyScript) readyStateCompleteCnt++;
816
854
  if (isDomContentLoadedScript) domContentLoadedCnt++;
817
- const blocks = script.getAttribute('async') === null && isReadyScript;
818
- const loadPromise = topLevelLoad(script.src || baseUrl, getFetchOpts(script), !script.src && script.innerHTML, !shimMode, blocks && lastStaticLoadPromise).catch(throwError);
819
- if (blocks)
855
+ const loadPromise = topLevelLoad(script.src || baseUrl, getFetchOpts(script), !script.src && script.innerHTML, !shimMode, isBlockingReadyScript && lastStaticLoadPromise).catch(throwError);
856
+ if (isBlockingReadyScript)
820
857
  lastStaticLoadPromise = loadPromise.then(readyStateCompleteCheck);
821
858
  if (isDomContentLoadedScript)
822
859
  loadPromise.then(domContentLoadedCheck);
@@ -1,14 +1,17 @@
1
- /* ES Module Shims Wasm 1.5.4 */
1
+ /* ES Module Shims Wasm 1.5.7 */
2
2
  (function () {
3
3
 
4
+ const hasWindow = typeof window !== 'undefined';
5
+ const hasDocument = typeof document !== 'undefined';
6
+
4
7
  const noop = () => {};
5
8
 
6
- const optionsScript = document.querySelector('script[type=esms-options]');
9
+ const optionsScript = hasDocument ? document.querySelector('script[type=esms-options]') : undefined;
7
10
 
8
11
  const esmsInitOptions = optionsScript ? JSON.parse(optionsScript.innerHTML) : {};
9
12
  Object.assign(esmsInitOptions, self.esmsInitOptions || {});
10
13
 
11
- let shimMode = !!esmsInitOptions.shimMode;
14
+ let shimMode = hasDocument ? !!esmsInitOptions.shimMode : true;
12
15
 
13
16
  const importHook = globalHook(shimMode && esmsInitOptions.onimport);
14
17
  const resolveHook = globalHook(shimMode && esmsInitOptions.resolve);
@@ -17,18 +20,19 @@
17
20
 
18
21
  const skip = esmsInitOptions.skip ? new RegExp(esmsInitOptions.skip) : null;
19
22
 
20
- let nonce = esmsInitOptions.nonce;
21
-
22
23
  const mapOverrides = esmsInitOptions.mapOverrides;
23
24
 
24
- if (!nonce) {
25
+ let nonce = esmsInitOptions.nonce;
26
+ if (!nonce && hasDocument) {
25
27
  const nonceElement = document.querySelector('script[nonce]');
26
28
  if (nonceElement)
27
29
  nonce = nonceElement.nonce || nonceElement.getAttribute('nonce');
28
30
  }
29
31
 
30
32
  const onerror = globalHook(esmsInitOptions.onerror || noop);
31
- const onpolyfill = esmsInitOptions.onpolyfill ? globalHook(esmsInitOptions.onpolyfill) : () => console.log('%c^^ Module TypeError above is polyfilled and can be ignored ^^', 'font-weight:900;color:#391');
33
+ const onpolyfill = esmsInitOptions.onpolyfill ? globalHook(esmsInitOptions.onpolyfill) : () => {
34
+ console.log('%c^^ Module TypeError above is polyfilled and can be ignored ^^', 'font-weight:900;color:#391');
35
+ };
32
36
 
33
37
  const { revokeBlobURLs, noLoadEventRetriggers, enforceIntegrity } = esmsInitOptions;
34
38
 
@@ -46,7 +50,11 @@
46
50
 
47
51
  const edge = !navigator.userAgentData && !!navigator.userAgent.match(/Edge\/\d+\.\d+/);
48
52
 
49
- const baseUrl = document.baseURI;
53
+ const baseUrl = hasDocument
54
+ ? document.baseURI
55
+ : `${location.protocol}//${location.host}${location.pathname.includes('/')
56
+ ? location.pathname.slice(0, location.pathname.lastIndexOf('/') + 1)
57
+ : location.pathname}`;
50
58
 
51
59
  function createBlob (source, type = 'text/javascript') {
52
60
  return URL.createObjectURL(new Blob([source], { type }));
@@ -54,7 +62,7 @@
54
62
 
55
63
  const eoop = err => setTimeout(() => { throw err });
56
64
 
57
- const throwError = err => { (window.reportError || window.safari && console.error || eoop)(err), void onerror(err); };
65
+ const throwError = err => { (self.reportError || hasWindow && window.safari && console.error || eoop)(err), void onerror(err); };
58
66
 
59
67
  function fromParent (parent) {
60
68
  return parent ? ` imported from ${parent}` : '';
@@ -232,6 +240,15 @@
232
240
 
233
241
  let supportsDynamicImportCheck = false;
234
242
 
243
+ // first check basic eval support
244
+ try {
245
+ eval('');
246
+ }
247
+ catch (e) {
248
+ throw new Error(`The ES Module Shims Wasm build will not work without eval support. Either use the alternative CSP-compatible build or make sure to add both the "unsafe-eval" and "unsafe-wasm-eval" CSP policies.`);
249
+ }
250
+
251
+ // polyfill dynamic import if not supported
235
252
  let dynamicImport;
236
253
  try {
237
254
  dynamicImport = (0, eval)('u=>import(u)');
@@ -239,7 +256,7 @@
239
256
  }
240
257
  catch (e) {}
241
258
 
242
- if (!supportsDynamicImportCheck) {
259
+ if (hasDocument && !supportsDynamicImportCheck) {
243
260
  let err;
244
261
  window.addEventListener('error', _err => err = _err);
245
262
  dynamicImport = (url, { errUrl = url }) => {
@@ -247,7 +264,6 @@
247
264
  const src = createBlob(`import*as m from'${url}';self._esmsi=m;`);
248
265
  const s = Object.assign(document.createElement('script'), { type: 'module', src });
249
266
  s.setAttribute('noshim', '');
250
- s.setAttribute('nonce', nonce);
251
267
  document.head.appendChild(s);
252
268
  return new Promise((resolve, reject) => {
253
269
  s.addEventListener('load', () => {
@@ -269,32 +285,43 @@
269
285
  let supportsJsonAssertions = false;
270
286
  let supportsCssAssertions = false;
271
287
 
272
- let supportsImportMeta = false;
273
- let supportsImportMaps = false;
274
-
288
+ let supportsImportMaps = hasDocument && HTMLScriptElement.supports ? HTMLScriptElement.supports('importmap') : false;
289
+ let supportsImportMeta = supportsImportMaps;
275
290
  let supportsDynamicImport = false;
276
291
 
277
- const featureDetectionPromise = Promise.resolve(supportsDynamicImportCheck).then(_supportsDynamicImport => {
292
+ const featureDetectionPromise = Promise.resolve(supportsImportMaps || supportsDynamicImportCheck).then(_supportsDynamicImport => {
278
293
  if (!_supportsDynamicImport)
279
294
  return;
280
295
  supportsDynamicImport = true;
281
296
 
282
297
  return Promise.all([
283
- dynamicImport(createBlob('import.meta')).then(() => supportsImportMeta = true, noop),
284
- cssModulesEnabled && dynamicImport(createBlob('import"data:text/css,{}"assert{type:"css"}')).then(() => supportsCssAssertions = true, noop),
285
- jsonModulesEnabled && dynamicImport(createBlob('import"data:text/json,{}"assert{type:"json"}')).then(() => supportsJsonAssertions = true, noop),
286
- new Promise(resolve => {
287
- self._$s = v => {
288
- document.head.removeChild(iframe);
289
- if (v) supportsImportMaps = true;
290
- delete self._$s;
291
- resolve();
292
- };
298
+ supportsImportMaps || dynamicImport(createBlob('import.meta')).then(() => supportsImportMeta = true, noop),
299
+ cssModulesEnabled && dynamicImport(createBlob(`import"${createBlob('', 'text/css')}"assert{type:"css"}`)).then(() => supportsCssAssertions = true, noop),
300
+ jsonModulesEnabled && dynamicImport(createBlob(`import"${createBlob('{}', 'text/json')}"assert{type:"json"}`)).then(() => supportsJsonAssertions = true, noop),
301
+ supportsImportMaps || hasDocument && (HTMLScriptElement.supports || new Promise(resolve => {
293
302
  const iframe = document.createElement('iframe');
294
303
  iframe.style.display = 'none';
295
- iframe.srcdoc = `<script type=importmap nonce="${nonce}">{"imports":{"x":"data:text/javascript,"}}<${''}/script><script nonce="${nonce}">import('x').then(()=>1,()=>0).then(v=>parent._$s(v))<${''}/script>`;
304
+ iframe.setAttribute('nonce', nonce);
305
+ // setting src to a blob URL results in a navigation event in webviews
306
+ // setting srcdoc is not supported in React native webviews on iOS
307
+ // therefore, we need to first feature detect srcdoc support
308
+ iframe.srcdoc = `<!doctype html><script nonce="${nonce}"><${''}/script>`;
296
309
  document.head.appendChild(iframe);
297
- })
310
+ iframe.contentWindow.addEventListener('DOMContentLoaded', () => {
311
+ self._$s = v => {
312
+ document.head.removeChild(iframe);
313
+ supportsImportMaps = v;
314
+ delete self._$s;
315
+ resolve();
316
+ };
317
+ const supportsSrcDoc = iframe.contentDocument.head.childNodes.length > 0;
318
+ const importMapTest = `<!doctype html><script type=importmap nonce="${nonce}">{"imports":{"x":"${createBlob('')}"}<${''}/script><script nonce="${nonce}">import('x').catch(() => {}).then(v=>parent._$s(!!v))<${''}/script>`;
319
+ if (supportsSrcDoc)
320
+ iframe.srcdoc = importMapTest;
321
+ else
322
+ iframe.contentDocument.write(importMapTest);
323
+ });
324
+ }))
298
325
  ]);
299
326
  });
300
327
 
@@ -331,7 +358,9 @@
331
358
  await initPromise;
332
359
  if (importHook) await importHook(id, typeof args[1] !== 'string' ? args[1] : {}, parentUrl);
333
360
  if (acceptingImportMaps || shimMode || !baselinePassthrough) {
334
- processImportMaps();
361
+ if (hasDocument)
362
+ processImportMaps();
363
+
335
364
  if (!shimMode)
336
365
  acceptingImportMaps = false;
337
366
  }
@@ -361,6 +390,10 @@
361
390
 
362
391
  importShim.resolve = resolveSync;
363
392
  importShim.getImportMap = () => JSON.parse(JSON.stringify(importMap));
393
+ importShim.addImportMap = importMapIn => {
394
+ if (!shimMode) throw new Error('Unsupported in polyfill mode.');
395
+ importMap = resolveAndComposeImportMap(importMapIn, baseUrl, importMap);
396
+ };
364
397
 
365
398
  const registry = importShim._r = {};
366
399
 
@@ -399,26 +432,47 @@
399
432
  }
400
433
  }
401
434
  baselinePassthrough = esmsInitOptions.polyfillEnable !== true && supportsDynamicImport && supportsImportMeta && supportsImportMaps && (!jsonModulesEnabled || supportsJsonAssertions) && (!cssModulesEnabled || supportsCssAssertions) && !importMapSrcOrLazy && !false;
402
- if (shimMode || !baselinePassthrough) {
403
- new MutationObserver(mutations => {
404
- for (const mutation of mutations) {
405
- if (mutation.type !== 'childList') continue;
406
- for (const node of mutation.addedNodes) {
407
- if (node.tagName === 'SCRIPT') {
408
- if (node.type === (shimMode ? 'module-shim' : 'module'))
409
- processScript(node);
410
- if (node.type === (shimMode ? 'importmap-shim' : 'importmap'))
411
- processImportMap(node);
435
+ if (hasDocument) {
436
+ if (!supportsImportMaps) {
437
+ const supports = HTMLScriptElement.supports || (type => type === 'classic' || type === 'module');
438
+ HTMLScriptElement.supports = type => type === 'importmap' || supports(type);
439
+ }
440
+
441
+ if (shimMode || !baselinePassthrough) {
442
+ new MutationObserver(mutations => {
443
+ for (const mutation of mutations) {
444
+ if (mutation.type !== 'childList') continue;
445
+ for (const node of mutation.addedNodes) {
446
+ if (node.tagName === 'SCRIPT') {
447
+ if (node.type === (shimMode ? 'module-shim' : 'module'))
448
+ processScript(node);
449
+ if (node.type === (shimMode ? 'importmap-shim' : 'importmap'))
450
+ processImportMap(node);
451
+ }
452
+ else if (node.tagName === 'LINK' && node.rel === (shimMode ? 'modulepreload-shim' : 'modulepreload'))
453
+ processPreload(node);
412
454
  }
413
- else if (node.tagName === 'LINK' && node.rel === (shimMode ? 'modulepreload-shim' : 'modulepreload'))
414
- processPreload(node);
415
455
  }
456
+ }).observe(document, {childList: true, subtree: true});
457
+ processImportMaps();
458
+ processScriptsAndPreloads();
459
+ if (document.readyState === 'complete') {
460
+ readyStateCompleteCheck();
416
461
  }
417
- }).observe(document, { childList: true, subtree: true });
418
- processImportMaps();
419
- processScriptsAndPreloads();
420
- return init;
462
+ else {
463
+ async function readyListener() {
464
+ await initPromise;
465
+ processImportMaps();
466
+ if (document.readyState === 'complete') {
467
+ readyStateCompleteCheck();
468
+ document.removeEventListener('readystatechange', readyListener);
469
+ }
470
+ }
471
+ document.addEventListener('readystatechange', readyListener);
472
+ }
473
+ }
421
474
  }
475
+ return init;
422
476
  });
423
477
  let importMapPromise = initPromise;
424
478
  let firstPolyfillLoad = true;
@@ -498,7 +552,7 @@
498
552
  const source = load.S;
499
553
 
500
554
  // edge doesnt execute sibling in order, so we fix this up by ensuring all previous executions are explicit dependencies
501
- let resolvedSource = edge && lastLoad ? `import '${lastLoad}';` : '';
555
+ let resolvedSource = edge && lastLoad ? `import '${lastLoad}';` : '';
502
556
 
503
557
  if (!imports.length) {
504
558
  resolvedSource += source;
@@ -539,7 +593,7 @@
539
593
  resolvedSource += `/*${source.slice(start - 1, statementEnd)}*/${urlJsString(blobUrl)}`;
540
594
 
541
595
  // circular shell execution
542
- if (!cycleShell && depLoad.s) {
596
+ if (!cycleShell && depLoad.s) {
543
597
  resolvedSource += `;import*as m$_${depIndex} from'${depLoad.b}';import{u$_ as u$_${depIndex}}from'${depLoad.s}';u$_${depIndex}(m$_${depIndex})`;
544
598
  depLoad.s = undefined;
545
599
  }
@@ -626,8 +680,8 @@
626
680
  return { r: res.url, s: `export default ${await res.text()}`, t: 'json' };
627
681
  else if (cssContentType.test(contentType)) {
628
682
  return { r: res.url, s: `var s=new CSSStyleSheet();s.replaceSync(${
629
- JSON.stringify((await res.text()).replace(cssUrlRegEx, (_match, quotes = '', relUrl1, relUrl2) => `url(${quotes}${resolveUrl(relUrl1 || relUrl2, url)}${quotes})`))
630
- });export default s;`, t: 'css' };
683
+ JSON.stringify((await res.text()).replace(cssUrlRegEx, (_match, quotes = '', relUrl1, relUrl2) => `url(${quotes}${resolveUrl(relUrl1 || relUrl2, url)}${quotes})`))
684
+ });export default s;`, t: 'css' };
631
685
  }
632
686
  else
633
687
  throw Error(`Unsupported Content-Type "${contentType}" loading ${url}${fromParent(parent)}. Modules must be served with a valid MIME type like application/javascript.`);
@@ -697,7 +751,7 @@
697
751
  load.L = load.f.then(async () => {
698
752
  let childFetchOpts = fetchOpts;
699
753
  load.d = (await Promise.all(load.a[0].map(async ({ n, d }) => {
700
- if (d >= 0 && !supportsDynamicImport || d === 2 && !supportsImportMeta)
754
+ if (d >= 0 && !supportsDynamicImport || d === -2 && !supportsImportMeta)
701
755
  load.n = true;
702
756
  if (d !== -1 || !n) return;
703
757
  const { r, b } = await resolve(n, load.r || load.u);
@@ -748,26 +802,18 @@
748
802
  document.dispatchEvent(new Event('DOMContentLoaded'));
749
803
  }
750
804
  // this should always trigger because we assume es-module-shims is itself a domcontentloaded requirement
751
- document.addEventListener('DOMContentLoaded', async () => {
752
- await initPromise;
753
- domContentLoadedCheck();
754
- if (shimMode || !baselinePassthrough) {
755
- processImportMaps();
756
- processScriptsAndPreloads();
757
- }
758
- });
759
-
760
- let readyStateCompleteCnt = 1;
761
- if (document.readyState === 'complete') {
762
- readyStateCompleteCheck();
763
- }
764
- else {
765
- document.addEventListener('readystatechange', async () => {
766
- processImportMaps();
805
+ if (hasDocument) {
806
+ document.addEventListener('DOMContentLoaded', async () => {
767
807
  await initPromise;
768
- readyStateCompleteCheck();
808
+ domContentLoadedCheck();
809
+ if (shimMode || !baselinePassthrough) {
810
+ processImportMaps();
811
+ processScriptsAndPreloads();
812
+ }
769
813
  });
770
814
  }
815
+
816
+ let readyStateCompleteCnt = 1;
771
817
  function readyStateCompleteCheck () {
772
818
  if (--readyStateCompleteCnt === 0 && !noLoadEventRetriggers)
773
819
  document.dispatchEvent(new Event('readystatechange'));
@@ -807,14 +853,13 @@
807
853
  return;
808
854
  script.ep = true;
809
855
  // does this load block readystate complete
810
- const isReadyScript = readyStateCompleteCnt > 0;
856
+ const isBlockingReadyScript = script.getAttribute('async') === null && readyStateCompleteCnt > 0;
811
857
  // does this load block DOMContentLoaded
812
858
  const isDomContentLoadedScript = domContentLoadedCnt > 0;
813
- if (isReadyScript) readyStateCompleteCnt++;
859
+ if (isBlockingReadyScript) readyStateCompleteCnt++;
814
860
  if (isDomContentLoadedScript) domContentLoadedCnt++;
815
- const blocks = script.getAttribute('async') === null && isReadyScript;
816
- const loadPromise = topLevelLoad(script.src || baseUrl, getFetchOpts(script), !script.src && script.innerHTML, !shimMode, blocks && lastStaticLoadPromise).catch(throwError);
817
- if (blocks)
861
+ const loadPromise = topLevelLoad(script.src || baseUrl, getFetchOpts(script), !script.src && script.innerHTML, !shimMode, isBlockingReadyScript && lastStaticLoadPromise).catch(throwError);
862
+ if (isBlockingReadyScript)
818
863
  lastStaticLoadPromise = loadPromise.then(readyStateCompleteCheck);
819
864
  if (isDomContentLoadedScript)
820
865
  loadPromise.then(domContentLoadedCheck);
package/index.d.ts CHANGED
@@ -18,7 +18,7 @@ interface ESMSInitOptions {
18
18
  /**
19
19
  * Disable retriggering of document readystate
20
20
  */
21
- noLoadEventRetriggers: true,
21
+ noLoadEventRetriggers: true;
22
22
 
23
23
  /**
24
24
  * #### Skip Processing Stability
@@ -45,24 +45,28 @@ interface ESMSInitOptions {
45
45
 
46
46
  /**
47
47
  * #### Error hook
48
- *
48
+ *
49
49
  * Register a callback for any ES Module Shims module errors.
50
- *
50
+ *
51
51
  */
52
52
  onerror: (e: any) => any;
53
53
 
54
54
  /**
55
55
  * #### Resolve Hook
56
- *
56
+ *
57
57
  * Only supported in Shim Mode.
58
- *
58
+ *
59
59
  * Provide a custom resolver function.
60
60
  */
61
- resolve: (id: string, parentUrl: string, resolve: (id: string, parentUrl: string) => string) => string | Promise<string>;
61
+ resolve: (
62
+ id: string,
63
+ parentUrl: string,
64
+ resolve: (id: string, parentUrl: string) => string
65
+ ) => string | Promise<string>;
62
66
 
63
67
  /**
64
68
  * #### Fetch Hook
65
- *
69
+ *
66
70
  * Only supported in Shim Mode.
67
71
  *
68
72
  * > Stability: Non-spec feature
@@ -126,14 +130,19 @@ interface ESMSInitOptions {
126
130
 
127
131
  /**
128
132
  * #### Revoke Blob URLs
129
- *
133
+ *
130
134
  * Set to *true* to cleanup blob URLs from memory after execution.
131
135
  * Can cost some compute time for large loads.
132
- *
136
+ *
133
137
  */
134
138
  revokeBlobURLs: boolean;
135
139
  }
136
140
 
141
+ interface ImportMap {
142
+ imports: Record<string, string>;
143
+ scopes: Record<string, Record<string, string>>;
144
+ }
145
+
137
146
  /**
138
147
  * Dynamic import(...) within any modules loaded will be rewritten as
139
148
  * importShim(...) automatically providing full support for all es-module-shims
@@ -151,6 +160,12 @@ declare function importShim<Default, Exports extends object>(
151
160
  parentUrl?: string
152
161
  ): Promise<{ default: Default } & Exports>;
153
162
 
163
+ declare namespace importShim {
164
+ const resolve: (id: string, parentURL?: string) => string;
165
+ const addImportMap: (importMap: Partial<ImportMap>) => void;
166
+ const getImportMap: () => ImportMap;
167
+ }
168
+
154
169
  interface Window {
155
170
  esmsInitOptions?: ESMSInitOptions;
156
171
  importShim: typeof importShim;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "es-module-shims",
3
- "version": "1.5.4",
3
+ "version": "1.5.7",
4
4
  "description": "Shims for the latest ES module features",
5
5
  "main": "dist/es-module-shims.js",
6
6
  "exports": {