function findScript(src: string): HTMLScriptElement | undefined { const scripts = Array.prototype.slice.call( window.document.querySelectorAll("script"), ); return scripts.find((s) => s.src === src); } /** * Load a script from a URL and append it to the document head */ export function loadScript( src: string, attributes?: Record, ): Promise { const found = findScript(src); if (found !== undefined) { const status = found?.getAttribute("status"); if (status === "loaded") { return Promise.resolve(found); } if (status === "loading") { return new Promise((resolve, reject) => { found.addEventListener("load", () => resolve(found)); found.addEventListener("error", (err) => reject(err)); }); } } return new Promise((resolve, reject) => { const script = window.document.createElement("script"); script.type = "text/javascript"; script.src = src; script.async = true; script.setAttribute("status", "loading"); for (const [k, v] of Object.entries(attributes ?? {})) { script.setAttribute(k, v); } script.onload = (): void => { script.onerror = script.onload = null; script.setAttribute("status", "loaded"); resolve(script); }; script.onerror = (): void => { script.onerror = script.onload = null; script.setAttribute("status", "error"); reject(new Error(`Failed to load ${src}`)); }; const firstExistingScript = window.document.querySelector("script"); if (!firstExistingScript) { window.document.head.appendChild(script); } else { firstExistingScript.parentElement?.insertBefore( script, firstExistingScript, ); } }); }