es-module-shims 1.3.6 → 1.4.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ES Module Shims 1.4.0 (2021/12/14)
2
+ * Feature: "enforceIntegrity" option and "modulepreload-shim" support for secure shim mode with integrity (https://github.com/guybedford/es-module-shims/pull/236)
3
+ * Fix: Safari parse error stacks (https://github.com/guybedford/es-module-shims/pull/238)
4
+ * Note: Polyfill mode console note when expected native errors are present (https://github.com/guybedford/es-module-shims/pull/237)
5
+
1
6
  ES Module Shims 1.3.6 (2021/12/07)
2
7
  * Fix resolve hook via esms options (#233, @ffortier)
3
8
 
package/README.md CHANGED
@@ -1,21 +1,20 @@
1
1
  # ES Module Shims
2
2
 
3
- [Release Notes](CHANGELOG.md)
3
+ Shims modern ES Modules features like import maps on top of the baseline modules support in browsers supported by [95% of users](https://caniuse.com/#feat=es6-module).
4
4
 
5
- [93% of users](https://caniuse.com/#feat=es6-module) are now running browsers with baseline support for ES modules. At the same time Chromium ships modern native module features and import maps support to [67% of users](https://caniuse.com/import-maps).
5
+ When running in polyfill mode, [the 67% of users](https://caniuse.com/import-maps) with import maps entirely bypass the shim code entirely.
6
6
 
7
- _It turns out that we can actually polyfill import maps and other new modules features on top of these baseline implementations in a performant 12KB shim._
7
+ In for the remaining 30% of users, the highly performant (see [benchmarks](#benchmarks)) production and [CSP-compatible](#csp-support) shim kicks in to rewrite module specifiers driven by the [Web Assembly ES Module Lexer](https://github.com/guybedford/es-module-lexer).
8
8
 
9
- This includes support for:
9
+ The following modules features are polyfilled:
10
10
 
11
11
  * [Import Maps](#import-maps) support.
12
12
  * Dynamic `import()` shimming when necessary in eg older Firefox versions.
13
13
  * `import.meta` and `import.meta.url`.
14
- * [JSON](#json-modules) and [CSS modules](#css-modules) with import assertions.
14
+ * [JSON](#json-modules) and [CSS modules](#css-modules) with import assertions (when enabled).
15
15
  * [`<link rel="modulepreload">` polyfill](#modulepreload) in non Chromium browsers for both shimmed and unshimmed preloading scenarios.
16
- * Comprehensive [CSP support](#csp-support) using nonces, no `unsafe-eval` or `blob:` policy being necessary.
17
16
 
18
- In addition custom [resolve](#resolve-hook) and [fetch](#fetch-hook) hooks can be implemented allowing for streaming in-browser transform workflows to support custom module types.
17
+ When running in shim mode, module rewriting is applied for all users and custom [resolve](#resolve-hook) and [fetch](#fetch-hook) hooks can be implemented allowing for custom resolution and streaming in-browser transform workflows.
19
18
 
20
19
  Because we are still using the native module loader the edge cases work out comprehensively, including:
21
20
 
@@ -23,54 +22,34 @@ Because we are still using the native module loader the edge cases work out comp
23
22
  * Dynamic import expressions (`import('src/' + varname')`)
24
23
  * Circular references, with the execption that live bindings are disabled for the first unexecuted circular parent.
25
24
 
26
- Due to the use of a tiny [JS tokenizer for ES module syntax only](https://github.com/guybedford/es-module-lexer), with very simple rewriting rules, transformation is very fast.
27
-
28
25
  ## Usage
29
26
 
30
- Include ES Module Shims with a `async` attribute on the script:
31
-
32
- For example, from CDN:
27
+ Include ES Module Shims with a `async` attribute on the script, then include an import map and module scripts normally:
33
28
 
34
29
  ```html
35
- <!-- UNPKG -->
36
- <script async src="https://unpkg.com/es-module-shims@1.3.0/dist/es-module-shims.js"></script>
37
-
38
- <!-- JSPM.IO -->
39
- <script async src="https://ga.jspm.io/npm:es-module-shims@1.3.0/dist/es-module-shims.js"></script>
40
- ```
41
-
42
- Then there are two ways to use ES Module Shims: Polyfill Mode and [Shim Mode](#shim-mode).
43
-
44
- ### Benchmarks
45
-
46
- ES Module Shims is designed for production performance. A [comprehensive benchmark suite](bench/README.md) tracks multiple loading scenarios for the project.
47
-
48
- Benchmark summary:
49
-
50
- * [ES Module Shims Chrome Passthrough](bench/README.md#chrome-passthrough-performance) (for [70% of users](https://caniuse.com/import-maps)) results in ~5ms extra initialization time over native for ES Module Shims fetching, execution and initialization, and on a slow connection the additional non-blocking bandwidth cost of its 10KB compressed download as expected.
51
- * [ES Module Shims Polyfilling](bench/README.md#native-v-polyfill-performance) (for the remaining [30% of users](https://caniuse.com/import-maps)) is on average 1.4x - 1.5x slower than native module loading, and up to 1.8x slower on slow networks (most likely due to the browser preloader), both for cached and uncached loads, and this result scales linearly up to 10MB and 20k modules loaded executing on the fastest connection in just over 2 seconds in Firefox.
52
- * [Very large import maps](bench/README.md#large-import-maps-performance) (100s of entries) cost only a few extra milliseconds upfront for the additional loading cost.
53
-
54
- ### Polyfill Mode
30
+ <script async src="https://ga.jspm.io/npm:es-module-shims@1.3.6/dist/es-module-shims.js"></script>
55
31
 
56
- Write your HTML modules like you would in the latest Chrome:
57
-
58
- ```html
32
+ <!-- https://generator.jspm.io/#U2NhYGBkDM0rySzJSU1hKEpNTC5xMLTQM9Az0C1K1jMAAKFS5w0gAA -->
59
33
  <script type="importmap">
60
34
  {
61
35
  "imports": {
62
- "app": "./src/app.js"
36
+ "react": "https://ga.jspm.io/npm:react@18.0.0-rc.0/index.js"
37
+ },
38
+ "scopes": {
39
+ "https://ga.jspm.io/npm:react@18.0.0-rc.0/": {
40
+ "object-assign": "https://ga.jspm.io/npm:object-assign@4.1.1/index.js"
41
+ }
63
42
  }
64
43
  }
65
44
  </script>
66
- <script type="module">import 'app'</script>
67
- ```
68
45
 
69
- and ES Module Shims will make it work in [all browsers with any ES Module Support](#browser-support).
70
-
71
- > `<script type="importmap">` should always be placed before any `<script type="module">` as per native support in browsers.
46
+ <script type="module">
47
+ import react from 'react';
48
+ console.log(react);
49
+ </script>
50
+ ```
72
51
 
73
- You will see a console error in browsers without import maps support like:
52
+ In browsers without import maps support, a console error will be given:
74
53
 
75
54
  ```
76
55
  Uncaught TypeError: Failed to resolve module specifier "app". Relative references must start with either "/", "./", or "../".
@@ -81,25 +60,23 @@ This execution failure is a feature - it avoids the polyfill causing double exec
81
60
 
82
61
  This is because the polyfill cannot disable the native loader - instead it will only execute modules that would otherwise fail resolving or parsing to avoid duplicate fetches or executions that would cause performance and reliability issues.
83
62
 
84
- If using CSS modules or JSON modules, since these features are relatively new, they require manually enabling using the initialization option:
63
+ ### Shim Mode
85
64
 
86
- ```html
87
- <script>
88
- window.esmsInitOptions = { polyfillEnable: ['css-modules', 'json-modules'] }
89
- </script>
90
- ```
65
+ Shim mode is an alternative to polyfill mode and doesn't rely on native modules erroring - instead it is triggered by the existence of any `<script type="importmap-shim">` or `<script type="module-shim">`, or when explicitly setting the [`shimMode` init option](#shim-mode-option).
91
66
 
92
- To verify when the polyfill is actively engaging as opposed to relying on the native loader, [a `polyfill` hook](#polyfill-hook) is provided.
67
+ In shim mode, only the above `importmap-shim` and `module-shim` tags will be parsed and executed by ES Module Shims.
93
68
 
94
- See the [Polyfill Mode Details](#polyfill-mode-details) section for more information about how the polyfill works and what options are available.
69
+ Shim mode also provides some additional features that aren't yet natively supported such as supporting multiple import maps, [external import maps](#external-import-maps) with a `"src"` attribute, [dynamically injecting import maps](#dynamic-import-maps), and [reading current import map state](#reading-current-import-map-state), which can be useful in certain applications.
95
70
 
96
- ### Shim Mode
71
+ ## Benchmarks
97
72
 
98
- Shim mode is an alternative to polyfill mode and doesn't rely on native modules erroring - instead it is triggered by the existence of any `<script type="importmap-shim">` or `<script type="module-shim">`, or when explicitly setting the [`shimMode` init option](#shim-mode-option).
73
+ ES Module Shims is designed for production performance. A [comprehensive benchmark suite](bench/README.md) tracks multiple loading scenarios for the project.
99
74
 
100
- In shim mode, normal module scripts and import maps are entirely ignored and only the above shim tags will be parsed and executed by ES Module Shims instead.
75
+ Benchmark summary:
101
76
 
102
- Shim mode also provides some additional features that aren't yet natively supported such as supporting multiple import maps, [external import maps](#external-import-maps) with a `"src"` attribute, [dynamically injecting import maps](#dynamic-import-maps), and [reading current import map state](#reading-current-import-map-state), which can be useful in certain applications.
77
+ * [ES Module Shims Chrome Passthrough](bench/README.md#chrome-passthrough-performance) (for [70% of users](https://caniuse.com/import-maps)) results in ~5ms extra initialization time over native for ES Module Shims fetching, execution and initialization, and on a slow connection the additional non-blocking bandwidth cost of its 10KB compressed download as expected.
78
+ * [ES Module Shims Polyfilling](bench/README.md#native-v-polyfill-performance) (for the remaining [30% of users](https://caniuse.com/import-maps)) is on average 1.4x - 1.5x slower than native module loading, and up to 1.8x slower on slow networks (most likely due to the browser preloader), both for cached and uncached loads, and this result scales linearly up to 10MB and 20k modules loaded executing on the fastest connection in just over 2 seconds in Firefox.
79
+ * [Very large import maps](bench/README.md#large-import-maps-performance) (100s of entries) cost only a few extra milliseconds upfront for the additional loading cost.
103
80
 
104
81
  ## Features
105
82
 
@@ -107,7 +84,7 @@ Shim mode also provides some additional features that aren't yet natively suppor
107
84
 
108
85
  Works in all browsers with [baseline ES module support](https://caniuse.com/#feat=es6-module).
109
86
 
110
- #### Browser Compatibility with ES Module Shims:
87
+ Browser Compatibility **with ES Module Shims**:
111
88
 
112
89
  | ES Modules Features | Chrome (61+) | Firefox (60+) | Safari (10.1+) | Edge (17+) |
113
90
  | ---------------------------------- | ------------------------------------ | ------------------------------------ | ------------------------------------ | ------------------------------------ |
@@ -127,7 +104,7 @@ Works in all browsers with [baseline ES module support](https://caniuse.com/#fea
127
104
  * 3: _CSS module support requires a separate [Constructable Stylesheets polyfill](https://github.com/calebdwilliams/construct-style-sheets#readme)._
128
105
  * 4: _Top-level await support is possible for ES Module Shims to implement, with the feature request tracking in https://github.com/guybedford/es-module-shims/issues/5._
129
106
 
130
- #### Current browser compatibility of modules features without ES module shims:
107
+ Browser compatibility **without ES module shims**:
131
108
 
132
109
  | ES Modules Features | Chrome (61+) | Firefox (60+) | Safari (10.1+) | Edge (17+) |
133
110
  | ---------------------------------- | ------------------------------------ | ------------------------------------ | ------------------------------------ | ------------------------------------ |
@@ -351,7 +328,15 @@ In browsers with variable feature support, sources are analyzed with module spec
351
328
 
352
329
  The current default native baseline for the ES module shims polyfill mode is browsers supporting import maps.
353
330
 
354
- If using more modern features like CSS Modules or JSON Modules, these need to be manually enabled via the [`polyfillEnable` init option](#polyfill-enable-option) to raise the native baseline to only browsers supporting these features.
331
+ If using more modern features like CSS Modules or JSON Modules, these need to be manually enabled via the [`polyfillEnable` init option](#polyfill-enable-option) to raise the native baseline to only browsers supporting these features:
332
+
333
+ ```html
334
+ <script>
335
+ window.esmsInitOptions = { polyfillEnable: ['css-modules', 'json-modules'] }
336
+ </script>
337
+ ```
338
+
339
+ To verify when the polyfill is actively engaging as opposed to relying on the native loader, [a `polyfill` hook](#polyfill-hook) is also provided.
355
340
 
356
341
  #### Polyfill Edge Case: Dynamic Import
357
342
 
@@ -421,6 +406,18 @@ Adding the `"noshim"` attribute to the script tag will also ensure that ES Modul
421
406
 
422
407
  Provide a `esmsInitOptions` on the global scope before `es-module-shims` is loaded to configure various aspects of the module loading process:
423
408
 
409
+ * [shimMode](#shim-mode-option)
410
+ * [polyfillEnable](#polyfill-enable-option)
411
+ * [enforceIntegrity](#enforce-integrity)
412
+ * [nonce](#nonce)
413
+ * [noLoadEventRetriggers](#no-load-event-retriggers)
414
+ * [skip](#skip-processing)
415
+ * [onerror](#error-hook)
416
+ * [onpolyfill](#polyfill-hook)
417
+ * [resolve](#resolve-hook)
418
+ * [fetch](#fetch-hook)
419
+ * [revokeBlobURLs](#revoke-blob-urls)
420
+
424
421
  ```html
425
422
  <script>
426
423
  window.esmsInitOptions = {
@@ -430,10 +427,11 @@ window.esmsInitOptions = {
430
427
  noLoadEventRetriggers: true, // default false
431
428
  skip: /^https:\/\/cdn\.com/, // defaults to null
432
429
  onerror: (e) => { /*...*/ }, // default noop
433
- onpolyfill: () => {},
430
+ onpolyfill: () => {}, // default logs to the console
434
431
  resolve: (id, parentUrl, resolve) => resolve(id, parentUrl), // default is spec resolution
435
432
  fetch: (url, options) => fetch(url, options), // default is native
436
433
  revokeBlobURLs: true, // default false
434
+ enforceIntegrity: true, // default true
437
435
  }
438
436
  </script>
439
437
  <script async src="es-module-shims.js"></script>
@@ -488,6 +486,26 @@ Currently this option supports just `"css-modules"` and `"json-modules"`.
488
486
  </script>
489
487
  ```
490
488
 
489
+ ### Enforce Integrity
490
+
491
+ When enabled, `enforceIntegrity` will ensure that all modules loaded through ES Module Shims must have integrity defined either on a `<link rel="modulepreload" integrity="...">` or on
492
+ a `<link rel="modulepreload-shim" integrity="...">` preload tag in shim mode. Modules without integrity will throw at fetch time.
493
+
494
+ For example in the following, only the listed `app.js` and `dep.js` modules will be able to execute with the provided integrity:
495
+
496
+ ```html
497
+ <script type="esms-options">{ "enforceIntegrity": true }</script>
498
+ <link rel="modulepreload-shim" href="/app.js" integrity="sha384-..." />\
499
+ <link rel="modulepreload-shim" href="/dep.js" integrity="sha384-..." />
500
+ <script type="module-shim">
501
+ import '/app.js';
502
+ </script>
503
+ ```
504
+
505
+ Strong execution guarantees are only possible in shim mode since in polyfill mode it is not possible to stop the native loader from executing code without an integrity.
506
+
507
+ Future versions of this option may provide support for origin-specific allow lists.
508
+
491
509
  ### Nonce
492
510
 
493
511
  The `nonce` option allows setting a CSP nonce to be used with all script injections for full CSP compatibility supported by the [CSP build](#csp-build) of ES Module Shims.
@@ -552,6 +570,8 @@ window.polyfilling = () => console.log('The polyfill is actively applying');
552
570
  </script>
553
571
  ```
554
572
 
573
+ 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.
574
+
555
575
  In the above, running in latest Chromium browsers, nothing will be logged, while running in an older browser that does not support newer features
556
576
  like import maps the console log will be output.
557
577
 
@@ -1,4 +1,4 @@
1
- /* ES Module Shims 1.3.6 */
1
+ /* ES Module Shims 1.4.0 */
2
2
  (function () {
3
3
 
4
4
  const edge = navigator.userAgent.match(/Edge\/\d\d\.\d+$/);
@@ -130,17 +130,17 @@
130
130
  for (let p in packages) {
131
131
  const resolvedLhs = resolveIfNotPlainOrUrl(p, baseUrl) || p;
132
132
  if (outPackages[resolvedLhs]) {
133
- throw new Error(`Dynamic import map rejected: Overrides entry "${resolvedLhs}" from ${outPackages[resolvedLhs]} to ${packages[resolvedLhs]}.`);
133
+ throw Error(`Rejected map override "${resolvedLhs}" from ${outPackages[resolvedLhs]} to ${packages[resolvedLhs]}.`);
134
134
  }
135
135
  let target = packages[p];
136
- if (typeof target !== 'string')
136
+ if (typeof target !== 'string')
137
137
  continue;
138
138
  const mapped = resolveImportMap(parentMap, resolveIfNotPlainOrUrl(target, baseUrl) || target, baseUrl);
139
139
  if (mapped) {
140
140
  outPackages[resolvedLhs] = mapped;
141
141
  continue;
142
142
  }
143
- targetWarning(p, packages[p], 'bare specifier did not resolve');
143
+ console.warn(`Mapping "${p}" -> "${packages[p]}" does not resolve`);
144
144
  }
145
145
  }
146
146
 
@@ -175,17 +175,10 @@
175
175
  if (pkgName) {
176
176
  const pkg = packages[pkgName];
177
177
  if (pkg === null) return;
178
- if (id.length > pkgName.length && pkg[pkg.length - 1] !== '/')
179
- targetWarning(pkgName, pkg, "should have a trailing '/'");
180
- else
181
- return pkg + id.slice(pkgName.length);
178
+ return pkg + id.slice(pkgName.length);
182
179
  }
183
180
  }
184
181
 
185
- function targetWarning (match, target, msg) {
186
- console.warn("Package target " + msg + ", resolving target '" + target + "' for " + match);
187
- }
188
-
189
182
  function resolveImportMap (importMap, resolvedOrPlain, parentUrl) {
190
183
  let scopeUrl = parentUrl && getMatch(parentUrl, importMap.scopes);
191
184
  while (scopeUrl) {
@@ -215,9 +208,9 @@
215
208
  }
216
209
 
217
210
  const onerror = globalHook(esmsInitOptions.onerror || noop);
218
- const onpolyfill = globalHook(esmsInitOptions.onpolyfill || noop);
211
+ const onpolyfill = esmsInitOptions.onpolyfill ? globalHook(esmsInitOptions.onpolyfill) : () => console.info(`OK: "Uncaught TypeError" module failure has been polyfilled`);
219
212
 
220
- const { revokeBlobURLs, noLoadEventRetriggers } = esmsInitOptions;
213
+ const { revokeBlobURLs, noLoadEventRetriggers, enforceIntegrity } = esmsInitOptions;
221
214
 
222
215
  const fetchHook = esmsInitOptions.fetch ? globalHook(esmsInitOptions.fetch) : fetch;
223
216
 
@@ -340,34 +333,36 @@
340
333
  const initPromise = featureDetectionPromise.then(() => {
341
334
  // shim mode is determined on initialization, no late shim mode
342
335
  if (!shimMode) {
343
- let seenScript = false;
344
- for (const script of document.querySelectorAll('script[type="module-shim"],script[type="importmap-shim"],script[type="module"],script[type="importmap"]')) {
345
- if (!seenScript && script.type === 'module')
346
- seenScript = true;
347
- if (script.type.endsWith('-shim')) {
348
- setShimMode();
349
- break;
350
- }
351
- if (seenScript && script.type === 'importmap') {
352
- importMapSrcOrLazy = true;
353
- break;
336
+ if (document.querySelectorAll('script[type=module-shim],script[type=importmap-shim],link[rel=modulepreload-shim]').length) {
337
+ setShimMode();
338
+ }
339
+ else {
340
+ let seenScript = false;
341
+ for (const script of document.querySelectorAll('script[type=module],script[type=importmap]')) {
342
+ if (!seenScript) {
343
+ if (script.type === 'module')
344
+ seenScript = true;
345
+ }
346
+ else if (script.type === 'importmap') {
347
+ importMapSrcOrLazy = true;
348
+ break;
349
+ }
354
350
  }
355
351
  }
356
352
  }
357
353
  baselinePassthrough = supportsDynamicImport && supportsImportMeta && supportsImportMaps && (!jsonModulesEnabled || supportsJsonAssertions) && (!cssModulesEnabled || supportsCssAssertions) && !importMapSrcOrLazy && !false;
358
- if (!baselinePassthrough) onpolyfill();
359
354
  if (shimMode || !baselinePassthrough) {
360
355
  new MutationObserver(mutations => {
361
356
  for (const mutation of mutations) {
362
357
  if (mutation.type !== 'childList') continue;
363
358
  for (const node of mutation.addedNodes) {
364
359
  if (node.tagName === 'SCRIPT') {
365
- if (!shimMode && node.type === 'module' || shimMode && node.type === 'module-shim')
360
+ if (node.type === (shimMode ? 'module-shim' : 'module'))
366
361
  processScript(node);
367
- if (!shimMode && node.type === 'importmap' || shimMode && node.type === 'importmap-shim')
362
+ if (node.type === (shimMode ? 'importmap-shim' : 'importmap'))
368
363
  processImportMap(node);
369
364
  }
370
- else if (node.tagName === 'LINK' && node.rel === 'modulepreload')
365
+ else if (node.tagName === 'LINK' && node.rel === (shimMode ? 'modulepreload-shim' : 'modulepreload'))
371
366
  processPreload(node);
372
367
  }
373
368
  }
@@ -378,8 +373,9 @@
378
373
  }
379
374
  });
380
375
  let importMapPromise = initPromise;
381
-
376
+ let firstPolyfillLoad = true;
382
377
  let acceptingImportMaps = true;
378
+
383
379
  async function topLevelLoad (url, fetchOpts, source, nativelyLoaded, lastStaticLoadPromise) {
384
380
  if (!shimMode)
385
381
  acceptingImportMaps = false;
@@ -403,6 +399,10 @@
403
399
  if (revokeBlobURLs) revokeObjectURLs(Object.keys(seen));
404
400
  return module;
405
401
  }
402
+ if (firstPolyfillLoad && !shimMode && load.n && nativelyLoaded) {
403
+ onpolyfill();
404
+ firstPolyfillLoad = false;
405
+ }
406
406
  const module = await dynamicImport(!shimMode && !load.n && nativelyLoaded ? load.u : load.b, { errUrl: load.u });
407
407
  // if the top-level load is a shell, run its update function
408
408
  if (load.s)
@@ -544,7 +544,6 @@
544
544
  const jsContentType = /^(text|application)\/(x-)?javascript(;|$)/;
545
545
  const jsonContentType = /^(text|application)\/json(;|$)/;
546
546
  const cssContentType = /^(text|application)\/css(;|$)/;
547
- const wasmContentType = /^application\/wasm(;|$)/;
548
547
 
549
548
  const cssUrlRegEx = /url\(\s*(?:(["'])((?:\\.|[^\n\\"'])+)\1|((?:\\.|[^\s,"'()\\])+))\s*\)/g;
550
549
 
@@ -562,6 +561,8 @@
562
561
  }
563
562
 
564
563
  async function doFetch (url, fetchOpts) {
564
+ if (enforceIntegrity && !fetchOpts.integrity)
565
+ throw Error(`No integrity for ${url}`);
565
566
  const poolQueue = pushFetchPool();
566
567
  if (poolQueue) await poolQueue;
567
568
  try {
@@ -571,7 +572,7 @@
571
572
  popFetchPool();
572
573
  }
573
574
  if (!res.ok)
574
- throw new Error(`${res.status} ${res.statusText} ${res.url}`);
575
+ throw Error(`${res.status} ${res.statusText} ${res.url}`);
575
576
  const contentType = res.headers.get('content-type');
576
577
  if (jsContentType.test(contentType))
577
578
  return { r: res.url, s: await res.text(), t: 'js' };
@@ -581,10 +582,8 @@
581
582
  return { r: res.url, s: `var s=new CSSStyleSheet();s.replaceSync(${
582
583
  JSON.stringify((await res.text()).replace(cssUrlRegEx, (_match, quotes, relUrl1, relUrl2) => `url(${quotes}${resolveUrl(relUrl1 || relUrl2, url)}${quotes})`))
583
584
  });export default s;`, t: 'css' };
584
- else if (wasmContentType.test(contentType))
585
- throw new Error('WASM modules not supported');
586
585
  else
587
- throw new Error(`Unknown Content-Type "${contentType}"`);
586
+ throw Error(`Unsupported Content-Type "${contentType}"`);
588
587
  }
589
588
 
590
589
  function getOrCreateLoad (url, fetchOpts, source) {
@@ -624,7 +623,7 @@
624
623
  ({ r: load.r, s: source, t } = await (fetchCache[url] || doFetch(url, fetchOpts)));
625
624
  if (t && !shimMode) {
626
625
  if (t === 'css' && !cssModulesEnabled || t === 'json' && !jsonModulesEnabled)
627
- throw new Error(`${t}-modules require <script type="esms-options">{ "polyfillEnable": ["${t}-modules"] }<${''}/script>`);
626
+ throw Error(`${t}-modules require <script type="esms-options">{ "polyfillEnable": ["${t}-modules"] }<${''}/script>`);
628
627
  if (t === 'css' && !supportsCssAssertions || t === 'json' && !supportsJsonAssertions)
629
628
  load.n = true;
630
629
  }
@@ -663,9 +662,9 @@
663
662
  }
664
663
 
665
664
  function processScriptsAndPreloads () {
666
- for (const script of document.querySelectorAll(shimMode ? 'script[type="module-shim"]' : 'script[type="module"]'))
665
+ for (const script of document.querySelectorAll(shimMode ? 'script[type=module-shim]' : 'script[type=module]'))
667
666
  processScript(script);
668
- for (const link of document.querySelectorAll('link[rel="modulepreload"]'))
667
+ for (const link of document.querySelectorAll(shimMode ? 'link[rel=modulepreload-shim]' : 'link[rel=modulepreload]'))
669
668
  processPreload(link);
670
669
  }
671
670
 
@@ -763,7 +762,8 @@
763
762
  if (isDomContentLoadedScript) domContentLoadedCnt++;
764
763
  const blocks = script.getAttribute('async') === null && isReadyScript;
765
764
  const loadPromise = topLevelLoad(script.src || `${baseUrl}?${id++}`, getFetchOpts(script), !script.src && script.innerHTML, !shimMode, blocks && lastStaticLoadPromise).catch(e => {
766
- setTimeout(() => { throw e });
765
+ // This used to be a setTimeout(() => { throw e }) but this breaks Safari stacks
766
+ console.error(e);
767
767
  onerror(e);
768
768
  });
769
769
  if (blocks)
@@ -1,4 +1,4 @@
1
- /* ES Module Shims Wasm 1.3.6 */
1
+ /* ES Module Shims Wasm 1.4.0 */
2
2
  (function () {
3
3
 
4
4
  const edge = navigator.userAgent.match(/Edge\/\d\d\.\d+$/);
@@ -130,17 +130,17 @@
130
130
  for (let p in packages) {
131
131
  const resolvedLhs = resolveIfNotPlainOrUrl(p, baseUrl) || p;
132
132
  if (outPackages[resolvedLhs]) {
133
- throw new Error(`Dynamic import map rejected: Overrides entry "${resolvedLhs}" from ${outPackages[resolvedLhs]} to ${packages[resolvedLhs]}.`);
133
+ throw Error(`Rejected map override "${resolvedLhs}" from ${outPackages[resolvedLhs]} to ${packages[resolvedLhs]}.`);
134
134
  }
135
135
  let target = packages[p];
136
- if (typeof target !== 'string')
136
+ if (typeof target !== 'string')
137
137
  continue;
138
138
  const mapped = resolveImportMap(parentMap, resolveIfNotPlainOrUrl(target, baseUrl) || target, baseUrl);
139
139
  if (mapped) {
140
140
  outPackages[resolvedLhs] = mapped;
141
141
  continue;
142
142
  }
143
- targetWarning(p, packages[p], 'bare specifier did not resolve');
143
+ console.warn(`Mapping "${p}" -> "${packages[p]}" does not resolve`);
144
144
  }
145
145
  }
146
146
 
@@ -175,17 +175,10 @@
175
175
  if (pkgName) {
176
176
  const pkg = packages[pkgName];
177
177
  if (pkg === null) return;
178
- if (id.length > pkgName.length && pkg[pkg.length - 1] !== '/')
179
- targetWarning(pkgName, pkg, "should have a trailing '/'");
180
- else
181
- return pkg + id.slice(pkgName.length);
178
+ return pkg + id.slice(pkgName.length);
182
179
  }
183
180
  }
184
181
 
185
- function targetWarning (match, target, msg) {
186
- console.warn("Package target " + msg + ", resolving target '" + target + "' for " + match);
187
- }
188
-
189
182
  function resolveImportMap (importMap, resolvedOrPlain, parentUrl) {
190
183
  let scopeUrl = parentUrl && getMatch(parentUrl, importMap.scopes);
191
184
  while (scopeUrl) {
@@ -215,9 +208,9 @@
215
208
  }
216
209
 
217
210
  const onerror = globalHook(esmsInitOptions.onerror || noop);
218
- const onpolyfill = globalHook(esmsInitOptions.onpolyfill || noop);
211
+ const onpolyfill = esmsInitOptions.onpolyfill ? globalHook(esmsInitOptions.onpolyfill) : () => console.info(`OK: "Uncaught TypeError" module failure has been polyfilled`);
219
212
 
220
- const { revokeBlobURLs, noLoadEventRetriggers } = esmsInitOptions;
213
+ const { revokeBlobURLs, noLoadEventRetriggers, enforceIntegrity } = esmsInitOptions;
221
214
 
222
215
  const fetchHook = esmsInitOptions.fetch ? globalHook(esmsInitOptions.fetch) : fetch;
223
216
 
@@ -338,34 +331,36 @@
338
331
  const initPromise = featureDetectionPromise.then(() => {
339
332
  // shim mode is determined on initialization, no late shim mode
340
333
  if (!shimMode) {
341
- let seenScript = false;
342
- for (const script of document.querySelectorAll('script[type="module-shim"],script[type="importmap-shim"],script[type="module"],script[type="importmap"]')) {
343
- if (!seenScript && script.type === 'module')
344
- seenScript = true;
345
- if (script.type.endsWith('-shim')) {
346
- setShimMode();
347
- break;
348
- }
349
- if (seenScript && script.type === 'importmap') {
350
- importMapSrcOrLazy = true;
351
- break;
334
+ if (document.querySelectorAll('script[type=module-shim],script[type=importmap-shim],link[rel=modulepreload-shim]').length) {
335
+ setShimMode();
336
+ }
337
+ else {
338
+ let seenScript = false;
339
+ for (const script of document.querySelectorAll('script[type=module],script[type=importmap]')) {
340
+ if (!seenScript) {
341
+ if (script.type === 'module')
342
+ seenScript = true;
343
+ }
344
+ else if (script.type === 'importmap') {
345
+ importMapSrcOrLazy = true;
346
+ break;
347
+ }
352
348
  }
353
349
  }
354
350
  }
355
351
  baselinePassthrough = supportsDynamicImport && supportsImportMeta && supportsImportMaps && (!jsonModulesEnabled || supportsJsonAssertions) && (!cssModulesEnabled || supportsCssAssertions) && !importMapSrcOrLazy && !false;
356
- if (!baselinePassthrough) onpolyfill();
357
352
  if (shimMode || !baselinePassthrough) {
358
353
  new MutationObserver(mutations => {
359
354
  for (const mutation of mutations) {
360
355
  if (mutation.type !== 'childList') continue;
361
356
  for (const node of mutation.addedNodes) {
362
357
  if (node.tagName === 'SCRIPT') {
363
- if (!shimMode && node.type === 'module' || shimMode && node.type === 'module-shim')
358
+ if (node.type === (shimMode ? 'module-shim' : 'module'))
364
359
  processScript(node);
365
- if (!shimMode && node.type === 'importmap' || shimMode && node.type === 'importmap-shim')
360
+ if (node.type === (shimMode ? 'importmap-shim' : 'importmap'))
366
361
  processImportMap(node);
367
362
  }
368
- else if (node.tagName === 'LINK' && node.rel === 'modulepreload')
363
+ else if (node.tagName === 'LINK' && node.rel === (shimMode ? 'modulepreload-shim' : 'modulepreload'))
369
364
  processPreload(node);
370
365
  }
371
366
  }
@@ -376,8 +371,9 @@
376
371
  }
377
372
  });
378
373
  let importMapPromise = initPromise;
379
-
374
+ let firstPolyfillLoad = true;
380
375
  let acceptingImportMaps = true;
376
+
381
377
  async function topLevelLoad (url, fetchOpts, source, nativelyLoaded, lastStaticLoadPromise) {
382
378
  if (!shimMode)
383
379
  acceptingImportMaps = false;
@@ -401,6 +397,10 @@
401
397
  if (revokeBlobURLs) revokeObjectURLs(Object.keys(seen));
402
398
  return module;
403
399
  }
400
+ if (firstPolyfillLoad && !shimMode && load.n && nativelyLoaded) {
401
+ onpolyfill();
402
+ firstPolyfillLoad = false;
403
+ }
404
404
  const module = await dynamicImport(!shimMode && !load.n && nativelyLoaded ? load.u : load.b, { errUrl: load.u });
405
405
  // if the top-level load is a shell, run its update function
406
406
  if (load.s)
@@ -542,7 +542,6 @@
542
542
  const jsContentType = /^(text|application)\/(x-)?javascript(;|$)/;
543
543
  const jsonContentType = /^(text|application)\/json(;|$)/;
544
544
  const cssContentType = /^(text|application)\/css(;|$)/;
545
- const wasmContentType = /^application\/wasm(;|$)/;
546
545
 
547
546
  const cssUrlRegEx = /url\(\s*(?:(["'])((?:\\.|[^\n\\"'])+)\1|((?:\\.|[^\s,"'()\\])+))\s*\)/g;
548
547
 
@@ -560,6 +559,8 @@
560
559
  }
561
560
 
562
561
  async function doFetch (url, fetchOpts) {
562
+ if (enforceIntegrity && !fetchOpts.integrity)
563
+ throw Error(`No integrity for ${url}`);
563
564
  const poolQueue = pushFetchPool();
564
565
  if (poolQueue) await poolQueue;
565
566
  try {
@@ -569,7 +570,7 @@
569
570
  popFetchPool();
570
571
  }
571
572
  if (!res.ok)
572
- throw new Error(`${res.status} ${res.statusText} ${res.url}`);
573
+ throw Error(`${res.status} ${res.statusText} ${res.url}`);
573
574
  const contentType = res.headers.get('content-type');
574
575
  if (jsContentType.test(contentType))
575
576
  return { r: res.url, s: await res.text(), t: 'js' };
@@ -579,10 +580,8 @@
579
580
  return { r: res.url, s: `var s=new CSSStyleSheet();s.replaceSync(${
580
581
  JSON.stringify((await res.text()).replace(cssUrlRegEx, (_match, quotes, relUrl1, relUrl2) => `url(${quotes}${resolveUrl(relUrl1 || relUrl2, url)}${quotes})`))
581
582
  });export default s;`, t: 'css' };
582
- else if (wasmContentType.test(contentType))
583
- throw new Error('WASM modules not supported');
584
583
  else
585
- throw new Error(`Unknown Content-Type "${contentType}"`);
584
+ throw Error(`Unsupported Content-Type "${contentType}"`);
586
585
  }
587
586
 
588
587
  function getOrCreateLoad (url, fetchOpts, source) {
@@ -622,7 +621,7 @@
622
621
  ({ r: load.r, s: source, t } = await (fetchCache[url] || doFetch(url, fetchOpts)));
623
622
  if (t && !shimMode) {
624
623
  if (t === 'css' && !cssModulesEnabled || t === 'json' && !jsonModulesEnabled)
625
- throw new Error(`${t}-modules require <script type="esms-options">{ "polyfillEnable": ["${t}-modules"] }<${''}/script>`);
624
+ throw Error(`${t}-modules require <script type="esms-options">{ "polyfillEnable": ["${t}-modules"] }<${''}/script>`);
626
625
  if (t === 'css' && !supportsCssAssertions || t === 'json' && !supportsJsonAssertions)
627
626
  load.n = true;
628
627
  }
@@ -661,9 +660,9 @@
661
660
  }
662
661
 
663
662
  function processScriptsAndPreloads () {
664
- for (const script of document.querySelectorAll(shimMode ? 'script[type="module-shim"]' : 'script[type="module"]'))
663
+ for (const script of document.querySelectorAll(shimMode ? 'script[type=module-shim]' : 'script[type=module]'))
665
664
  processScript(script);
666
- for (const link of document.querySelectorAll('link[rel="modulepreload"]'))
665
+ for (const link of document.querySelectorAll(shimMode ? 'link[rel=modulepreload-shim]' : 'link[rel=modulepreload]'))
667
666
  processPreload(link);
668
667
  }
669
668
 
@@ -761,7 +760,8 @@
761
760
  if (isDomContentLoadedScript) domContentLoadedCnt++;
762
761
  const blocks = script.getAttribute('async') === null && isReadyScript;
763
762
  const loadPromise = topLevelLoad(script.src || `${baseUrl}?${id++}`, getFetchOpts(script), !script.src && script.innerHTML, !shimMode, blocks && lastStaticLoadPromise).catch(e => {
764
- setTimeout(() => { throw e });
763
+ // This used to be a setTimeout(() => { throw e }) but this breaks Safari stacks
764
+ console.error(e);
765
765
  onerror(e);
766
766
  });
767
767
  if (blocks)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "es-module-shims",
3
- "version": "1.3.6",
3
+ "version": "1.4.0",
4
4
  "description": "Shims for the latest ES module features",
5
5
  "main": "dist/es-module-shims.js",
6
6
  "exports": {
@@ -11,8 +11,8 @@
11
11
  "build": "rollup -c",
12
12
  "bench": "bash bench/bench.sh",
13
13
  "bench:clear": "rm -rf bench/results",
14
- "footprint": "npm run build ; cat dist/es-module-shims.js | brotli | wc -c",
15
- "footprint:csp": "npm run build ; cat dist/es-module-shims.csp.js | brotli | wc -c",
14
+ "footprint": "npm run build ; cat dist/es-module-shims.js | terser -mc | brotli | wc -c",
15
+ "footprint:wasm": "npm run build ; cat dist/es-module-shims.wasm.js | terser -mc | brotli | wc -c",
16
16
  "prepublishOnly": "npm run build",
17
17
  "test": "npm run test:test-base-href ; npm run test:test-csp ; npm run test:test-dom-order ; npm run test:test-early-module-load ; npm run test:test-polyfill ; npm run test:test-polyfill-wasm ; npm run test:test-preload-case ; npm run test:test-revoke-blob-urls ; npm run test:test-shim",
18
18
  "test:test-base-href": "cross-env TEST_NAME=test-base-href node test/server.mjs",