es-module-shims 1.10.1 → 2.0.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/README.md CHANGED
@@ -27,7 +27,7 @@ Because we are still using the native module loader the edge cases work out comp
27
27
  Include ES Module Shims with a `async` attribute on the script, then include an import map and module scripts normally:
28
28
 
29
29
  ```html
30
- <script async src="https://ga.jspm.io/npm:es-module-shims@1.10.1/dist/es-module-shims.js"></script>
30
+ <script async src="https://ga.jspm.io/npm:es-module-shims@2.0.1/dist/es-module-shims.js"></script>
31
31
 
32
32
  <!-- https://generator.jspm.io/#U2NhYGBkDM0rySzJSU1hKEpNTC5xMLTQM9Az0C1K1jMAAKFS5w0gAA -->
33
33
  <script type="importmap">
@@ -62,9 +62,9 @@ This error is important - it means that the native browser loader didn't execute
62
62
  at link time, and before execution time. And this is what allows the polyfill to be able to reexecute the modules and their dependencies
63
63
  without risk of duplicate execution.
64
64
 
65
- The ES Module Shims polyfill will analyze the browser to see if it supports import maps. If it does, it doesn't do anything more,
66
- otherwise it will analyze all module scripts on the page to see if any of them have bare specifier imports that will fail like this.
67
- If one is found, it will then be reexecuted through ES Module Shims using its internal shimming of modules features.
65
+ The ES Module Shims polyfill will analyze the browser to check its fine-grained support for various import maps and modules features.
66
+ If it is deeemed to support a baseline set of features, and multiple import maps are not in use, the polyfill will do no further work. Otherwise, it
67
+ will analyze all module scripts on the page to see if any of them have static module syntax that would fail. If found, that graph will then be reexecuted through ES Module Shims using its internal rewriting of import statements to polyfill features.
68
68
 
69
69
  When the polyfill kicks in another console log message is output(which can be disabled or customized via the [polyfill hook](#polyfill-hook)):
70
70
 
@@ -72,6 +72,9 @@ When the polyfill kicks in another console log message is output(which can be di
72
72
  ^^ Module error above is polyfilled and can be ignored ^^
73
73
  ```
74
74
 
75
+ The fetch options used by the polyfill are carefully followed per the spec. In older Firefox and Safari this fetch network cache is
76
+ not fully shared with the polyfill so separate entries can be seen in the network tab, network-level cache coalescing is still seen at the very least.
77
+
75
78
  ### Polyfill Edge Case: Dynamic Import
76
79
 
77
80
  Only static link-time errors are polyfilled, not runtime errors.
@@ -124,45 +127,52 @@ If a static failure is not possible and dynamic import must be used, one alterna
124
127
 
125
128
  When running in polyfill mode, it can be thought of that are effectively two loaders running on the page - the ES Module Shims polyfill loader, and the native loader.
126
129
 
127
- Note that instances are not shared between these loaders for consistency and performance, since some browsers do not properly share the fetch cache and native loader cache resulting in a double fetch which would be inefficient.
130
+ Whenever possible, the polyfill loader will share native modules that can be correctly executed, with one exception per the previous section - modules which use dynamic import, which are imported as dependencies of modules which require polyfill features.
128
131
 
129
- As a result, if you have two module graphs - one native and one polyfilled, they will not share the same dependency instance, for example:
132
+ For example consider two shimmed modules, both of which use import maps:
130
133
 
131
- ```html
132
- <script type="importmap">
133
- {
134
- "imports": {
135
- "dep": "/dep.js"
136
- }
137
- }
138
- </script>
139
- <script type="module">
140
- import '/dep.js';
141
- </script>
142
- <script type="module">
143
- import 'dep';
144
- </script>
134
+ `shim-a.js`
135
+ ```js
136
+ import 'mapped-dep-a';
137
+ ```
138
+
139
+ `shim-b.js`
140
+ ```js
141
+ import 'mapped-dep-b';
142
+ ```
143
+
144
+ where `mapped-dep-a` resolves to `/dep-a.js` and `mapped-dep-b` resolves to `/dep-b.js`, respectively containing:
145
+
146
+ `/dep-a.js`
147
+ ```js
148
+ console.log('dep a');
145
149
  ```
146
150
 
147
- ```dep
148
- console.log('DEP');
151
+ `/dep-b.js`
152
+ ```js
153
+ console.log('dep b');
154
+ import(expr);
149
155
  ```
150
156
 
151
- When polyfilling import maps, ES Module Shims will pick up on the second import failure and reexecute `/dep.js` as a new instance, logging `"DEP"` twice.
157
+ While the shim modules are always loaded in the shim loader, the `dep-a.js` module is loaded from the native loader since it does not require any polyfilling.
158
+
159
+ On th other hand, `dep-b.js` is loaded from the shim loader as well _because it was loaded by a polyfilled parent graph and uses dynamic import_. Within the polyfill loader, the `import(expr)` is replaced with `importShim(expr)` to support import maps. This is in contrast to top-level native graphs which do not get shimmed per the previous section.
152
160
 
153
- For this reason it is important to always ensure all modules hit the polyfill path, either by having all graphs use import maps at the top-level, or via `importShim` directly.
161
+ As a result, `import('/dep-a.js')` in the native loader is the same instance as the `dep-a.js` loaded by `shim-a.js`, but `dep-b` would be executed twice if passed into `import('/dep-b.js')` - the shim loader and native loader instances are separate, and `dep b` would be logged twice.
154
162
 
155
- If you really need to support instance sharing with the native loader, a useful workaround is to use the [`skip` option](#skip) to list modules which should always be loaded via the native loader:
163
+ Note that this is the ONLY scenario in which instance sharing will not otherwise occur in the polyfill loader.
164
+
165
+ A workaround to this instance sharing case is to use the [`skip` option](#skip) to list modules which should always be loaded via the native loader (which also saves on analysis work time for performance):
156
166
 
157
167
  ```html
158
168
  <script type="esms-options">
159
169
  {
160
- "skip": ["/dep.js"]
170
+ "skip": ["/dep-b.js"]
161
171
  }
162
172
  </script>
163
173
  ```
164
174
 
165
- The above would then fully cause dependency module instance to be shared between ES Module Shims and the native loader, with the polyfill then logging `"DEP"` only once.
175
+ The above would then fully cause dependency module instance of dep-b to be shared between ES Module Shims and the native loader, with the polyfill then logging `"dep b"` only once.
166
176
 
167
177
  #### No Shim Scripts
168
178
 
@@ -217,6 +227,7 @@ Browser Compatibility on baseline ES modules support **with** ES Module Shims:
217
227
  | [modulepreload](#modulepreload) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
218
228
  | [Import Maps](#import-maps) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
219
229
  | [Import Map Integrity](#import-map-integrity) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
230
+ | [Multiple Import Maps](#multiple-import-maps) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
220
231
  | [JSON Modules](#json-modules) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
221
232
  | [CSS Modules](#css-modules) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
222
233
  | [Wasm Modules](#wasm-modules) | 89+ | 89+ | 15+ |
@@ -228,7 +239,8 @@ Browser compatibility **without** ES Module Shims:
228
239
  | [modulepreload](#modulepreload) | 66+ | 115+ | 17.5+ |
229
240
  | [import.meta.url](#importmetaurl) | ~76+ | ~67+ | ~12+ |
230
241
  | [Import Maps](#import-maps) | 89+ | 108+ | 16.4+ |
231
- | [Import Map Integrity](#import-map-integrity) | Pending | :x: | :x: |
242
+ | [Import Map Integrity](#import-map-integrity) | 127+ | :x: | :x: |
243
+ | [Multiple Import Maps](#multiple-import-maps) | Pending | :x: | :x: |
232
244
  | [JSON Modules](#json-modules) | 123+ | :x: | 17.2+ |
233
245
  | [CSS Modules](#css-modules) | 123+ | :x: | :x: |
234
246
  | [Wasm Modules](#wasm-modules) | :x: | :x: | :x: |
@@ -268,14 +280,6 @@ Using this polyfill we can write:
268
280
 
269
281
  All modules are still loaded with the native browser module loader, but with their specifiers rewritten then executed as Blob URLs, so there is a relatively minimal overhead to using a polyfill approach like this.
270
282
 
271
- #### Multiple Import Maps
272
-
273
- Multiple import maps are not currently supported in any native implementation, Chromium support is currently being tracked in https://bugs.chromium.org/p/chromium/issues/detail?id=927119.
274
-
275
- In polyfill mode, multiple import maps are therefore not supported.
276
-
277
- In shim mode, support for multiple `importmap-shim` scripts follows the [import map extensions](https://github.com/guybedford/import-maps-extensions) proposal.
278
-
279
283
  #### External Import Maps
280
284
 
281
285
  External import maps (using a `"src"` attribute) are not currently supported in any native implementation.
@@ -284,9 +288,13 @@ In polyfill mode, external import maps are therefore not supported.
284
288
 
285
289
  In shim mode, external import maps are fully supported.
286
290
 
287
- #### Dynamic Import Maps
291
+ ### Multiple Import Maps
292
+
293
+ Multiple import maps have been recently implemented in Chromium in https://bugs.chromium.org/p/chromium/issues/detail?id=927119, including supporting dynamically loading import maps even after modules have been loaded.
294
+
295
+ In polyfill mode, multiple import maps are supported.
288
296
 
289
- Support for dynamically injecting import maps with JavaScript via eg:
297
+ Support for dynamically injecting import maps with JavaScript via e.g.:
290
298
 
291
299
  ```js
292
300
  document.body.appendChild(Object.assign(document.createElement('script'), {
@@ -295,11 +303,64 @@ document.body.appendChild(Object.assign(document.createElement('script'), {
295
303
  }));
296
304
  ```
297
305
 
298
- is supported in Chromium, provided it is injected before any module loads and there is no other import map yet loaded (multiple import maps are not supported).
306
+ is also provided using mutation observers.
307
+
308
+ The caveat for multiple import map support polyfill support in browsers that only support a single import map is per the usual "polyfill rule" for es-module-shims - only those top-level graphs with static import feailures can be polyfilled.
309
+
310
+ Therefore, imports that would otherwise be supported fine by the first map can't be polyfilled, for example:
311
+
312
+ ```html
313
+ <script type="importmap">
314
+ {
315
+ "imports": {
316
+ "a": "/a.js"
317
+ }
318
+ }
319
+ </script>
320
+ <script type="importmap">
321
+ {
322
+ "scopes": {
323
+ "/": {
324
+ "a": "/b.js"
325
+ }
326
+ }
327
+ }
328
+ </script>
329
+ <script type="module">
330
+ import 'a';
331
+ </script>
332
+ ```
333
+
334
+ In the above, browsers with single import maps support will resolve `/a.js` and the polyfill will not apply, while browsers without any import maps support will be polyfilled to resolve `/b.js`.
335
+
336
+ Instead, following the usual advice, make sure to design the app to either have static failures or not on all polyfill environments to get well-defined polyfill behaviour:
337
+
338
+ ```html
339
+ <script type="importmap">
340
+ {
341
+ "imports": {
342
+ "a": "/a.js"
343
+ }
344
+ }
345
+ </script>
346
+ <script type="importmap">
347
+ {
348
+ "imports": {
349
+ "b": "/b.js"
350
+ }
351
+ }
352
+ </script>
353
+ <script type="module">
354
+ import 'a';
355
+ </script>
356
+ <script type="module">
357
+ import 'b';
358
+ </script>
359
+ ```
299
360
 
300
- Both modes in ES Module Shims support dynamic injection using DOM Mutation Observers.
361
+ The above will then correctly execute both `a` and `b`, with only the `b` importer being polyfilled.
301
362
 
302
- While in polyfill mode the same restrictions apply that multiple import maps, import maps with a `src` attribute, and import maps loaded after the first module load are not supported, in shim mode all of these behaviours are fully enabled for `"importmap-shim"`.
363
+ Note that shimmed graphs will always support correct mappings - the above rules only apply to the initial polyfill engagement.
303
364
 
304
365
  #### Reading current import map state
305
366
 
@@ -529,10 +590,8 @@ window.esmsInitOptions = {
529
590
  polyfillEnable: ['css-modules', 'json-modules'], // default empty
530
591
  // Custom CSP nonce
531
592
  nonce: 'n0nce', // default is automatic detection
532
- // Don't retrigger load events on module scripts (DOMContentLoaded, domready)
593
+ // Don't retrigger load events on module scripts (DOMContentLoaded, domready, window 'onload')
533
594
  noLoadEventRetriggers: true, // default false
534
- // Retrigger window 'load' event (will be combined into load event above on next major)
535
- globalLoadEventRetrigger: true, // default false
536
595
  // Skip source analysis of certain URLs for full native passthrough
537
596
  skip: /^https:\/\/cdn\.com/, // defaults to null
538
597
  // Clean up blob URLs after execution
@@ -659,7 +718,7 @@ Alternatively, add a `blob:` URL policy with the CSP build to get CSP compatibil
659
718
 
660
719
  ### No Load Event Retriggers
661
720
 
662
- Because of the extra processing done by ES Module Shims it is possible for static module scripts to execute after the `DOMContentLoaded` or `readystatechange` events they expect, which can cause missed attachment.
721
+ Because of the extra processing done by ES Module Shims it is possible for static module scripts to execute after the `DOMContentLoaded`, `readystatechange` or window `load` events they expect, which can cause missed attachment.
663
722
 
664
723
  In addition, script elements will also have their load events refired when polyfilled.
665
724
 
@@ -670,20 +729,13 @@ In such a case, this double event firing can be disabled with the `noLoadEventRe
670
729
  ```js
671
730
  <script type="esms-options">
672
731
  {
673
- // do not re-trigger DOM events (onreadystatechange, DOMContentLoaded)
732
+ // do not re-trigger DOM events (onreadystatechange, DOMContentLoaded, window 'onload')
674
733
  "noLoadEventRetriggers": true
675
734
  }
676
735
  </script>
677
736
  <script async src="es-module-shims.js"></script>
678
737
  ```
679
738
 
680
- ### Global Load Event Retrigger
681
-
682
- In ES Module Shims 1.x, load event retriggers only apply to `DOMContentLoaded` and `readystatechange` and not to the window `load` event.
683
- To enable the window / worker `'load'` event, set `globalLoadEventRetrigger: true`.
684
-
685
- In the next major version, this will be the default for load events, at which point only `noLoadEventRetriggers` will remain.
686
-
687
739
  ### Skip
688
740
 
689
741
  When loading modules that you know will only use baseline modules features, it is possible to set a rule to explicitly opt-out modules from being polyfilled to always load and be referenced through the native loader only. This enables instance sharing with the native loader and also improves performance because those modules then do not need to be processed or transformed at all, so that only local application code is handled and not library code.
@@ -840,10 +892,6 @@ If the resolve hook should apply for all modules in the entire module graph, mak
840
892
  </script>
841
893
  ```
842
894
 
843
- Support for an asynchronous resolve hook has been deprecated as of 1.5.0 and will be removed in the next major.
844
-
845
- Instead async work should be done with the import hook.
846
-
847
895
  #### Meta Hook
848
896
 
849
897
  The meta hook allows customizing the `import.meta` object in each module scope.