2019-10-09 15:03:30 +00:00
|
|
|
/**
|
|
|
|
* Default timeout for loading scripts.
|
|
|
|
*/
|
|
|
|
const DEFAULT_TIMEOUT = 5000;
|
2017-12-05 20:34:24 +00:00
|
|
|
|
2016-10-05 14:36:59 +00:00
|
|
|
/**
|
2016-12-07 22:00:54 +00:00
|
|
|
* Loads a script from a specific URL. React Native cannot load a JS
|
2017-08-23 13:03:55 +00:00
|
|
|
* file/resource/URL via a <script> HTML element, so the implementation
|
2017-10-01 06:35:19 +00:00
|
|
|
* fetches the specified {@code url} as plain text using {@link fetch()} and
|
2017-08-31 19:16:44 +00:00
|
|
|
* then evaluates the fetched string as JavaScript code (using {@link eval()}).
|
2016-10-05 14:36:59 +00:00
|
|
|
*
|
2017-08-31 19:16:44 +00:00
|
|
|
* @param {string} url - The absolute URL from which the script is to be
|
2016-12-07 22:00:54 +00:00
|
|
|
* (down)loaded.
|
2017-12-05 20:34:24 +00:00
|
|
|
* @param {number} [timeout] - The timeout in millisecnods after which the
|
|
|
|
* loading of the specified {@code url} is to be aborted/rejected (if not
|
|
|
|
* settled yet).
|
2021-03-16 15:59:33 +00:00
|
|
|
* @param {boolean} skipEval - Whether we want to skip evaluating the loaded content or not.
|
2016-10-05 14:36:59 +00:00
|
|
|
* @returns {void}
|
|
|
|
*/
|
2019-10-09 15:03:30 +00:00
|
|
|
export async function loadScript(
|
2022-07-29 13:18:14 +00:00
|
|
|
url: string, timeout: number = DEFAULT_TIMEOUT, skipEval = false): Promise<any> {
|
2019-10-09 15:03:30 +00:00
|
|
|
// XXX The implementation of fetch on Android will throw an Exception on
|
|
|
|
// the Java side which will break the app if the URL is invalid (which
|
|
|
|
// the implementation of fetch on Android calls 'unexpected url'). In
|
|
|
|
// order to try to prevent the breakage of the app, try to fail on an
|
|
|
|
// invalid URL as soon as possible.
|
|
|
|
const { hostname, pathname, protocol } = new URL(url);
|
2017-09-25 17:28:36 +00:00
|
|
|
|
2019-10-09 15:03:30 +00:00
|
|
|
// XXX The standard URL implementation should throw an Error if the
|
|
|
|
// specified URL is relative. Unfortunately, the polyfill used on
|
|
|
|
// react-native does not.
|
|
|
|
if (!hostname || !pathname || !protocol) {
|
|
|
|
throw new Error(`unexpected url: ${url}`);
|
|
|
|
}
|
2017-09-25 17:28:36 +00:00
|
|
|
|
2019-10-09 15:03:30 +00:00
|
|
|
const controller = new AbortController();
|
|
|
|
const signal = controller.signal;
|
2017-09-25 17:28:36 +00:00
|
|
|
|
2019-10-09 15:03:30 +00:00
|
|
|
const timer = setTimeout(() => {
|
|
|
|
controller.abort();
|
|
|
|
}, timeout);
|
2017-12-05 20:34:24 +00:00
|
|
|
|
2019-10-09 15:03:30 +00:00
|
|
|
const response = await fetch(url, { signal });
|
2017-12-05 20:34:24 +00:00
|
|
|
|
2019-10-09 15:03:30 +00:00
|
|
|
// If the timeout hits the above will raise AbortError.
|
|
|
|
|
|
|
|
clearTimeout(timer);
|
|
|
|
|
|
|
|
switch (response.status) {
|
|
|
|
case 200: {
|
|
|
|
const txt = await response.text();
|
|
|
|
|
|
|
|
if (skipEval) {
|
|
|
|
return txt;
|
|
|
|
}
|
2016-12-07 22:00:54 +00:00
|
|
|
|
2019-10-09 15:03:30 +00:00
|
|
|
return eval.call(window, txt); // eslint-disable-line no-eval
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
throw new Error(`loadScript error: ${response.statusText}`);
|
|
|
|
}
|
2016-10-05 14:36:59 +00:00
|
|
|
}
|