es-module-shims 1.3.4 → 1.4.1

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,17 @@
1
+ ES Module Shims 1.4.1 (2021/12/15)
2
+ * Fix: Firefox parse error stacks (https://github.com/guybedford/es-module-shims/pull/239)
3
+
4
+ ES Module Shims 1.4.0 (2021/12/14)
5
+ * Feature: "enforceIntegrity" option and "modulepreload-shim" support for secure shim mode with integrity (https://github.com/guybedford/es-module-shims/pull/236)
6
+ * Fix: Safari parse error stacks (https://github.com/guybedford/es-module-shims/pull/238)
7
+ * Note: Polyfill mode console note when expected native errors are present (https://github.com/guybedford/es-module-shims/pull/237)
8
+
9
+ ES Module Shims 1.3.6 (2021/12/07)
10
+ * Fix resolve hook via esms options (#233, @ffortier)
11
+
12
+ ES Module Shims 1.3.5 (2021/12/02)
13
+ * Fix trailing comment regex for source mapping normalization with trailling comments (https://github.com/guybedford/es-module-shims/pull/231)
14
+
1
15
  ES Module Shims 1.3.4 (2021/12/02)
2
16
  * Support source mapping normalization with trailing comments (https://github.com/guybedford/es-module-shims/pull/229)
3
17
 
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,7 +1,9 @@
1
- /* ES Module Shims 1.3.4 */
1
+ /* ES Module Shims 1.4.1 */
2
2
  (function () {
3
3
 
4
- const edge = navigator.userAgent.match(/Edge\/\d\d\.\d+$/);
4
+ const uaMatch = navigator.userAgent.match(/(Edge|Safari)\/\d+\.\d+/);
5
+ const edge = uaMatch && uaMatch[1] === 'Edge';
6
+ const safari = uaMatch && uaMatch[1] === 'Safari';
5
7
 
6
8
  let baseUrl;
7
9
 
@@ -130,17 +132,17 @@
130
132
  for (let p in packages) {
131
133
  const resolvedLhs = resolveIfNotPlainOrUrl(p, baseUrl) || p;
132
134
  if (outPackages[resolvedLhs]) {
133
- throw new Error(`Dynamic import map rejected: Overrides entry "${resolvedLhs}" from ${outPackages[resolvedLhs]} to ${packages[resolvedLhs]}.`);
135
+ throw Error(`Rejected map override "${resolvedLhs}" from ${outPackages[resolvedLhs]} to ${packages[resolvedLhs]}.`);
134
136
  }
135
137
  let target = packages[p];
136
- if (typeof target !== 'string')
138
+ if (typeof target !== 'string')
137
139
  continue;
138
140
  const mapped = resolveImportMap(parentMap, resolveIfNotPlainOrUrl(target, baseUrl) || target, baseUrl);
139
141
  if (mapped) {
140
142
  outPackages[resolvedLhs] = mapped;
141
143
  continue;
142
144
  }
143
- targetWarning(p, packages[p], 'bare specifier did not resolve');
145
+ console.warn(`Mapping "${p}" -> "${packages[p]}" does not resolve`);
144
146
  }
145
147
  }
146
148
 
@@ -175,17 +177,10 @@
175
177
  if (pkgName) {
176
178
  const pkg = packages[pkgName];
177
179
  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);
180
+ return pkg + id.slice(pkgName.length);
182
181
  }
183
182
  }
184
183
 
185
- function targetWarning (match, target, msg) {
186
- console.warn("Package target " + msg + ", resolving target '" + target + "' for " + match);
187
- }
188
-
189
184
  function resolveImportMap (importMap, resolvedOrPlain, parentUrl) {
190
185
  let scopeUrl = parentUrl && getMatch(parentUrl, importMap.scopes);
191
186
  while (scopeUrl) {
@@ -199,14 +194,14 @@
199
194
 
200
195
  const optionsScript = document.querySelector('script[type=esms-options]');
201
196
 
202
- const esmsInitOptions$1 = optionsScript ? JSON.parse(optionsScript.innerHTML) : self.esmsInitOptions ? self.esmsInitOptions : {};
197
+ const esmsInitOptions = optionsScript ? JSON.parse(optionsScript.innerHTML) : self.esmsInitOptions ? self.esmsInitOptions : {};
203
198
 
204
- let shimMode = !!esmsInitOptions$1.shimMode;
205
- const resolveHook = globalHook(shimMode && esmsInitOptions$1.resolve);
199
+ let shimMode = !!esmsInitOptions.shimMode;
200
+ const resolveHook = globalHook(shimMode && esmsInitOptions.resolve);
206
201
 
207
- const skip = esmsInitOptions$1.skip ? new RegExp(esmsInitOptions$1.skip) : null;
202
+ const skip = esmsInitOptions.skip ? new RegExp(esmsInitOptions.skip) : null;
208
203
 
209
- let nonce = esmsInitOptions$1.nonce;
204
+ let nonce = esmsInitOptions.nonce;
210
205
 
211
206
  if (!nonce) {
212
207
  const nonceElement = document.querySelector('script[nonce]');
@@ -214,18 +209,18 @@
214
209
  nonce = nonceElement.nonce || nonceElement.getAttribute('nonce');
215
210
  }
216
211
 
217
- const onerror = globalHook(esmsInitOptions$1.onerror || noop);
218
- const onpolyfill = globalHook(esmsInitOptions$1.onpolyfill || noop);
212
+ const onerror = globalHook(esmsInitOptions.onerror || noop);
213
+ const onpolyfill = esmsInitOptions.onpolyfill ? globalHook(esmsInitOptions.onpolyfill) : () => console.info(`OK: "Uncaught TypeError" module failure has been polyfilled`);
219
214
 
220
- const { revokeBlobURLs, noLoadEventRetriggers } = esmsInitOptions$1;
215
+ const { revokeBlobURLs, noLoadEventRetriggers, enforceIntegrity } = esmsInitOptions;
221
216
 
222
- const fetchHook = esmsInitOptions$1.fetch ? globalHook(esmsInitOptions$1.fetch) : fetch;
217
+ const fetchHook = esmsInitOptions.fetch ? globalHook(esmsInitOptions.fetch) : fetch;
223
218
 
224
219
  function globalHook (name) {
225
220
  return typeof name === 'string' ? self[name] : name;
226
221
  }
227
222
 
228
- const enable = Array.isArray(esmsInitOptions$1.polyfillEnable) ? esmsInitOptions$1.polyfillEnable : [];
223
+ const enable = Array.isArray(esmsInitOptions.polyfillEnable) ? esmsInitOptions.polyfillEnable : [];
229
224
  const cssModulesEnabled = enable.includes('css-modules');
230
225
  const jsonModulesEnabled = enable.includes('json-modules');
231
226
 
@@ -318,7 +313,7 @@
318
313
  };
319
314
  }
320
315
 
321
- const resolve = resolveHook ? async (id, parentUrl) => ({ r: await esmsInitOptions.resolve(id, parentUrl, defaultResolve), b: false }) : _resolve;
316
+ const resolve = resolveHook ? async (id, parentUrl) => ({ r: await resolveHook(id, parentUrl, defaultResolve), b: false }) : _resolve;
322
317
 
323
318
  let id = 0;
324
319
  const registry = {};
@@ -340,34 +335,36 @@
340
335
  const initPromise = featureDetectionPromise.then(() => {
341
336
  // shim mode is determined on initialization, no late shim mode
342
337
  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;
338
+ if (document.querySelectorAll('script[type=module-shim],script[type=importmap-shim],link[rel=modulepreload-shim]').length) {
339
+ setShimMode();
340
+ }
341
+ else {
342
+ let seenScript = false;
343
+ for (const script of document.querySelectorAll('script[type=module],script[type=importmap]')) {
344
+ if (!seenScript) {
345
+ if (script.type === 'module')
346
+ seenScript = true;
347
+ }
348
+ else if (script.type === 'importmap') {
349
+ importMapSrcOrLazy = true;
350
+ break;
351
+ }
354
352
  }
355
353
  }
356
354
  }
357
355
  baselinePassthrough = supportsDynamicImport && supportsImportMeta && supportsImportMaps && (!jsonModulesEnabled || supportsJsonAssertions) && (!cssModulesEnabled || supportsCssAssertions) && !importMapSrcOrLazy && !false;
358
- if (!baselinePassthrough) onpolyfill();
359
356
  if (shimMode || !baselinePassthrough) {
360
357
  new MutationObserver(mutations => {
361
358
  for (const mutation of mutations) {
362
359
  if (mutation.type !== 'childList') continue;
363
360
  for (const node of mutation.addedNodes) {
364
361
  if (node.tagName === 'SCRIPT') {
365
- if (!shimMode && node.type === 'module' || shimMode && node.type === 'module-shim')
362
+ if (node.type === (shimMode ? 'module-shim' : 'module'))
366
363
  processScript(node);
367
- if (!shimMode && node.type === 'importmap' || shimMode && node.type === 'importmap-shim')
364
+ if (node.type === (shimMode ? 'importmap-shim' : 'importmap'))
368
365
  processImportMap(node);
369
366
  }
370
- else if (node.tagName === 'LINK' && node.rel === 'modulepreload')
367
+ else if (node.tagName === 'LINK' && node.rel === (shimMode ? 'modulepreload-shim' : 'modulepreload'))
371
368
  processPreload(node);
372
369
  }
373
370
  }
@@ -378,8 +375,9 @@
378
375
  }
379
376
  });
380
377
  let importMapPromise = initPromise;
381
-
378
+ let firstPolyfillLoad = true;
382
379
  let acceptingImportMaps = true;
380
+
383
381
  async function topLevelLoad (url, fetchOpts, source, nativelyLoaded, lastStaticLoadPromise) {
384
382
  if (!shimMode)
385
383
  acceptingImportMaps = false;
@@ -403,6 +401,10 @@
403
401
  if (revokeBlobURLs) revokeObjectURLs(Object.keys(seen));
404
402
  return module;
405
403
  }
404
+ if (firstPolyfillLoad && !shimMode && load.n && nativelyLoaded) {
405
+ onpolyfill();
406
+ firstPolyfillLoad = false;
407
+ }
406
408
  const module = await dynamicImport(!shimMode && !load.n && nativelyLoaded ? load.u : load.b, { errUrl: load.u });
407
409
  // if the top-level load is a shell, run its update function
408
410
  if (load.s)
@@ -539,12 +541,11 @@
539
541
  load.S = undefined;
540
542
  }
541
543
 
542
- const sourceMapURLRegEx = /\n\/\/# source(Mapping)?URL=([^\n]+)\s*((;|\/\/[^#][^\n]+)\s*)*$/;
544
+ const sourceMapURLRegEx = /\n\/\/# source(Mapping)?URL=([^\n]+)\s*((;|\/\/[^#][^\n]*)\s*)*$/;
543
545
 
544
546
  const jsContentType = /^(text|application)\/(x-)?javascript(;|$)/;
545
547
  const jsonContentType = /^(text|application)\/json(;|$)/;
546
548
  const cssContentType = /^(text|application)\/css(;|$)/;
547
- const wasmContentType = /^application\/wasm(;|$)/;
548
549
 
549
550
  const cssUrlRegEx = /url\(\s*(?:(["'])((?:\\.|[^\n\\"'])+)\1|((?:\\.|[^\s,"'()\\])+))\s*\)/g;
550
551
 
@@ -562,6 +563,8 @@
562
563
  }
563
564
 
564
565
  async function doFetch (url, fetchOpts) {
566
+ if (enforceIntegrity && !fetchOpts.integrity)
567
+ throw Error(`No integrity for ${url}`);
565
568
  const poolQueue = pushFetchPool();
566
569
  if (poolQueue) await poolQueue;
567
570
  try {
@@ -571,7 +574,7 @@
571
574
  popFetchPool();
572
575
  }
573
576
  if (!res.ok)
574
- throw new Error(`${res.status} ${res.statusText} ${res.url}`);
577
+ throw Error(`${res.status} ${res.statusText} ${res.url}`);
575
578
  const contentType = res.headers.get('content-type');
576
579
  if (jsContentType.test(contentType))
577
580
  return { r: res.url, s: await res.text(), t: 'js' };
@@ -581,10 +584,8 @@
581
584
  return { r: res.url, s: `var s=new CSSStyleSheet();s.replaceSync(${
582
585
  JSON.stringify((await res.text()).replace(cssUrlRegEx, (_match, quotes, relUrl1, relUrl2) => `url(${quotes}${resolveUrl(relUrl1 || relUrl2, url)}${quotes})`))
583
586
  });export default s;`, t: 'css' };
584
- else if (wasmContentType.test(contentType))
585
- throw new Error('WASM modules not supported');
586
587
  else
587
- throw new Error(`Unknown Content-Type "${contentType}"`);
588
+ throw Error(`Unsupported Content-Type "${contentType}"`);
588
589
  }
589
590
 
590
591
  function getOrCreateLoad (url, fetchOpts, source) {
@@ -624,7 +625,7 @@
624
625
  ({ r: load.r, s: source, t } = await (fetchCache[url] || doFetch(url, fetchOpts)));
625
626
  if (t && !shimMode) {
626
627
  if (t === 'css' && !cssModulesEnabled || t === 'json' && !jsonModulesEnabled)
627
- throw new Error(`${t}-modules require <script type="esms-options">{ "polyfillEnable": ["${t}-modules"] }<${''}/script>`);
628
+ throw Error(`${t}-modules require <script type="esms-options">{ "polyfillEnable": ["${t}-modules"] }<${''}/script>`);
628
629
  if (t === 'css' && !supportsCssAssertions || t === 'json' && !supportsJsonAssertions)
629
630
  load.n = true;
630
631
  }
@@ -663,9 +664,9 @@
663
664
  }
664
665
 
665
666
  function processScriptsAndPreloads () {
666
- for (const script of document.querySelectorAll(shimMode ? 'script[type="module-shim"]' : 'script[type="module"]'))
667
+ for (const script of document.querySelectorAll(shimMode ? 'script[type=module-shim]' : 'script[type=module]'))
667
668
  processScript(script);
668
- for (const link of document.querySelectorAll('link[rel="modulepreload"]'))
669
+ for (const link of document.querySelectorAll(shimMode ? 'link[rel=modulepreload-shim]' : 'link[rel=modulepreload]'))
669
670
  processPreload(link);
670
671
  }
671
672
 
@@ -763,7 +764,12 @@
763
764
  if (isDomContentLoadedScript) domContentLoadedCnt++;
764
765
  const blocks = script.getAttribute('async') === null && isReadyScript;
765
766
  const loadPromise = topLevelLoad(script.src || `${baseUrl}?${id++}`, getFetchOpts(script), !script.src && script.innerHTML, !shimMode, blocks && lastStaticLoadPromise).catch(e => {
766
- setTimeout(() => { throw e });
767
+ // Safari only gives error via console.error
768
+ if (safari)
769
+ console.error(e);
770
+ // Firefox only gives error stack via setTimeout
771
+ else
772
+ setTimeout(() => { throw e});
767
773
  onerror(e);
768
774
  });
769
775
  if (blocks)
@@ -1,7 +1,9 @@
1
- /* ES Module Shims Wasm 1.3.4 */
1
+ /* ES Module Shims Wasm 1.4.1 */
2
2
  (function () {
3
3
 
4
- const edge = navigator.userAgent.match(/Edge\/\d\d\.\d+$/);
4
+ const uaMatch = navigator.userAgent.match(/(Edge|Safari)\/\d+\.\d+/);
5
+ const edge = uaMatch && uaMatch[1] === 'Edge';
6
+ const safari = uaMatch && uaMatch[1] === 'Safari';
5
7
 
6
8
  let baseUrl;
7
9
 
@@ -130,17 +132,17 @@
130
132
  for (let p in packages) {
131
133
  const resolvedLhs = resolveIfNotPlainOrUrl(p, baseUrl) || p;
132
134
  if (outPackages[resolvedLhs]) {
133
- throw new Error(`Dynamic import map rejected: Overrides entry "${resolvedLhs}" from ${outPackages[resolvedLhs]} to ${packages[resolvedLhs]}.`);
135
+ throw Error(`Rejected map override "${resolvedLhs}" from ${outPackages[resolvedLhs]} to ${packages[resolvedLhs]}.`);
134
136
  }
135
137
  let target = packages[p];
136
- if (typeof target !== 'string')
138
+ if (typeof target !== 'string')
137
139
  continue;
138
140
  const mapped = resolveImportMap(parentMap, resolveIfNotPlainOrUrl(target, baseUrl) || target, baseUrl);
139
141
  if (mapped) {
140
142
  outPackages[resolvedLhs] = mapped;
141
143
  continue;
142
144
  }
143
- targetWarning(p, packages[p], 'bare specifier did not resolve');
145
+ console.warn(`Mapping "${p}" -> "${packages[p]}" does not resolve`);
144
146
  }
145
147
  }
146
148
 
@@ -175,17 +177,10 @@
175
177
  if (pkgName) {
176
178
  const pkg = packages[pkgName];
177
179
  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);
180
+ return pkg + id.slice(pkgName.length);
182
181
  }
183
182
  }
184
183
 
185
- function targetWarning (match, target, msg) {
186
- console.warn("Package target " + msg + ", resolving target '" + target + "' for " + match);
187
- }
188
-
189
184
  function resolveImportMap (importMap, resolvedOrPlain, parentUrl) {
190
185
  let scopeUrl = parentUrl && getMatch(parentUrl, importMap.scopes);
191
186
  while (scopeUrl) {
@@ -199,14 +194,14 @@
199
194
 
200
195
  const optionsScript = document.querySelector('script[type=esms-options]');
201
196
 
202
- const esmsInitOptions$1 = optionsScript ? JSON.parse(optionsScript.innerHTML) : self.esmsInitOptions ? self.esmsInitOptions : {};
197
+ const esmsInitOptions = optionsScript ? JSON.parse(optionsScript.innerHTML) : self.esmsInitOptions ? self.esmsInitOptions : {};
203
198
 
204
- let shimMode = !!esmsInitOptions$1.shimMode;
205
- const resolveHook = globalHook(shimMode && esmsInitOptions$1.resolve);
199
+ let shimMode = !!esmsInitOptions.shimMode;
200
+ const resolveHook = globalHook(shimMode && esmsInitOptions.resolve);
206
201
 
207
- const skip = esmsInitOptions$1.skip ? new RegExp(esmsInitOptions$1.skip) : null;
202
+ const skip = esmsInitOptions.skip ? new RegExp(esmsInitOptions.skip) : null;
208
203
 
209
- let nonce = esmsInitOptions$1.nonce;
204
+ let nonce = esmsInitOptions.nonce;
210
205
 
211
206
  if (!nonce) {
212
207
  const nonceElement = document.querySelector('script[nonce]');
@@ -214,18 +209,18 @@
214
209
  nonce = nonceElement.nonce || nonceElement.getAttribute('nonce');
215
210
  }
216
211
 
217
- const onerror = globalHook(esmsInitOptions$1.onerror || noop);
218
- const onpolyfill = globalHook(esmsInitOptions$1.onpolyfill || noop);
212
+ const onerror = globalHook(esmsInitOptions.onerror || noop);
213
+ const onpolyfill = esmsInitOptions.onpolyfill ? globalHook(esmsInitOptions.onpolyfill) : () => console.info(`OK: "Uncaught TypeError" module failure has been polyfilled`);
219
214
 
220
- const { revokeBlobURLs, noLoadEventRetriggers } = esmsInitOptions$1;
215
+ const { revokeBlobURLs, noLoadEventRetriggers, enforceIntegrity } = esmsInitOptions;
221
216
 
222
- const fetchHook = esmsInitOptions$1.fetch ? globalHook(esmsInitOptions$1.fetch) : fetch;
217
+ const fetchHook = esmsInitOptions.fetch ? globalHook(esmsInitOptions.fetch) : fetch;
223
218
 
224
219
  function globalHook (name) {
225
220
  return typeof name === 'string' ? self[name] : name;
226
221
  }
227
222
 
228
- const enable = Array.isArray(esmsInitOptions$1.polyfillEnable) ? esmsInitOptions$1.polyfillEnable : [];
223
+ const enable = Array.isArray(esmsInitOptions.polyfillEnable) ? esmsInitOptions.polyfillEnable : [];
229
224
  const cssModulesEnabled = enable.includes('css-modules');
230
225
  const jsonModulesEnabled = enable.includes('json-modules');
231
226
 
@@ -316,7 +311,7 @@
316
311
  };
317
312
  }
318
313
 
319
- const resolve = resolveHook ? async (id, parentUrl) => ({ r: await esmsInitOptions.resolve(id, parentUrl, defaultResolve), b: false }) : _resolve;
314
+ const resolve = resolveHook ? async (id, parentUrl) => ({ r: await resolveHook(id, parentUrl, defaultResolve), b: false }) : _resolve;
320
315
 
321
316
  let id = 0;
322
317
  const registry = {};
@@ -338,34 +333,36 @@
338
333
  const initPromise = featureDetectionPromise.then(() => {
339
334
  // shim mode is determined on initialization, no late shim mode
340
335
  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;
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
+ }
352
350
  }
353
351
  }
354
352
  }
355
353
  baselinePassthrough = supportsDynamicImport && supportsImportMeta && supportsImportMaps && (!jsonModulesEnabled || supportsJsonAssertions) && (!cssModulesEnabled || supportsCssAssertions) && !importMapSrcOrLazy && !false;
356
- if (!baselinePassthrough) onpolyfill();
357
354
  if (shimMode || !baselinePassthrough) {
358
355
  new MutationObserver(mutations => {
359
356
  for (const mutation of mutations) {
360
357
  if (mutation.type !== 'childList') continue;
361
358
  for (const node of mutation.addedNodes) {
362
359
  if (node.tagName === 'SCRIPT') {
363
- if (!shimMode && node.type === 'module' || shimMode && node.type === 'module-shim')
360
+ if (node.type === (shimMode ? 'module-shim' : 'module'))
364
361
  processScript(node);
365
- if (!shimMode && node.type === 'importmap' || shimMode && node.type === 'importmap-shim')
362
+ if (node.type === (shimMode ? 'importmap-shim' : 'importmap'))
366
363
  processImportMap(node);
367
364
  }
368
- else if (node.tagName === 'LINK' && node.rel === 'modulepreload')
365
+ else if (node.tagName === 'LINK' && node.rel === (shimMode ? 'modulepreload-shim' : 'modulepreload'))
369
366
  processPreload(node);
370
367
  }
371
368
  }
@@ -376,8 +373,9 @@
376
373
  }
377
374
  });
378
375
  let importMapPromise = initPromise;
379
-
376
+ let firstPolyfillLoad = true;
380
377
  let acceptingImportMaps = true;
378
+
381
379
  async function topLevelLoad (url, fetchOpts, source, nativelyLoaded, lastStaticLoadPromise) {
382
380
  if (!shimMode)
383
381
  acceptingImportMaps = false;
@@ -401,6 +399,10 @@
401
399
  if (revokeBlobURLs) revokeObjectURLs(Object.keys(seen));
402
400
  return module;
403
401
  }
402
+ if (firstPolyfillLoad && !shimMode && load.n && nativelyLoaded) {
403
+ onpolyfill();
404
+ firstPolyfillLoad = false;
405
+ }
404
406
  const module = await dynamicImport(!shimMode && !load.n && nativelyLoaded ? load.u : load.b, { errUrl: load.u });
405
407
  // if the top-level load is a shell, run its update function
406
408
  if (load.s)
@@ -537,12 +539,11 @@
537
539
  load.S = undefined;
538
540
  }
539
541
 
540
- const sourceMapURLRegEx = /\n\/\/# source(Mapping)?URL=([^\n]+)\s*((;|\/\/[^#][^\n]+)\s*)*$/;
542
+ const sourceMapURLRegEx = /\n\/\/# source(Mapping)?URL=([^\n]+)\s*((;|\/\/[^#][^\n]*)\s*)*$/;
541
543
 
542
544
  const jsContentType = /^(text|application)\/(x-)?javascript(;|$)/;
543
545
  const jsonContentType = /^(text|application)\/json(;|$)/;
544
546
  const cssContentType = /^(text|application)\/css(;|$)/;
545
- const wasmContentType = /^application\/wasm(;|$)/;
546
547
 
547
548
  const cssUrlRegEx = /url\(\s*(?:(["'])((?:\\.|[^\n\\"'])+)\1|((?:\\.|[^\s,"'()\\])+))\s*\)/g;
548
549
 
@@ -560,6 +561,8 @@
560
561
  }
561
562
 
562
563
  async function doFetch (url, fetchOpts) {
564
+ if (enforceIntegrity && !fetchOpts.integrity)
565
+ throw Error(`No integrity for ${url}`);
563
566
  const poolQueue = pushFetchPool();
564
567
  if (poolQueue) await poolQueue;
565
568
  try {
@@ -569,7 +572,7 @@
569
572
  popFetchPool();
570
573
  }
571
574
  if (!res.ok)
572
- throw new Error(`${res.status} ${res.statusText} ${res.url}`);
575
+ throw Error(`${res.status} ${res.statusText} ${res.url}`);
573
576
  const contentType = res.headers.get('content-type');
574
577
  if (jsContentType.test(contentType))
575
578
  return { r: res.url, s: await res.text(), t: 'js' };
@@ -579,10 +582,8 @@
579
582
  return { r: res.url, s: `var s=new CSSStyleSheet();s.replaceSync(${
580
583
  JSON.stringify((await res.text()).replace(cssUrlRegEx, (_match, quotes, relUrl1, relUrl2) => `url(${quotes}${resolveUrl(relUrl1 || relUrl2, url)}${quotes})`))
581
584
  });export default s;`, t: 'css' };
582
- else if (wasmContentType.test(contentType))
583
- throw new Error('WASM modules not supported');
584
585
  else
585
- throw new Error(`Unknown Content-Type "${contentType}"`);
586
+ throw Error(`Unsupported Content-Type "${contentType}"`);
586
587
  }
587
588
 
588
589
  function getOrCreateLoad (url, fetchOpts, source) {
@@ -622,7 +623,7 @@
622
623
  ({ r: load.r, s: source, t } = await (fetchCache[url] || doFetch(url, fetchOpts)));
623
624
  if (t && !shimMode) {
624
625
  if (t === 'css' && !cssModulesEnabled || t === 'json' && !jsonModulesEnabled)
625
- 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>`);
626
627
  if (t === 'css' && !supportsCssAssertions || t === 'json' && !supportsJsonAssertions)
627
628
  load.n = true;
628
629
  }
@@ -661,9 +662,9 @@
661
662
  }
662
663
 
663
664
  function processScriptsAndPreloads () {
664
- 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]'))
665
666
  processScript(script);
666
- for (const link of document.querySelectorAll('link[rel="modulepreload"]'))
667
+ for (const link of document.querySelectorAll(shimMode ? 'link[rel=modulepreload-shim]' : 'link[rel=modulepreload]'))
667
668
  processPreload(link);
668
669
  }
669
670
 
@@ -761,7 +762,12 @@
761
762
  if (isDomContentLoadedScript) domContentLoadedCnt++;
762
763
  const blocks = script.getAttribute('async') === null && isReadyScript;
763
764
  const loadPromise = topLevelLoad(script.src || `${baseUrl}?${id++}`, getFetchOpts(script), !script.src && script.innerHTML, !shimMode, blocks && lastStaticLoadPromise).catch(e => {
764
- setTimeout(() => { throw e });
765
+ // Safari only gives error via console.error
766
+ if (safari)
767
+ console.error(e);
768
+ // Firefox only gives error stack via setTimeout
769
+ else
770
+ setTimeout(() => { throw e});
765
771
  onerror(e);
766
772
  });
767
773
  if (blocks)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "es-module-shims",
3
- "version": "1.3.4",
3
+ "version": "1.4.1",
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",