Last active
February 1, 2024 21:16
-
-
Save westc/5a632ddd50c2b93ad04a803e9269573a to your computer and use it in GitHub Desktop.
Loads a <script> tag if necessary and finishes once the `checker` returns a promised value that is not falsy.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Can load multiple scripts (javascript and css) and finishes once the | |
* `checker` returns a promised value that is not falsy. | |
* @param {string[]} urls | |
* Ordinarily a URL is determined to be for CSS or JS by the extension of the | |
* pathname but if it doesn't end in .js or .css this will not be possible and | |
* it will default to JS. If you want to force a URL to be recognized as a JS | |
* file you should prefix the URL with `"js:"` and if you want it to be | |
* recognized as a CSS file you should prefix it with `"css:"`. | |
* @param {null|undefined|{ | |
* checker?: (scripts: (HTMLLinkElement|HTMLScriptElement)[], startTime: number, options: ThisType) => T; | |
* postCheck?: boolean; | |
* preCheck?: boolean; | |
* timeoutMS?: number; | |
* uniquifyByURL?: boolean; | |
* }} options | |
* @return {Promise<T|(HTMLLinkElement|HTMLScriptElement)[]>} | |
* @template T | |
*/ | |
async function loadScripts(urls, options) { | |
return new Promise(async(resolve, reject) => { | |
const {checker, postCheck, preCheck, timeoutMS, uniquifyByURL} = options ?? {}; | |
// If a pre-check should be done... | |
if (checker && preCheck) { | |
const result = await checker(void 0, void 0, options); | |
if (result) return resolve(result); | |
} | |
/** | |
* Get an array of the results that corresponds to the given urls. | |
* @type {(HTMLLinkElement|HTMLScriptElement)[]} | |
*/ | |
const scripts = await Promise.all(urls.map(url => new Promise((resolve, reject) => { | |
let isCSS; | |
const realUrl = url.replace( | |
/^(css|js):\s*|\.(css|js)(?:\?[^]*)?#?/i, | |
(m, fakeProtocol, ext) => { | |
isCSS = (ext ?? fakeProtocol).toLowerCase() === 'css'; | |
return fakeProtocol ? '' : m; | |
} | |
); | |
/** @type {HTMLLinkElement|HTMLScriptElement} */ | |
const elem = (uniquifyByURL | |
&& document.querySelector(`${isCSS ? 'link' : 'script'}[data-url="${CSS.escape(realUrl)}"]`) | |
) | |
?? (isCSS | |
? Object.assign(document.createElement('link'), {rel: 'stylesheet', href: realUrl}) | |
: Object.assign(document.createElement('script'), {src: realUrl}) | |
); | |
elem.setAttribute('data-url', realUrl); | |
// If the link or script tag has errored out or has loaded go ahead | |
if (elem.hasLoaded) { | |
if (elem.loadError) reject(elem.loadError); | |
else resolve(elem); | |
} | |
else { | |
elem.addEventListener('load', () => { | |
elem.hasLoaded = true; | |
resolve(elem); | |
}); | |
elem.addEventListener('error', evt => { | |
elem.hasLoaded = true; | |
elem.loadError = evt; | |
reject(evt); | |
}); | |
if (!elem.parentNode) (document.head ?? document.body).appendChild(elem); | |
} | |
}))); | |
// If there not supposed to do the post-check just return the scripts. | |
if (!(postCheck ?? !!checker)) return resolve(scripts); | |
// Since the checker needs to pass before returning start things off so that | |
// the checker will be occurred every 50ms until it passes or times out. | |
async function callback() { | |
try { | |
if (timeoutMS == null || Date.now() - startTime >= timeoutMS) { | |
throw new Error(`Timed out when using loadScript() on ${JSON.stringify(urls)}.`); | |
} | |
const result = await checker(scripts, startTime, options); | |
if (result) resolve(result); | |
else setTimeout(callback, 50); | |
} | |
catch (err) { | |
reject(err); | |
} | |
} | |
const startTime = Date.now(); | |
callback(); | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Loads the ace editor code if not already found. | |
await loadScript(['https://cdnjs.cloudflare.com/ajax/libs/ace/1.13.1/ace.js'], { | |
preCheck: true, | |
checker: () => window.ace, | |
timeoutMS: 3000 | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment