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
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
|