UNPKG

@grail-ui/svelte

Version:

[![NPM](https://img.shields.io/npm/v/@grail-ui/svelte)](https://www.npmjs.com/package/@grail-ui/svelte) [![minified](https://img.shields.io/bundlephobia/min/@grail-ui/svelte)](https://bundlephobia.com/package/@grail-ui/svelte) [![minified + zipped](https:

103 lines (102 loc) 3.9 kB
import { get, writable } from 'svelte/store'; import { noop } from '../util/noop.js'; import { isClient } from '../util/is.js'; import { toReadable } from '../util/store.js'; import { tryOnDestroy, tryOnMount } from '../util/lifecycle.js'; export const createScriptTag = (options) => { const defaultDocument = isClient ? window.document : undefined; const { url, immediate = true, type = 'text/javascript', async = true, crossOrigin, referrerPolicy, noModule, defer, document = defaultDocument, attrs = {}, autoUnload = true, onLoaded = noop, onError = noop, } = options; function queryScript() { return document?.querySelector(`script[src="${url}"]`) ?? null; } const scriptTag = writable(queryScript()); let _promise = null; const loadScript = (waitForScriptLoad) => new Promise((resolve, reject) => { // Check if document actually exists, otherwise resolve the Promise (SSR Support). if (!document) { resolve(false); return; } // Some little closure for resolving the Promise. const resolveWithElement = (el) => { scriptTag.set(el); resolve(el); return el; }; // Local variable defining if the <script> tag should be appended or not. let shouldAppend = false; let script = queryScript(); // Script tag not found, preparing the element for appending if (!script) { script = document.createElement('script'); script.type = type; script.async = async; script.src = url; // Optional attributes if (defer) script.defer = defer; if (crossOrigin) script.crossOrigin = crossOrigin; if (noModule) script.noModule = noModule; if (referrerPolicy) script.referrerPolicy = referrerPolicy; Object.entries(attrs).forEach(([name, value]) => script.setAttribute(name, value)); // Enables shouldAppend shouldAppend = true; } // Script tag already exists, resolve the loading Promise with it. else if (script.hasAttribute('data-loaded')) { if (script.getAttribute('data-loaded') === 'true') { resolveWithElement(script); onLoaded(script); } else { onError(script); reject(script); } return; } // Event listeners const handleError = (event) => { script.setAttribute('data-loaded', 'false'); onError(script, event); reject(script); }; script.addEventListener('error', handleError); script.addEventListener('abort', handleError); script.addEventListener('load', () => { script.setAttribute('data-loaded', 'true'); onLoaded(script); resolveWithElement(script); }); // Append the <script> tag to head. if (shouldAppend) script = document.head.appendChild(script); // If script load awaiting isn't needed, we can resolve the Promise. if (!waitForScriptLoad) resolveWithElement(script); }); const load = (waitForScriptLoad = true) => { if (!_promise) { _promise = loadScript(waitForScriptLoad); } return _promise; }; const unload = () => { if (!document) return; _promise = null; if (get(scriptTag)) scriptTag.set(null); const el = queryScript(); if (el) document.head.removeChild(el); }; if (immediate) tryOnMount(load); if (autoUnload) { tryOnDestroy(unload); } return { scriptTag: toReadable(scriptTag), load, unload }; };