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.
212 lines
9.1 KiB
212 lines
9.1 KiB
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.startTunnelsAsync = startTunnelsAsync;
|
|
exports.stopTunnelsAsync = stopTunnelsAsync;
|
|
function _config() {
|
|
const data = require("@expo/config");
|
|
_config = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function path() {
|
|
const data = _interopRequireWildcard(require("path"));
|
|
path = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _internal() {
|
|
const data = require("../internal");
|
|
_internal = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function UrlUtils() {
|
|
const data = _interopRequireWildcard(require("./ngrokUrl"));
|
|
UrlUtils = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
const NGROK_CONFIG = {
|
|
authToken: '5W1bR67GNbWcXqmxZzBG1_56GezNeaX6sSRvn8npeQ8',
|
|
authTokenPublicId: '5W1bR67GNbWcXqmxZzBG1',
|
|
domain: 'exp.direct'
|
|
};
|
|
function getNgrokConfigPath() {
|
|
return path().join(_internal().UserSettings.dotExpoHomeDirectory(), 'ngrok.yml');
|
|
}
|
|
async function getProjectRandomnessAsync(projectRoot) {
|
|
const ps = await _internal().ProjectSettings.readAsync(projectRoot);
|
|
const randomness = ps.urlRandomness;
|
|
if (randomness) {
|
|
return randomness;
|
|
} else {
|
|
return resetProjectRandomnessAsync(projectRoot);
|
|
}
|
|
}
|
|
async function resetProjectRandomnessAsync(projectRoot) {
|
|
const randomness = UrlUtils().someRandomness();
|
|
_internal().ProjectSettings.setAsync(projectRoot, {
|
|
urlRandomness: randomness
|
|
});
|
|
return randomness;
|
|
}
|
|
async function connectToNgrokAsync(projectRoot, ngrok, args, hostnameAsync, ngrokPid, attempts = 0) {
|
|
try {
|
|
const configPath = getNgrokConfigPath();
|
|
const hostname = await hostnameAsync();
|
|
const url = await ngrok.connect({
|
|
hostname,
|
|
configPath,
|
|
onStatusChange: handleStatusChange.bind(null, projectRoot),
|
|
...args
|
|
});
|
|
return url;
|
|
} catch (e) {
|
|
// Attempt to connect 3 times
|
|
if (attempts >= 2) {
|
|
if (e.message) {
|
|
throw new (_internal().XDLError)('NGROK_ERROR', e.toString());
|
|
} else {
|
|
throw new (_internal().XDLError)('NGROK_ERROR', JSON.stringify(e));
|
|
}
|
|
}
|
|
if (!attempts) {
|
|
attempts = 0;
|
|
} // Attempt to fix the issue
|
|
if (e.error_code && e.error_code === 103) {
|
|
if (attempts === 0) {
|
|
// Failed to start tunnel. Might be because url already bound to another session.
|
|
if (ngrokPid) {
|
|
try {
|
|
process.kill(ngrokPid, 'SIGKILL');
|
|
} catch {
|
|
_internal().ProjectUtils.logDebug(projectRoot, 'expo', `Couldn't kill ngrok with PID ${ngrokPid}`);
|
|
}
|
|
} else {
|
|
await ngrok.kill();
|
|
}
|
|
} else {
|
|
// Change randomness to avoid conflict if killing ngrok didn't help
|
|
await resetProjectRandomnessAsync(projectRoot);
|
|
}
|
|
} // Wait 100ms and then try again
|
|
await (0, _internal().delayAsync)(100);
|
|
return connectToNgrokAsync(projectRoot, ngrok, args, hostnameAsync, null, attempts + 1);
|
|
}
|
|
}
|
|
const TUNNEL_TIMEOUT = 10 * 1000;
|
|
async function startTunnelsAsync(projectRoot, options = {}) {
|
|
const ngrok = await (0, _internal().resolveNgrokAsync)(projectRoot, options);
|
|
const username = (await _internal().UserManager.getCurrentUsernameAsync()) || _internal().ANONYMOUS_USERNAME;
|
|
(0, _internal().assertValidProjectRoot)(projectRoot);
|
|
const packagerInfo = await _internal().ProjectSettings.readPackagerInfoAsync(projectRoot);
|
|
if (!packagerInfo.packagerPort) {
|
|
throw new (_internal().XDLError)('NO_PACKAGER_PORT', `No packager found for project at ${projectRoot}.`);
|
|
}
|
|
if (!packagerInfo.expoServerPort) {
|
|
throw new (_internal().XDLError)('NO_EXPO_SERVER_PORT', `No Expo server found for project at ${projectRoot}.`);
|
|
}
|
|
const expoServerPort = packagerInfo.expoServerPort;
|
|
await stopTunnelsAsync(projectRoot);
|
|
if (await _internal().Android.startAdbReverseAsync(projectRoot)) {
|
|
_internal().ProjectUtils.logInfo(projectRoot, 'expo', 'Successfully ran `adb reverse`. Localhost URLs should work on the connected Android device.');
|
|
}
|
|
const packageShortName = path().parse(projectRoot).base;
|
|
const expRc = await (0, _config().readExpRcAsync)(projectRoot);
|
|
let startedTunnelsSuccessfully = false;
|
|
|
|
// Some issues with ngrok cause it to hang indefinitely. After
|
|
// TUNNEL_TIMEOUTms we just throw an error.
|
|
await Promise.race([(async () => {
|
|
await (0, _internal().delayAsync)(TUNNEL_TIMEOUT);
|
|
if (!startedTunnelsSuccessfully) {
|
|
throw new Error('Starting tunnels timed out');
|
|
}
|
|
})(), (async () => {
|
|
const createResolver = (extra = []) => async function resolveHostnameAsync() {
|
|
const randomness = expRc.manifestTunnelRandomness ? expRc.manifestTunnelRandomness : await getProjectRandomnessAsync(projectRoot);
|
|
return [...extra, randomness, UrlUtils().domainify(username), UrlUtils().domainify(packageShortName), NGROK_CONFIG.domain].join('.');
|
|
};
|
|
|
|
// If both ports are defined and they don't match then we can assume the legacy dev server is being used.
|
|
const isLegacyDevServer = !!expoServerPort && !!packagerInfo.packagerPort && expoServerPort !== packagerInfo.packagerPort;
|
|
_internal().ProjectUtils.logInfo(projectRoot, 'expo', `Using legacy dev server: ${isLegacyDevServer}`);
|
|
const expoServerNgrokUrl = await connectToNgrokAsync(projectRoot, ngrok, {
|
|
authtoken: NGROK_CONFIG.authToken,
|
|
port: expoServerPort,
|
|
proto: 'http'
|
|
}, createResolver(), packagerInfo.ngrokPid);
|
|
let packagerNgrokUrl;
|
|
if (isLegacyDevServer) {
|
|
packagerNgrokUrl = await connectToNgrokAsync(projectRoot, ngrok, {
|
|
authtoken: NGROK_CONFIG.authToken,
|
|
port: packagerInfo.packagerPort,
|
|
proto: 'http'
|
|
}, createResolver(['packager']), packagerInfo.ngrokPid);
|
|
} else {
|
|
// Custom dev server will share the port across expo and metro dev servers,
|
|
// this means we only need one ngrok URL.
|
|
packagerNgrokUrl = expoServerNgrokUrl;
|
|
}
|
|
await _internal().ProjectSettings.setPackagerInfoAsync(projectRoot, {
|
|
expoServerNgrokUrl,
|
|
packagerNgrokUrl,
|
|
ngrokPid: ngrok.getActiveProcess().pid
|
|
});
|
|
startedTunnelsSuccessfully = true;
|
|
_internal().ProjectUtils.logWithLevel(projectRoot, 'info', {
|
|
tag: 'expo',
|
|
_expoEventType: 'TUNNEL_READY'
|
|
}, 'Tunnel ready.');
|
|
})()]);
|
|
}
|
|
async function stopTunnelsAsync(projectRoot) {
|
|
(0, _internal().assertValidProjectRoot)(projectRoot);
|
|
const ngrok = await (0, _internal().resolveNgrokAsync)(projectRoot, {
|
|
shouldPrompt: false
|
|
}).catch(() => null);
|
|
if (!ngrok) {
|
|
return;
|
|
}
|
|
|
|
// This will kill all ngrok tunnels in the process.
|
|
// We'll need to change this if we ever support more than one project
|
|
// open at a time in XDE.
|
|
const packagerInfo = await _internal().ProjectSettings.readPackagerInfoAsync(projectRoot);
|
|
const ngrokProcess = ngrok.getActiveProcess();
|
|
const ngrokProcessPid = ngrokProcess ? ngrokProcess.pid : null;
|
|
if (packagerInfo.ngrokPid && packagerInfo.ngrokPid !== ngrokProcessPid) {
|
|
// Ngrok is running in some other process. Kill at the os level.
|
|
try {
|
|
process.kill(packagerInfo.ngrokPid);
|
|
} catch {
|
|
_internal().ProjectUtils.logDebug(projectRoot, 'expo', `Couldn't kill ngrok with PID ${packagerInfo.ngrokPid}`);
|
|
}
|
|
} else {
|
|
// Ngrok is running from the current process. Kill using ngrok api.
|
|
await ngrok.kill();
|
|
}
|
|
await _internal().ProjectSettings.setPackagerInfoAsync(projectRoot, {
|
|
expoServerNgrokUrl: null,
|
|
packagerNgrokUrl: null,
|
|
ngrokPid: null
|
|
});
|
|
await _internal().Android.stopAdbReverseAsync(projectRoot);
|
|
}
|
|
function handleStatusChange(projectRoot, status) {
|
|
if (status === 'closed') {
|
|
_internal().ProjectUtils.logError(projectRoot, 'expo', 'We noticed your tunnel is having issues. ' + 'This may be due to intermittent problems with our tunnel provider. ' + 'If you have trouble connecting to your app, try to Restart the project, ' + 'or switch Host to LAN.');
|
|
} else if (status === 'connected') {
|
|
_internal().ProjectUtils.logInfo(projectRoot, 'expo', 'Tunnel connected.');
|
|
}
|
|
}
|
|
//# sourceMappingURL=ngrok.js.map
|