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.

356 lines
12 KiB

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getBundleUrlAsync = getBundleUrlAsync;
exports.getExpoGoConfig = getExpoGoConfig;
exports.getManifestHandler = getManifestHandler;
exports.getManifestResponseAsync = getManifestResponseAsync;
exports.getPackagerOptionsAsync = getPackagerOptionsAsync;
exports.getSignedManifestStringAsync = getSignedManifestStringAsync;
exports.getUnsignedManifestString = getUnsignedManifestString;
exports.stripPort = stripPort;
function _config() {
const data = require("@expo/config");
_config = function () {
return data;
};
return data;
}
function _chalk() {
const data = _interopRequireDefault(require("chalk"));
_chalk = function () {
return data;
};
return data;
}
function _os() {
const data = _interopRequireDefault(require("os"));
_os = function () {
return data;
};
return data;
}
function _url() {
const data = require("url");
_url = function () {
return data;
};
return data;
}
function _internal() {
const data = require("../internal");
_internal = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const _cachedSignedManifest = {
manifestString: null,
signedManifest: null
};
const blacklistedEnvironmentVariables = new Set(['EXPO_APPLE_PASSWORD', 'EXPO_ANDROID_KEY_PASSWORD', 'EXPO_ANDROID_KEYSTORE_PASSWORD', 'EXPO_IOS_DIST_P12_PASSWORD', 'EXPO_IOS_PUSH_P12_PASSWORD', 'EXPO_CLI_PASSWORD']);
function shouldExposeEnvironmentVariableInManifest(key) {
if (blacklistedEnvironmentVariables.has(key.toUpperCase())) {
return false;
}
return key.startsWith('REACT_NATIVE_') || key.startsWith('EXPO_');
}
function stripPort(host) {
if (!host) {
return host;
}
return new (_url().URL)('/', `http://${host}`).hostname;
}
async function getPackagerOptionsAsync(projectRoot) {
// Get packager opts and then copy into bundleUrlPackagerOpts
const projectSettings = await _internal().ProjectSettings.readAsync(projectRoot);
const bundleUrlPackagerOpts = JSON.parse(JSON.stringify(projectSettings));
bundleUrlPackagerOpts.urlType = 'http';
if (bundleUrlPackagerOpts.hostType === 'redirect') {
bundleUrlPackagerOpts.hostType = 'tunnel';
}
return [projectSettings, bundleUrlPackagerOpts];
}
async function getBundleUrlAsync({
projectRoot,
platform,
projectSettings,
bundleUrlPackagerOpts,
mainModuleName,
hostname
}) {
const queryParams = _internal().UrlUtils.constructBundleQueryParams(projectRoot, projectSettings);
const path = `/${encodeURI(mainModuleName)}.bundle?platform=${encodeURIComponent(platform)}&${queryParams}`;
return (await _internal().UrlUtils.constructBundleUrlAsync(projectRoot, bundleUrlPackagerOpts, hostname)) + path;
}
function getPlatformFromRequest(headers) {
return (headers['exponent-platform'] || 'ios').toString();
}
function getManifestHandler(projectRoot) {
return async (req, res, next) => {
// Only support `/`, `/manifest`, `/index.exp` for the manifest middleware.
if (!req.url || !['/', '/manifest', '/index.exp'].includes(
// Strip the query params
(0, _url().parse)(req.url).pathname || req.url)) {
next();
return;
}
try {
var _exp$sdkVersion;
// We intentionally don't `await`. We want to continue trying even
// if there is a potential error in the package.json and don't want to slow
// down the request
_internal().Doctor.validateWithNetworkAsync(projectRoot).catch(error => {
_internal().ProjectUtils.logError(projectRoot, 'expo', `Error: could not load config json at ${projectRoot}: ${error.toString()}`, 'doctor-config-json-not-read');
});
const {
manifestString,
exp,
hostInfo
} = await getManifestResponseFromHeadersAsync({
projectRoot,
headers: req.headers
});
const sdkVersion = (_exp$sdkVersion = exp.sdkVersion) !== null && _exp$sdkVersion !== void 0 ? _exp$sdkVersion : null;
// Send the response
res.setHeader('Exponent-Server', JSON.stringify(hostInfo));
// End the request
res.end(manifestString);
// Log analytics
_internal().Analytics.logEvent('Serve Manifest', {
developerTool: _internal().Config.developerTool,
sdkVersion
});
} catch (e) {
_internal().ProjectUtils.logError(projectRoot, 'expo', e.stack);
// 5xx = Server Error HTTP code
res.statusCode = 520;
res.end(JSON.stringify({
error: e.toString()
}));
}
try {
const deviceIds = req.headers['expo-dev-client-id'];
if (deviceIds) {
await _internal().ProjectSettings.saveDevicesAsync(projectRoot, deviceIds);
}
} catch (e) {
_internal().ProjectUtils.logError(projectRoot, 'expo', e.stack);
}
};
}
async function getManifestResponseFromHeadersAsync({
projectRoot,
headers
}) {
// Read from headers
const platform = getPlatformFromRequest(headers);
const acceptSignature = headers['exponent-accept-signature'];
return getManifestResponseAsync({
projectRoot,
host: headers.host,
platform,
acceptSignature
});
}
async function getExpoGoConfig({
projectRoot,
projectSettings,
mainModuleName,
hostname
}) {
const [debuggerHost, logUrl] = await Promise.all([_internal().UrlUtils.constructDebuggerHostAsync(projectRoot, hostname), _internal().UrlUtils.constructLogUrlAsync(projectRoot, hostname)]);
return {
developer: {
tool: _internal().Config.developerTool,
projectRoot
},
packagerOpts: projectSettings,
mainModuleName,
// Add this string to make Flipper register React Native / Metro as "running".
// Can be tested by running:
// `METRO_SERVER_PORT=19000 open -a flipper.app`
// Where 19000 is the port where the Expo project is being hosted.
__flipperHack: 'React Native packager is running',
debuggerHost,
logUrl
};
}
async function getManifestResponseAsync({
projectRoot,
host,
platform,
acceptSignature
}) {
// Read the config
const projectConfig = (0, _config().getConfig)(projectRoot, {
skipSDKVersionRequirement: true
});
// Opt towards newest functionality when expo isn't installed.
if (!projectConfig.exp.sdkVersion) {
projectConfig.exp.sdkVersion = 'UNVERSIONED';
}
// Read from headers
const hostname = stripPort(host);
// Get project entry point and initial module
let entryPoint = (0, _internal().resolveEntryPoint)(projectRoot, platform, projectConfig);
// NOTE(Bacon): Webpack is currently hardcoded to index.bundle on native
// in the future (TODO) we should move this logic into a Webpack plugin and use
// a generated file name like we do on web.
if (_internal().Webpack.isTargetingNative()) {
entryPoint = 'index.js';
}
const mainModuleName = _internal().UrlUtils.stripJSExtension(entryPoint);
// Gather packager and host info
const hostInfo = await createHostInfoAsync();
const [projectSettings, bundleUrlPackagerOpts] = await getPackagerOptionsAsync(projectRoot);
// Create the manifest and set fields within it
const expoGoConfig = await getExpoGoConfig({
projectRoot,
projectSettings,
mainModuleName,
hostname
});
const hostUri = await _internal().UrlUtils.constructHostUriAsync(projectRoot, hostname);
const manifest = {
...projectConfig.exp,
...expoGoConfig,
hostUri
};
// Adding the env variables to the Expo manifest is unsafe.
// This feature is deprecated in SDK 41 forward.
if (manifest.sdkVersion && _internal().Versions.lteSdkVersion(manifest, '40.0.0')) {
manifest.env = getManifestEnvironment();
}
// Add URLs to the manifest
manifest.bundleUrl = await getBundleUrlAsync({
projectRoot,
platform,
projectSettings,
bundleUrlPackagerOpts,
mainModuleName,
hostname
});
// Resolve all assets and set them on the manifest as URLs
await _internal().ProjectAssets.resolveManifestAssets({
projectRoot,
manifest,
async resolver(path) {
if (_internal().Webpack.isTargetingNative()) {
// When using our custom dev server, just do assets normally
// without the `assets/` subpath redirect.
return (0, _url().resolve)(manifest.bundleUrl.match(/^https?:\/\/.*?\//)[0], path);
}
return manifest.bundleUrl.match(/^https?:\/\/.*?\//)[0] + 'assets/' + path;
}
});
// The server normally inserts this but if we're offline we'll do it here
await _internal().ProjectAssets.resolveGoogleServicesFile(projectRoot, manifest);
// Create the final string
let manifestString;
try {
manifestString = await getManifestStringAsync(manifest, hostInfo.host, acceptSignature);
} catch (error) {
if (error.code === 'UNAUTHORIZED_ERROR' && manifest.owner) {
// Don't have permissions for siging, warn and enable offline mode.
addSigningDisabledWarning(projectRoot, `This project belongs to ${_chalk().default.bold(`@${manifest.owner}`)} and you have not been granted the appropriate permissions.\n` + `Please request access from an admin of @${manifest.owner} or change the "owner" field to an account you belong to.\n` + (0, _internal().learnMore)('https://docs.expo.dev/versions/latest/config/app/#owner'));
_internal().ConnectionStatus.setIsOffline(true);
manifestString = await getManifestStringAsync(manifest, hostInfo.host, acceptSignature);
} else if (error.code === 'ENOTFOUND') {
// Got a DNS error, i.e. can't access exp.host, warn and enable offline mode.
addSigningDisabledWarning(projectRoot, `Could not reach Expo servers, please check if you can access ${error.hostname || 'exp.host'}.`);
_internal().ConnectionStatus.setIsOffline(true);
manifestString = await getManifestStringAsync(manifest, hostInfo.host, acceptSignature);
} else {
throw error;
}
}
return {
manifestString,
exp: manifest,
hostInfo
};
}
const addSigningDisabledWarning = (() => {
let seen = false;
return (projectRoot, reason) => {
if (!seen) {
seen = true;
_internal().ProjectUtils.logWarning(projectRoot, 'expo', `${reason}\nFalling back to offline mode.`, 'signing-disabled');
}
};
})();
function getManifestEnvironment() {
return Object.keys(process.env).reduce((prev, key) => {
if (shouldExposeEnvironmentVariableInManifest(key)) {
prev[key] = process.env[key];
}
return prev;
}, {});
}
async function getManifestStringAsync(manifest, hostUUID, acceptSignature) {
const currentSession = await _internal().UserManager.getSessionAsync();
if (!currentSession || _internal().ConnectionStatus.isOffline()) {
manifest.id = `@${_internal().ANONYMOUS_USERNAME}/${manifest.slug}-${hostUUID}`;
}
if (!acceptSignature) {
return JSON.stringify(manifest);
} else if (!currentSession || _internal().ConnectionStatus.isOffline()) {
return getUnsignedManifestString(manifest);
} else {
return await getSignedManifestStringAsync(manifest, currentSession);
}
}
async function createHostInfoAsync() {
const host = await _internal().UserSettings.getAnonymousIdentifierAsync();
return {
host,
server: 'xdl',
serverVersion: require('xdl/package.json').version,
serverDriver: _internal().Config.developerTool,
serverOS: _os().default.platform(),
serverOSVersion: _os().default.release()
};
}
async function getSignedManifestStringAsync(manifest,
// NOTE: we currently ignore the currentSession that is passed in, see the note below about analytics.
currentSession) {
var _manifest$owner;
const manifestString = JSON.stringify(manifest);
if (_cachedSignedManifest.manifestString === manifestString) {
return _cachedSignedManifest.signedManifest;
}
// WARNING: Removing the following line will regress analytics, see: https://github.com/expo/expo-cli/pull/2357
// TODO: make this more obvious from code
const user = await _internal().UserManager.ensureLoggedInAsync();
const {
response
} = await _internal().ApiV2.clientForUser(user).postAsync('manifest/sign', {
args: {
remoteUsername: (_manifest$owner = manifest.owner) !== null && _manifest$owner !== void 0 ? _manifest$owner : await _internal().UserManager.getCurrentUsernameAsync(),
remotePackageName: manifest.slug
},
manifest: manifest
});
_cachedSignedManifest.manifestString = manifestString;
_cachedSignedManifest.signedManifest = response;
return response;
}
function getUnsignedManifestString(manifest) {
const unsignedManifest = {
manifestString: JSON.stringify(manifest),
signature: 'UNSIGNED'
};
return JSON.stringify(unsignedManifest);
}
//# sourceMappingURL=ManifestHandler.js.map