You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

loadScript.native.js 2.1KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. // @flow
  2. /**
  3. * Default timeout for loading scripts.
  4. */
  5. const DEFAULT_TIMEOUT = 5000;
  6. /**
  7. * Loads a script from a specific URL. React Native cannot load a JS
  8. * file/resource/URL via a <script> HTML element, so the implementation
  9. * fetches the specified {@code url} as plain text using {@link fetch()} and
  10. * then evaluates the fetched string as JavaScript code (using {@link eval()}).
  11. *
  12. * @param {string} url - The absolute URL from which the script is to be
  13. * (down)loaded.
  14. * @param {number} [timeout] - The timeout in millisecnods after which the
  15. * loading of the specified {@code url} is to be aborted/rejected (if not
  16. * settled yet).
  17. * @param {boolean} skipEval - Whether we want to skip evaluating the loaded content or not.
  18. * @returns {void}
  19. */
  20. export async function loadScript(
  21. url: string, timeout: number = DEFAULT_TIMEOUT, skipEval: boolean = false): Promise<any> {
  22. // XXX The implementation of fetch on Android will throw an Exception on
  23. // the Java side which will break the app if the URL is invalid (which
  24. // the implementation of fetch on Android calls 'unexpected url'). In
  25. // order to try to prevent the breakage of the app, try to fail on an
  26. // invalid URL as soon as possible.
  27. const { hostname, pathname, protocol } = new URL(url);
  28. // XXX The standard URL implementation should throw an Error if the
  29. // specified URL is relative. Unfortunately, the polyfill used on
  30. // react-native does not.
  31. if (!hostname || !pathname || !protocol) {
  32. throw new Error(`unexpected url: ${url}`);
  33. }
  34. const controller = new AbortController();
  35. const signal = controller.signal;
  36. const timer = setTimeout(() => {
  37. controller.abort();
  38. }, timeout);
  39. const response = await fetch(url, { signal });
  40. // If the timeout hits the above will raise AbortError.
  41. clearTimeout(timer);
  42. switch (response.status) {
  43. case 200: {
  44. const txt = await response.text();
  45. if (skipEval) {
  46. return txt;
  47. }
  48. return eval.call(window, txt); // eslint-disable-line no-eval
  49. }
  50. default:
  51. throw new Error(`loadScript error: ${response.statusText}`);
  52. }
  53. }