@expo/metro-runtime
Version:
Tools for making advanced Metro bundler features work
91 lines (79 loc) • 2.65 kB
text/typescript
/**
* Copyright © 2022 650 Industries.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const currentSrc =
typeof document !== 'undefined' && document.currentScript
? ('src' in document.currentScript && document.currentScript.src) || null
: null;
// Basically `__webpack_require__.l`.
export function fetchThenEvalAsync(
url: string,
{
scriptType,
nonce,
crossOrigin,
}: { scriptType?: string; nonce?: string; crossOrigin?: string } = {}
): Promise<void> {
if (
typeof window === 'undefined' ||
// In development, use the fetch/eval method to detect the server error codes and parse bundler errors for the error overlay.
__DEV__
) {
return require('./fetchThenEvalJs').fetchThenEvalAsync(url);
}
return new Promise<void>((resolve, reject) => {
const script = document.createElement('script');
if (scriptType) script.type = scriptType;
if (nonce) script.setAttribute('nonce', nonce);
// script.setAttribute('data-expo-metro', ...);
script.src = url;
if (crossOrigin && script.src.indexOf(window.location.origin + '/') !== 0) {
script.crossOrigin = crossOrigin;
}
script.onload = () => {
script.parentNode && script.parentNode.removeChild(script);
resolve();
};
// Create a new error object to preserve the original stack trace.
const error = new AsyncRequireError();
// Server error or network error.
script.onerror = (ev) => {
let event: Event;
if (typeof ev === 'string') {
event = {
type: 'error',
target: {
// @ts-expect-error
src: event,
},
};
} else {
event = ev;
}
const errorType = event && (event.type === 'load' ? 'missing' : event.type);
// @ts-expect-error
const realSrc = event?.target?.src;
error.message = 'Loading module ' + url + ' failed.\n(' + errorType + ': ' + realSrc + ')';
error.type = errorType;
error.request = realSrc;
script.parentNode && script.parentNode.removeChild(script);
reject(error);
};
if (script.src === currentSrc) {
// NOTE(kitten): We always prevent `fetchThenEval` from loading the "current script".
// This points at our entrypoint bundle, and we should never reload and reevaluate the
// entrypoint bundle
resolve();
} else {
document.head.appendChild(script);
}
});
}
class AsyncRequireError extends Error {
readonly name = 'AsyncRequireError';
type?: string;
request?: string;
}