TheDemystifier

Anki script - insert Script

Oct 4th, 2023 (edited)
194
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * Ensures a `<script type="module">` tag pointing at `path` exists in `<head>`, loading it as an
  3.  * ES module and resolving once it (or a fallback) has executed.
  4.  *
  5.  * Design notes:
  6.  * - Module scripts are cached by the browser's module map, keyed by the *resolved* URL. Once a
  7.  *   module has been fetched/instantiated, re-adding a tag with the same `src` (or clearing and
  8.  *   re-setting `src`, as classic scripts allow) does **not** re-run its top-level code — the
  9.  *   engine just reuses the cached module record. So, unlike a classic-script loader, we treat an
  10.  *   existing matching `<script>` tag as "already loaded" and resolve immediately, rather than
  11.  *   trying to force a reload.
  12.  * - `script.src` is always reported by the DOM as an absolute URL, so `path` is resolved against
  13.  *   `document.baseURI` before comparing, otherwise a relative `path` would never match an
  14.  *   existing absolute `src` and we'd insert duplicate script tags.
  15.  * - If the primary `path` fails to load (e.g. a local/offline asset is missing), we transparently
  16.  *   retry once from `altURL` (e.g. a CDN mirror) before rejecting.
  17.  *
  18.  * @param {string} path - Primary module script URL to load (may be relative to the current document).
  19.  * @param {string} altURL - Fallback module script URL to try if `path` fails to load.
  20.  * @returns {Promise<void>} Resolves once a module script (primary or fallback) has loaded;
  21.  *   rejects only if both the primary and fallback loads fail.
  22.  */
  23. function insertScript(path, altURL) {
  24.     return new Promise((resolve, reject) => {
  25.         // Normalize to an absolute URL so it can be compared against existing <script src> values.
  26.         const resolvedPath = new URL(path, document.baseURI).href;
  27.  
  28.         // Reuse an already-inserted module tag instead of loading the same module twice.
  29.         const existingScript = Array.from(document.head.getElementsByTagName('script')).find(
  30.             (script) => script.src === resolvedPath,
  31.         );
  32.  
  33.         if (existingScript) {
  34.             // See design notes above: re-triggering `src` would not re-execute an already-cached module.
  35.             resolve();
  36.             return;
  37.         }
  38.  
  39.         loadModuleScript(resolvedPath, resolve, () => {
  40.             // Primary source failed — fall back to the alternate URL before giving up entirely.
  41.             loadModuleScript(altURL, resolve, reject);
  42.         });
  43.     });
  44.  
  45.     /**
  46.      * Creates, configures, and appends a single `<script type="module">` element.
  47.      *
  48.      * @param {string} src - The module URL to load.
  49.      * @param {() => void} onSuccess - Invoked once the module has loaded.
  50.      * @param {(error: Error) => void} onFailure - Invoked if the module fails to load.
  51.      * @returns {void}
  52.      */
  53.     function loadModuleScript(src, onSuccess, onFailure) {
  54.         const script = document.createElement('script');
  55.         // `type="module"` gives us native deferred, once-only execution and top-level `import`/`export`.
  56.         script.type = 'module';
  57.         script.src = src;
  58.         script.onload = () => onSuccess();
  59.         script.onerror = () => onFailure(new Error(`Failed to load module script: ${src}`));
  60.         document.head.appendChild(script);
  61.     }
  62. }
Advertisement
Add Comment
Please, Sign In to add comment