UNPKG

react-native

Version:

A framework for building native apps using React

153 lines (140 loc) 4.14 kB
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow strict-local * @format * @oncall react_native */ import Networking from '../../Network/RCTNetworking'; import DevLoadingView from '../../Utilities/DevLoadingView'; import HMRClient from '../../Utilities/HMRClient'; import getDevServer from './getDevServer'; declare var global: {globalEvalWithSourceUrl?: (string, string) => mixed, ...}; let pendingRequests = 0; const cachedPromisesByUrl = new Map<string, Promise<void>>(); function asyncRequest( url: string, ): Promise<{body: string, headers: {[string]: string}}> { let id = null; let responseText = null; let headers = null; let dataListener; let completeListener; let responseListener; let incrementalDataListener; return new Promise<{body: string, headers: {[string]: string}}>( (resolve, reject) => { dataListener = Networking.addListener( 'didReceiveNetworkData', ([requestId, response]) => { if (requestId === id) { responseText = response; } }, ); incrementalDataListener = Networking.addListener( 'didReceiveNetworkIncrementalData', ([requestId, data]) => { if (requestId === id) { if (responseText != null) { responseText += data; } else { responseText = data; } } }, ); responseListener = Networking.addListener( 'didReceiveNetworkResponse', ([requestId, status, responseHeaders]) => { if (requestId === id) { headers = responseHeaders; } }, ); completeListener = Networking.addListener( 'didCompleteNetworkResponse', ([requestId, error]) => { if (requestId === id) { if (error) { reject(error); } else { //$FlowFixMe[incompatible-call] resolve({body: responseText, headers}); } } }, ); Networking.sendRequest( 'GET', 'asyncRequest', url, {}, '', 'text', true, 0, requestId => { id = requestId; }, true, ); }, ).finally(() => { dataListener?.remove(); completeListener?.remove(); responseListener?.remove(); incrementalDataListener?.remove(); }); } function buildUrlForBundle(bundlePathAndQuery: string) { const {url: serverUrl} = getDevServer(); return ( serverUrl.replace(/\/+$/, '') + '/' + bundlePathAndQuery.replace(/^\/+/, '') ); } module.exports = function (bundlePathAndQuery: string): Promise<void> { const requestUrl = buildUrlForBundle(bundlePathAndQuery); let loadPromise = cachedPromisesByUrl.get(requestUrl); if (loadPromise) { return loadPromise; } DevLoadingView.showMessage('Downloading...', 'load'); ++pendingRequests; loadPromise = asyncRequest(requestUrl) .then<void>(({body, headers}) => { if ( headers['Content-Type'] != null && headers['Content-Type'].indexOf('application/json') >= 0 ) { // Errors are returned as JSON. throw new Error( JSON.parse(body).message || `Unknown error fetching '${bundlePathAndQuery}'`, ); } HMRClient.registerBundle(requestUrl); // Some engines do not support `sourceURL` as a comment. We expose a // `globalEvalWithSourceUrl` function to handle updates in that case. if (global.globalEvalWithSourceUrl) { global.globalEvalWithSourceUrl(body, requestUrl); } else { // eslint-disable-next-line no-eval eval(body); } }) .catch<void>(e => { cachedPromisesByUrl.delete(requestUrl); throw e; }) .finally(() => { if (!--pendingRequests) { DevLoadingView.hide(); } }); cachedPromisesByUrl.set(requestUrl, loadPromise); return loadPromise; };