swapScripts = (targetScript, sourceScript) => {
  return new Promise((resolve) => {
    /**@type {HTMLScriptElement} */
    const newScript = document.createElement("script");

    sourceScript = sourceScript ?? targetScript;

    // Use existing src or create url from script content
    // This is necessary for onload event to work properly
    const src = sourceScript.src || createScriptLink(sourceScript.text);

    // Remember the script to prevent loading it more than once
    loadedScripts.add(sourceScript.outerHTML);

    // We respect async attribute, so we only wait for script to load
    // when it's explicitly no async attribute
    if (!sourceScript.async) {
      const onScriptLoaded = ({ type }) => {
        newScript.removeEventListener("load", onScriptLoaded);
        newScript.removeEventListener("error", onScriptLoaded);
        resolve(type === "error" ? false : newScript);
      };

      newScript.addEventListener("load", onScriptLoaded);
      newScript.addEventListener("error", onScriptLoaded);
    } else {
      resolve();
    }

    if (sourceScript.dataset.alwaysReload !== undefined) {
      newScript.dataset.alwaysReload = "";
    }

    if (sourceScript.id) newScript.id = sourceScript.id;
    if (sourceScript.className) newScript.className = sourceScript.className;

    newScript.dataset.replaced = "true";
    newScript.async = sourceScript.async;
    newScript.defer = sourceScript.defer;
    newScript.type = "text/javascript";
    newScript.src = src;

    targetScript.parentNode.insertBefore(newScript, targetScript);
    targetScript.remove();
  });
}
@param {HTMLScriptElement} targetScript
@param {HTMLScriptElement} sourceScript