UNPKG

xdl

Version:

The Expo Development Library

1,216 lines (1,060 loc) 71.2 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.stopAsync = exports.startAsync = exports.getUrlAsync = exports.setOptionsAsync = exports.stopTunnelsAsync = exports.startTunnelsAsync = exports.stopExpoServerAsync = exports.startExpoServerAsync = exports.stopReactNativeServerAsync = exports.startReactNativeServerAsync = exports.buildAsync = exports.publishAsync = exports.getSlugAsync = exports.getManifestUrlWithFallbackAsync = exports.currentStatus = undefined; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; let currentStatus = exports.currentStatus = (() => { var _ref = _asyncToGenerator(function* (projectDir) { const manifestUrl = yield (_UrlUtils || _load_UrlUtils()).constructManifestUrlAsync(projectDir, { urlType: 'http' }); const packagerUrl = yield (_UrlUtils || _load_UrlUtils()).constructBundleUrlAsync(projectDir, { urlType: 'http' }); let packagerRunning = false; try { const res = yield (_request || _load_request()).default.promise(`${packagerUrl}/status`); if (res.statusCode < 400 && res.body && res.body.includes('packager-status:running')) { packagerRunning = true; } } catch (e) {} let manifestServerRunning = false; try { const res = yield (_request || _load_request()).default.promise(manifestUrl); if (res.statusCode < 400) { manifestServerRunning = true; } } catch (e) {} if (packagerRunning && manifestServerRunning) { return 'running'; } else if (packagerRunning || manifestServerRunning) { return 'ill'; } else { return 'exited'; } }); return function currentStatus(_x) { return _ref.apply(this, arguments); }; })(); let _areTunnelsHealthy = (() => { var _ref2 = _asyncToGenerator(function* (projectRoot) { const packagerInfo = yield (_ProjectSettings || _load_ProjectSettings()).readPackagerInfoAsync(projectRoot); if (!packagerInfo.packagerNgrokUrl || !packagerInfo.expoServerNgrokUrl) { return false; } const status = yield currentStatus(projectRoot); return status === 'running'; }); return function _areTunnelsHealthy(_x2) { return _ref2.apply(this, arguments); }; })(); let getManifestUrlWithFallbackAsync = exports.getManifestUrlWithFallbackAsync = (() => { var _ref3 = _asyncToGenerator(function* (projectRoot) { const projectSettings = yield (_ProjectSettings || _load_ProjectSettings()).readAsync(projectRoot); if (projectSettings.hostType === 'tunnel' && !(_Config || _load_Config()).default.offline && !(yield _areTunnelsHealthy(projectRoot))) { // Fall back to LAN URL if tunnels are not available. return { url: yield (_UrlUtils || _load_UrlUtils()).constructManifestUrlAsync(projectRoot, { hostType: 'lan' }), isUrlFallback: true }; } else { return { url: yield (_UrlUtils || _load_UrlUtils()).constructManifestUrlAsync(projectRoot), isUrlFallback: false }; } }); return function getManifestUrlWithFallbackAsync(_x3) { return _ref3.apply(this, arguments); }; })(); let _assertValidProjectRoot = (() => { var _ref4 = _asyncToGenerator(function* (projectRoot) { if (!projectRoot) { throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.NO_PROJECT_ROOT, 'No project root specified'); } }); return function _assertValidProjectRoot(_x4) { return _ref4.apply(this, arguments); }; })(); let _getFreePortAsync = (() => { var _ref5 = _asyncToGenerator(function* (rangeStart) { let port = yield (0, (_freeportAsync || _load_freeportAsync()).default)(rangeStart); if (!port) { throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.NO_PORT_FOUND, 'No available port found'); } return port; }); return function _getFreePortAsync(_x5) { return _ref5.apply(this, arguments); }; })(); let _getForPlatformAsync = (() => { var _ref6 = _asyncToGenerator(function* (projectRoot, url, platform, { errorCode, minLength }) { url = (_UrlUtils || _load_UrlUtils()).getPlatformSpecificBundleUrl(url, platform); let fullUrl = `${url}&platform=${platform}`; let response = yield (_request || _load_request()).default.promise.get({ url: fullUrl, headers: { 'Exponent-Platform': platform } }); if (response.statusCode !== 200) { if (response.body) { let body; try { body = JSON.parse(response.body); } catch (e) { (_ProjectUtils || _load_ProjectUtils()).logError(projectRoot, 'expo', response.body); } if (body !== undefined) { if (body.message) { (_ProjectUtils || _load_ProjectUtils()).logError(projectRoot, 'expo', body.message); } else { (_ProjectUtils || _load_ProjectUtils()).logError(projectRoot, 'expo', response.body); } } } throw new (_XDLError || _load_XDLError()).default(errorCode, `Packager URL ${fullUrl} returned unexpected code ${response.statusCode}. Please open your project in the Expo app and see if there are any errors. Also scroll up and make sure there were no errors or warnings when opening your project.`); } if (!response.body || minLength && response.body.length < minLength) { throw new (_XDLError || _load_XDLError()).default(errorCode, `Body is: ${response.body}`); } return response.body; }); return function _getForPlatformAsync(_x6, _x7, _x8, _x9) { return _ref6.apply(this, arguments); }; })(); let _resolveManifestAssets = (() => { var _ref7 = _asyncToGenerator(function* (projectRoot, manifest, resolver, strict = false) { try { // Asset fields that the user has set const assetSchemas = (yield (_ExpSchema || _load_ExpSchema()).getAssetSchemasAsync(manifest.sdkVersion)).filter(function ({ fieldPath }) { return (_lodash || _load_lodash()).default.get(manifest, fieldPath); }); // Get the URLs const urls = yield Promise.all(assetSchemas.map((() => { var _ref8 = _asyncToGenerator(function* ({ fieldPath }) { const pathOrURL = (_lodash || _load_lodash()).default.get(manifest, fieldPath); if (pathOrURL.match(/^https?:\/\/(.*)$/)) { // It's a remote URL return pathOrURL; } else if (_fs.default.existsSync(_path.default.resolve(projectRoot, pathOrURL))) { return yield resolver(pathOrURL); } else { const err = new Error('Could not resolve local asset.'); // $FlowFixMe err.localAssetPath = pathOrURL; // $FlowFixMe err.manifestField = fieldPath; throw err; } }); return function (_x13) { return _ref8.apply(this, arguments); }; })())); // Set the corresponding URL fields assetSchemas.forEach(function ({ fieldPath }, index) { return (_lodash || _load_lodash()).default.set(manifest, fieldPath + 'Url', urls[index]); }); } catch (e) { let logMethod = (_ProjectUtils || _load_ProjectUtils()).logWarning; if (strict) { logMethod = (_ProjectUtils || _load_ProjectUtils()).logError; } if (e.localAssetPath) { logMethod(projectRoot, 'expo', `Unable to resolve asset "${e.localAssetPath}" from "${e.manifestField}" in your app/exp.json.`); } else { logMethod(projectRoot, 'expo', `Warning: Unable to resolve manifest assets. Icons might not work. ${e.message}.`); } if (strict) { throw new Error('Resolving assets failed.'); } } }); return function _resolveManifestAssets(_x10, _x11, _x12) { return _ref7.apply(this, arguments); }; })(); let getSlugAsync = exports.getSlugAsync = (() => { var _ref9 = _asyncToGenerator(function* (projectRoot, options = {}) { // Verify that exp/app.json exist let { exp, pkg } = yield (_ProjectUtils || _load_ProjectUtils()).readConfigJsonAsync(projectRoot); if (!exp || !pkg) { const configName = yield (_ProjectUtils || _load_ProjectUtils()).configFilenameAsync(projectRoot); throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.NO_PACKAGE_JSON, `Couldn't read ${configName} file in project at ${projectRoot}`); } if (!exp.slug && pkg.name) { exp.slug = pkg.name; } else if (!exp.slug) { const configName = yield (_ProjectUtils || _load_ProjectUtils()).configFilenameAsync(projectRoot); throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.INVALID_MANIFEST, `${configName} in ${projectRoot} must contain the slug field`); } return exp.slug; }); return function getSlugAsync(_x14) { return _ref9.apply(this, arguments); }; })(); let publishAsync = exports.publishAsync = (() => { var _ref10 = _asyncToGenerator(function* (projectRoot, options = {}) { const user = yield (_User || _load_User()).default.ensureLoggedInAsync(); yield _validatePackagerReadyAsync(projectRoot); (_Analytics || _load_Analytics()).logEvent('Publish', { projectRoot }); // Get project config let exp = yield _getPublishExpConfigAsync(projectRoot, options); // TODO: refactor this out to a function, throw error if length doesn't match let { hooks } = exp; delete exp.hooks; let validPostPublishHooks = []; if (hooks && hooks.postPublish) { hooks.postPublish.forEach(function (hook) { let { file, config } = hook; let fn = _requireFromProject(file, projectRoot); if (fn === null) { (_Logger || _load_Logger()).default.global.error(`Unable to load postPublishHook: '${file}'`); } else { hook._fn = fn; validPostPublishHooks.push(hook); } }); if (validPostPublishHooks.length !== hooks.postPublish.length) { (_Logger || _load_Logger()).default.global.error(); throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.HOOK_INITIALIZATION_ERROR, 'Please fix your postPublish hook configuration.'); } } let { iosBundle, androidBundle } = yield _buildPublishBundlesAsync(projectRoot); yield _fetchAndUploadAssetsAsync(projectRoot, exp); let { iosSourceMap, androidSourceMap } = yield _maybeBuildSourceMapsAsync(projectRoot, exp, { force: validPostPublishHooks.length }); let response; try { response = yield _uploadArtifactsAsync({ exp, iosBundle, androidBundle, options }); } catch (e) { if (e.serverError === 'SCHEMA_VALIDATION_ERROR') { throw new Error(`There was an error validating your project schema. Check for any warnings about the contents of your app/exp.json.`); } throw new Error(`There was an error publishing your project. Please check for any warnings.`); } yield _maybeWriteArtifactsToDiskAsync({ exp, projectRoot, iosBundle, androidBundle, iosSourceMap, androidSourceMap }); if (validPostPublishHooks.length || exp.ios && exp.ios.publishManifestPath || exp.android && exp.android.publishManifestPath) { let [androidManifest, iosManifest] = yield Promise.all([(_ExponentTools || _load_ExponentTools()).getManifestAsync(response.url, { 'Exponent-SDK-Version': exp.sdkVersion, 'Exponent-Platform': 'android', 'Expo-Release-Channel': options.releaseChannel }), (_ExponentTools || _load_ExponentTools()).getManifestAsync(response.url, { 'Exponent-SDK-Version': exp.sdkVersion, 'Exponent-Platform': 'ios', 'Expo-Release-Channel': options.releaseChannel })]); const hookOptions = { url: response.url, exp, iosBundle, iosSourceMap, iosManifest, androidBundle, androidSourceMap, androidManifest, projectRoot, log: function (msg) { (_Logger || _load_Logger()).default.global.info({ quiet: true }, msg); } }; for (let hook of validPostPublishHooks) { (_Logger || _load_Logger()).default.global.info(`Running postPublish hook: ${hook.file}`); try { let result = hook._fn(_extends({ config: hook.config }, hookOptions)); // If it's a promise, wait for it to resolve if (result && result.then) { result = yield result; } if (result) { (_Logger || _load_Logger()).default.global.info({ quiet: true }, result); } } catch (e) { (_Logger || _load_Logger()).default.global.warn(`Warning: postPublish hook '${hook.file}' failed: ${e.stack}`); } } if (exp.ios && exp.ios.publishManifestPath) { yield _writeArtifactSafelyAsync(projectRoot, 'ios.publishManifestPath', exp.ios.publishManifestPath, JSON.stringify(iosManifest)); } if (exp.android && exp.android.publishManifestPath) { yield _writeArtifactSafelyAsync(projectRoot, 'android.publishManifestPath', exp.android.publishManifestPath, JSON.stringify(androidManifest)); } } // TODO: move to postPublish hook if (exp.isKernel) { yield _handleKernelPublishedAsync({ user, exp, projectRoot, url: response.url }); } return response; }); return function publishAsync(_x15) { return _ref10.apply(this, arguments); }; })(); let _uploadArtifactsAsync = (() => { var _ref11 = _asyncToGenerator(function* ({ exp, iosBundle, androidBundle, options }) { (_Logger || _load_Logger()).default.global.info('Uploading JavaScript bundles'); let formData = new (_FormData || _load_FormData()).default(); formData.append('expJson', JSON.stringify(exp)); formData.append('iosBundle', _createBlob(iosBundle), 'iosBundle'); formData.append('androidBundle', _createBlob(androidBundle), 'androidBundle'); formData.append('options', JSON.stringify(options)); let response = yield (_Api || _load_Api()).default.callMethodAsync('publish', null, 'put', null, { formData }); return response; }); return function _uploadArtifactsAsync(_x16) { return _ref11.apply(this, arguments); }; })(); let _validatePackagerReadyAsync = (() => { var _ref12 = _asyncToGenerator(function* (projectRoot) { _assertValidProjectRoot(projectRoot); // Ensure the packager is started let packagerInfo = yield (_ProjectSettings || _load_ProjectSettings()).readPackagerInfoAsync(projectRoot); if (!packagerInfo.packagerPort) { throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.NO_PACKAGER_PORT, `No packager found for project at ${projectRoot}.`); } }); return function _validatePackagerReadyAsync(_x17) { return _ref12.apply(this, arguments); }; })(); let _getPublishExpConfigAsync = (() => { var _ref13 = _asyncToGenerator(function* (projectRoot, options) { let schema = (_joi || _load_joi()).default.object().keys({ releaseChannel: (_joi || _load_joi()).default.string() }); // Validate schema try { yield (_joi || _load_joi()).default.promise.validate(options, schema); options.releaseChannel = options.releaseChannel || 'default'; // joi default not enforcing this :/ } catch (e) { throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.INVALID_OPTIONS, e.toString()); } // Verify that exp/app.json and package.json exist let { exp, pkg } = yield (_ProjectUtils || _load_ProjectUtils()).readConfigJsonAsync(projectRoot); if (!exp || !pkg) { const configName = yield (_ProjectUtils || _load_ProjectUtils()).configFilenameAsync(projectRoot); throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.NO_PACKAGE_JSON, `Couldn't read ${configName} file in project at ${projectRoot}`); } // Support version and name being specified in package.json for legacy // support pre: exp.json if (!exp.version && pkg.version) { exp.version = pkg.version; } if (!exp.slug && pkg.name) { exp.slug = pkg.name; } if (exp.android && exp.android.config) { delete exp.android.config; } if (exp.ios && exp.ios.config) { delete exp.ios.config; } // Only allow projects to be published with UNVERSIONED if a correct token is set in env if (exp.sdkVersion === 'UNVERSIONED' && !process.env['EXPO_SKIP_MANIFEST_VALIDATION_TOKEN']) { throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.INVALID_OPTIONS, 'Cannot publish with sdkVersion UNVERSIONED.'); } return exp; }); return function _getPublishExpConfigAsync(_x18, _x19) { return _ref13.apply(this, arguments); }; })(); // Fetch iOS and Android bundles for publishing let _buildPublishBundlesAsync = (() => { var _ref14 = _asyncToGenerator(function* (projectRoot) { let entryPoint = yield (_Exp || _load_Exp()).determineEntryPointAsync(projectRoot); let publishUrl = yield (_UrlUtils || _load_UrlUtils()).constructPublishUrlAsync(projectRoot, entryPoint); (_Logger || _load_Logger()).default.global.info('Building iOS bundle'); let iosBundle = yield _getForPlatformAsync(projectRoot, publishUrl, 'ios', { errorCode: (_ErrorCode || _load_ErrorCode()).default.INVALID_BUNDLE, minLength: MINIMUM_BUNDLE_SIZE }); (_Logger || _load_Logger()).default.global.info('Building Android bundle'); let androidBundle = yield _getForPlatformAsync(projectRoot, publishUrl, 'android', { errorCode: (_ErrorCode || _load_ErrorCode()).default.INVALID_BUNDLE, minLength: MINIMUM_BUNDLE_SIZE }); return { iosBundle, androidBundle }; }); return function _buildPublishBundlesAsync(_x20) { return _ref14.apply(this, arguments); }; })(); // note(brentvatne): currently we build source map anytime there is a // postPublish hook -- we may have an option in the future to manually // enable sourcemap building, but for now it's very fast, most apps in // production should use sourcemaps for error reporting, and in the worst // case, adding a few seconds to a postPublish hook isn't too annoying let _maybeBuildSourceMapsAsync = (() => { var _ref15 = _asyncToGenerator(function* (projectRoot, exp, options = {}) { if (!options.force) { return { iosSourceMap: null, androidSourceMap: null }; } let entryPoint = yield (_Exp || _load_Exp()).determineEntryPointAsync(projectRoot); let sourceMapUrl = yield (_UrlUtils || _load_UrlUtils()).constructSourceMapUrlAsync(projectRoot, entryPoint); (_Logger || _load_Logger()).default.global.info('Building sourcemaps'); let iosSourceMap = yield _getForPlatformAsync(projectRoot, sourceMapUrl, 'ios', { errorCode: (_ErrorCode || _load_ErrorCode()).default.INVALID_BUNDLE, minLength: MINIMUM_BUNDLE_SIZE }); let androidSourceMap = yield _getForPlatformAsync(projectRoot, sourceMapUrl, 'android', { errorCode: (_ErrorCode || _load_ErrorCode()).default.INVALID_BUNDLE, minLength: MINIMUM_BUNDLE_SIZE }); return { iosSourceMap, androidSourceMap }; }); return function _maybeBuildSourceMapsAsync(_x21, _x22) { return _ref15.apply(this, arguments); }; })(); let _fetchAndUploadAssetsAsync = (() => { var _ref16 = _asyncToGenerator(function* (projectRoot, exp) { (_Logger || _load_Logger()).default.global.info('Analyzing assets'); let entryPoint = yield (_Exp || _load_Exp()).determineEntryPointAsync(projectRoot); let assetsUrl = yield (_UrlUtils || _load_UrlUtils()).constructAssetsUrlAsync(projectRoot, entryPoint); let iosAssetsJson = yield _getForPlatformAsync(projectRoot, assetsUrl, 'ios', { errorCode: (_ErrorCode || _load_ErrorCode()).default.INVALID_ASSETS }); let androidAssetsJson = yield _getForPlatformAsync(projectRoot, assetsUrl, 'android', { errorCode: (_ErrorCode || _load_ErrorCode()).default.INVALID_ASSETS }); // Resolve manifest assets to their S3 URL and add them to the list of assets to // be uploaded const manifestAssets = []; yield _resolveManifestAssets(projectRoot, exp, (() => { var _ref17 = _asyncToGenerator(function* (assetPath) { const absolutePath = _path.default.resolve(projectRoot, assetPath); const contents = yield _fs.default.promise.readFile(absolutePath); const hash = (0, (_md5hex || _load_md5hex()).default)(contents); manifestAssets.push({ files: [absolutePath], fileHashes: [hash] }); return 'https://d1wp6m56sqw74a.cloudfront.net/~assets/' + hash; }); return function (_x25) { return _ref17.apply(this, arguments); }; })(), true); (_Logger || _load_Logger()).default.global.info('Uploading assets'); // Upload asset files const iosAssets = JSON.parse(iosAssetsJson); const androidAssets = JSON.parse(androidAssetsJson); const assets = iosAssets.concat(androidAssets).concat(manifestAssets); if (assets.length > 0 && assets[0].fileHashes) { yield uploadAssetsAsync(projectRoot, assets); } else { (_Logger || _load_Logger()).default.global.info({ quiet: true }, 'No assets to upload, skipped.'); } return exp; }); return function _fetchAndUploadAssetsAsync(_x23, _x24) { return _ref16.apply(this, arguments); }; })(); let _writeArtifactSafelyAsync = (() => { var _ref18 = _asyncToGenerator(function* (projectRoot, keyName, artifactPath, artifact) { const pathToWrite = _path.default.resolve(projectRoot, artifactPath); if (!_fs.default.existsSync(_path.default.dirname(pathToWrite))) { (_Logger || _load_Logger()).default.global.warn(`app.json specifies ${keyName}: ${pathToWrite}, but that directory does not exist.`); } else { yield _fs.default.promise.writeFile(pathToWrite, artifact); } return; }); return function _writeArtifactSafelyAsync(_x26, _x27, _x28, _x29) { return _ref18.apply(this, arguments); }; })(); let _maybeWriteArtifactsToDiskAsync = (() => { var _ref19 = _asyncToGenerator(function* ({ exp, projectRoot, iosBundle, androidBundle, iosSourceMap, androidSourceMap }) { if (exp.android && exp.android.publishBundlePath) { yield _writeArtifactSafelyAsync(projectRoot, 'android.publishBundlePath', exp.android.publishBundlePath, androidBundle); } if (exp.ios && exp.ios.publishBundlePath) { yield _writeArtifactSafelyAsync(projectRoot, 'ios.publishBundlePath', exp.ios.publishBundlePath, iosBundle); } if (exp.android && exp.android.publishSourceMapPath) { yield _writeArtifactSafelyAsync(projectRoot, 'android.publishSourceMapPath', exp.android.publishSourceMapPath, androidSourceMap); } if (exp.ios && exp.ios.publishSourceMapPath) { yield _writeArtifactSafelyAsync(projectRoot, 'ios.publishSourceMapPath', exp.ios.publishSourceMapPath, iosSourceMap); } }); return function _maybeWriteArtifactsToDiskAsync(_x30) { return _ref19.apply(this, arguments); }; })(); let _handleKernelPublishedAsync = (() => { var _ref20 = _asyncToGenerator(function* ({ projectRoot, user, exp, url }) { let kernelBundleUrl = `${(_Config || _load_Config()).default.api.scheme}://${(_Config || _load_Config()).default.api.host}`; if ((_Config || _load_Config()).default.api.port) { kernelBundleUrl = `${kernelBundleUrl}:${(_Config || _load_Config()).default.api.port}`; } kernelBundleUrl = `${kernelBundleUrl}/@${user.username}/${exp.slug}/bundle`; if (exp.kernel.androidManifestPath) { let manifest = yield (_ExponentTools || _load_ExponentTools()).getManifestAsync(url, { 'Exponent-SDK-Version': exp.sdkVersion, 'Exponent-Platform': 'android' }); manifest.bundleUrl = kernelBundleUrl; manifest.sdkVersion = 'UNVERSIONED'; yield _fs.default.promise.writeFile(_path.default.resolve(projectRoot, exp.kernel.androidManifestPath), JSON.stringify(manifest)); } if (exp.kernel.iosManifestPath) { let manifest = yield (_ExponentTools || _load_ExponentTools()).getManifestAsync(url, { 'Exponent-SDK-Version': exp.sdkVersion, 'Exponent-Platform': 'ios' }); manifest.bundleUrl = kernelBundleUrl; manifest.sdkVersion = 'UNVERSIONED'; yield _fs.default.promise.writeFile(_path.default.resolve(projectRoot, exp.kernel.iosManifestPath), JSON.stringify(manifest)); } }); return function _handleKernelPublishedAsync(_x31) { return _ref20.apply(this, arguments); }; })(); // TODO(jesse): Add analytics for upload let uploadAssetsAsync = (() => { var _ref21 = _asyncToGenerator(function* (projectRoot, assets) { // Collect paths by key, also effectively handles duplicates in the array const paths = {}; assets.forEach(function (asset) { asset.files.forEach(function (path, index) { paths[asset.fileHashes[index]] = path; }); }); // Collect list of assets missing on host const metas = (yield (_Api || _load_Api()).default.callMethodAsync('assetsMetadata', [], 'post', { keys: Object.keys(paths) })).metadata; const missing = Object.keys(paths).filter(function (key) { return !metas[key].exists; }); if (missing.length === 0) { (_Logger || _load_Logger()).default.global.info({ quiet: true }, `No assets changed, skipped.`); } // Upload them! yield Promise.all((_lodash || _load_lodash()).default.chunk(missing, 5).map((() => { var _ref22 = _asyncToGenerator(function* (keys) { let formData = new (_FormData || _load_FormData()).default(); for (const key of keys) { (_ProjectUtils || _load_ProjectUtils()).logDebug(projectRoot, 'expo', `uploading ${paths[key]}`); let relativePath = paths[key].replace(projectRoot, ''); (_Logger || _load_Logger()).default.global.info({ quiet: true }, `Uploading ${relativePath}`); formData.append(key, (yield _readFileForUpload(paths[key])), paths[key]); } yield (_Api || _load_Api()).default.callMethodAsync('uploadAssets', [], 'put', null, { formData }); }); return function (_x34) { return _ref22.apply(this, arguments); }; })())); }); return function uploadAssetsAsync(_x32, _x33) { return _ref21.apply(this, arguments); }; })(); let _readFileForUpload = (() => { var _ref23 = _asyncToGenerator(function* (path) { if ((0, (_EnvironmentHelper || _load_EnvironmentHelper()).isNode)()) { return _fs.default.createReadStream(path); } else { // $FlowFixMe: fs.promise property not found. const data = yield _fs.default.promise.readFile(path); return new Blob([data]); } }); return function _readFileForUpload(_x35) { return _ref23.apply(this, arguments); }; })(); let buildAsync = exports.buildAsync = (() => { var _ref24 = _asyncToGenerator(function* (projectRoot, options = {}) { yield (_User || _load_User()).default.ensureLoggedInAsync(); _assertValidProjectRoot(projectRoot); (_Analytics || _load_Analytics()).logEvent('Build Shell App', { projectRoot }); let schema = (_joi || _load_joi()).default.object().keys({ current: (_joi || _load_joi()).default.boolean(), mode: (_joi || _load_joi()).default.string(), platform: (_joi || _load_joi()).default.any().valid('ios', 'android', 'all'), expIds: (_joi || _load_joi()).default.array(), type: (_joi || _load_joi()).default.any().valid('archive', 'simulator'), releaseChannel: (_joi || _load_joi()).default.string().regex(/[a-z\d][a-z\d._-]*/) }); try { yield (_joi || _load_joi()).default.promise.validate(options, schema); } catch (e) { throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.INVALID_OPTIONS, e.toString()); } let { exp, pkg } = yield (_ProjectUtils || _load_ProjectUtils()).readConfigJsonAsync(projectRoot); const configName = yield (_ProjectUtils || _load_ProjectUtils()).configFilenameAsync(projectRoot); const configPrefix = configName === 'app.json' ? 'expo.' : ''; if (!exp || !pkg) { throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.NO_PACKAGE_JSON, `Couldn't read ${configName} file in project at ${projectRoot}`); } // Support version and name being specified in package.json for legacy // support pre: exp.json if (!exp.version && pkg.version) { exp.version = pkg.version; } if (!exp.slug && pkg.name) { exp.slug = pkg.name; } if (options.platform === 'ios' || options.platform === 'all') { if (!exp.ios || !exp.ios.bundleIdentifier) { throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.INVALID_MANIFEST, `Must specify a bundle identifier in order to build this experience for iOS. Please specify one in ${configName} at "${configPrefix}ios.bundleIdentifier"`); } } if (options.platform === 'android' || options.platform === 'all') { if (!exp.android || !exp.android.package) { throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.INVALID_MANIFEST, `Must specify a java package in order to build this experience for Android. Please specify one in ${configName} at "${configPrefix}android.package"`); } } let response = yield (_Api || _load_Api()).default.callMethodAsync('build', [], 'put', { manifest: exp, options }); return response; }); return function buildAsync(_x36) { return _ref24.apply(this, arguments); }; })(); let _waitForRunningAsync = (() => { var _ref25 = _asyncToGenerator(function* (url) { try { let response = yield (_request || _load_request()).default.promise(url); // Looking for "Cached Bundles" string is hacky, but unfortunately // ngrok returns a 200 when it succeeds but the port it's proxying // isn't bound. if (response.statusCode >= 200 && response.statusCode < 300 && response.body && response.body.includes('packager-status:running')) { return true; } } catch (e) { // Try again after delay } yield (0, (_delayAsync || _load_delayAsync()).default)(100); return _waitForRunningAsync(url); }); return function _waitForRunningAsync(_x37) { return _ref25.apply(this, arguments); }; })(); let _restartWatchmanAsync = (() => { var _ref26 = _asyncToGenerator(function* (projectRoot) { try { let result = yield (0, (_spawnAsync || _load_spawnAsync()).default)('watchman', ['watch-del', projectRoot]); yield (0, (_spawnAsync || _load_spawnAsync()).default)('watchman', ['watch-project', projectRoot]); if (result.stdout.includes('root')) { (_ProjectUtils || _load_ProjectUtils()).logInfo(projectRoot, 'expo', 'Restarted watchman.'); return; } } catch (e) {} (_ProjectUtils || _load_ProjectUtils()).logError(projectRoot, 'expo', 'Attempted to restart watchman but failed. Please try running `watchman watch-del-all`.'); }); return function _restartWatchmanAsync(_x38) { return _ref26.apply(this, arguments); }; })(); let startReactNativeServerAsync = exports.startReactNativeServerAsync = (() => { var _ref27 = _asyncToGenerator(function* (projectRoot, options = {}, verbose = true) { yield (_User || _load_User()).default.ensureLoggedInAsync(); _assertValidProjectRoot(projectRoot); yield stopReactNativeServerAsync(projectRoot); yield (_Watchman || _load_Watchman()).addToPathAsync(); // Attempt to fix watchman if it's hanging yield (_Watchman || _load_Watchman()).unblockAndGetVersionAsync(projectRoot); let { exp } = yield (_ProjectUtils || _load_ProjectUtils()).readConfigJsonAsync(projectRoot); let packagerPort = yield _getFreePortAsync(19001); // Create packager options let nodeModulesPath = exp.nodeModulesPath ? _path.default.join(_path.default.resolve(projectRoot, exp.nodeModulesPath), 'node_modules') : _path.default.join(projectRoot, 'node_modules'); let packagerOpts = { port: packagerPort, customLogReporterPath: _path.default.join(nodeModulesPath, 'expo', 'tools', 'LogReporter'), assetExts: ['ttf'] }; if (!(_Versions || _load_Versions()).gteSdkVersion(exp, '16.0.0')) { delete packagerOpts.customLogReporterPath; } const userPackagerOpts = (_lodash || _load_lodash()).default.get(exp, 'packagerOpts'); if (userPackagerOpts) { // The RN CLI expects rn-cli.config.js's path to be absolute. We use the // project root to resolve relative paths since that was the original // behavior of the RN CLI. if (userPackagerOpts.config) { userPackagerOpts.config = _path.default.resolve(projectRoot, userPackagerOpts.config); } packagerOpts = _extends({}, packagerOpts, userPackagerOpts, userPackagerOpts.assetExts ? { assetExts: (_lodash || _load_lodash()).default.uniq([...packagerOpts.assetExts, ...userPackagerOpts.assetExts]) } : {}); } let cliOpts = (_lodash || _load_lodash()).default.reduce(packagerOpts, function (opts, val, key) { // If the packager opt value is boolean, don't set // --[opt] [value], just set '--opt' if (typeof val === 'boolean') { opts.push(`--${key}`); } else if (val && val !== '') { opts.push(`--${key}`, val); } return opts; }, ['start']); if (options.reset) { cliOpts.push('--reset-cache'); } // Get custom CLI path from project package.json, but fall back to node_module path let defaultCliPath = _path.default.join(projectRoot, 'node_modules', 'react-native', 'local-cli', 'cli.js'); const cliPath = (_lodash || _load_lodash()).default.get(exp, 'rnCliPath', defaultCliPath); let nodePath; // When using a custom path for the RN CLI, we want it to use the project // root to look up config files and Node modules if (exp.rnCliPath) { nodePath = _nodePathForProjectRoot(projectRoot); } else { nodePath = null; } (_ProjectUtils || _load_ProjectUtils()).logInfo(projectRoot, 'expo', 'Starting React Native packager...'); // Run the copy of Node that's embedded in Electron by setting the // ELECTRON_RUN_AS_NODE environment variable // Note: the CLI script sets up graceful-fs and sets ulimit to 4096 in the // child process let packagerProcess = _child_process.default.fork(cliPath, cliOpts, { cwd: projectRoot, env: _extends({}, process.env, { REACT_NATIVE_APP_ROOT: projectRoot, NODE_PATH: nodePath, ELECTRON_RUN_AS_NODE: 1 }), silent: true }); yield (_ProjectSettings || _load_ProjectSettings()).setPackagerInfoAsync(projectRoot, { packagerPort, packagerPid: packagerProcess.pid }); // TODO: do we need this? don't know if it's ever called process.on('exit', function () { (0, (_treeKill || _load_treeKill()).default)(packagerProcess.pid); }); packagerProcess.stdout.setEncoding('utf8'); packagerProcess.stderr.setEncoding('utf8'); packagerProcess.stdout.on('data', function (data) { if (verbose) { _logPackagerOutput(projectRoot, 'info', data); } }); packagerProcess.stderr.on('data', function (data) { if (verbose) { _logPackagerOutput(projectRoot, 'error', data); } }); packagerProcess.on('exit', (() => { var _ref28 = _asyncToGenerator(function* (code) { (_ProjectUtils || _load_ProjectUtils()).logDebug(projectRoot, 'expo', `packager process exited with code ${code}`); }); return function (_x40) { return _ref28.apply(this, arguments); }; })()); let packagerUrl = yield (_UrlUtils || _load_UrlUtils()).constructBundleUrlAsync(projectRoot, { urlType: 'http', hostType: 'localhost' }); yield _waitForRunningAsync(`${packagerUrl}/status`); }); return function startReactNativeServerAsync(_x39) { return _ref27.apply(this, arguments); }; })(); // Simulate the node_modules resolution // If you project dir is /Jesse/Expo/Universe/BubbleBounce, returns // "/Jesse/node_modules:/Jesse/Expo/node_modules:/Jesse/Expo/Universe/node_modules:/Jesse/Expo/Universe/BubbleBounce/node_modules" let stopReactNativeServerAsync = exports.stopReactNativeServerAsync = (() => { var _ref29 = _asyncToGenerator(function* (projectRoot) { yield (_User || _load_User()).default.ensureLoggedInAsync(); _assertValidProjectRoot(projectRoot); let packagerInfo = yield (_ProjectSettings || _load_ProjectSettings()).readPackagerInfoAsync(projectRoot); if (!packagerInfo.packagerPort || !packagerInfo.packagerPid) { (_ProjectUtils || _load_ProjectUtils()).logDebug(projectRoot, 'expo', `No packager found for project at ${projectRoot}.`); return; } (_ProjectUtils || _load_ProjectUtils()).logDebug(projectRoot, 'expo', `Killing packager process tree: ${packagerInfo.packagerPid}`); try { yield (_treeKill || _load_treeKill()).default.promise(packagerInfo.packagerPid, 'SIGKILL'); } catch (e) { (_ProjectUtils || _load_ProjectUtils()).logDebug(projectRoot, 'expo', `Error stopping packager process: ${e.toString()}`); } yield (_ProjectSettings || _load_ProjectSettings()).setPackagerInfoAsync(projectRoot, { packagerPort: null, packagerPid: null }); }); return function stopReactNativeServerAsync(_x41) { return _ref29.apply(this, arguments); }; })(); let startExpoServerAsync = exports.startExpoServerAsync = (() => { var _ref30 = _asyncToGenerator(function* (projectRoot) { yield (_User || _load_User()).default.ensureLoggedInAsync(); _assertValidProjectRoot(projectRoot); yield stopExpoServerAsync(projectRoot); let app = (0, (_express || _load_express()).default)(); app.use((_bodyParser || _load_bodyParser()).default.json({ limit: '10mb' })); app.use((_bodyParser || _load_bodyParser()).default.urlencoded({ limit: '10mb', extended: true })); if ((yield (_Doctor || _load_Doctor()).validateWithNetworkAsync(projectRoot)) === (_Doctor || _load_Doctor()).FATAL) { throw new Error(`Couldn't start project. Please fix the errors and restart the project.`); } // Serve the manifest. let manifestHandler = (() => { var _ref31 = _asyncToGenerator(function* (req, res) { try { // 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 (_Doctor || _load_Doctor()).validateWithNetworkAsync(projectRoot); let { exp: manifest } = yield (_ProjectUtils || _load_ProjectUtils()).readConfigJsonAsync(projectRoot); if (!manifest) { const configName = yield (_ProjectUtils || _load_ProjectUtils()).configFilenameAsync(projectRoot); throw new Error(`No ${configName} file found`); } // Get packager opts and then copy into bundleUrlPackagerOpts let packagerOpts = yield (_ProjectSettings || _load_ProjectSettings()).getPackagerOptsAsync(projectRoot); let bundleUrlPackagerOpts = JSON.parse(JSON.stringify(packagerOpts)); bundleUrlPackagerOpts.urlType = 'http'; if (bundleUrlPackagerOpts.hostType === 'redirect') { bundleUrlPackagerOpts.hostType = 'tunnel'; } manifest.xde = true; // deprecated manifest.developer = { tool: (_Config || _load_Config()).default.developerTool, projectRoot }; manifest.packagerOpts = packagerOpts; manifest.env = {}; for (let key of Object.keys(process.env)) { if (key.startsWith('REACT_NATIVE_') || key.startsWith('EXPO_')) { manifest.env[key] = process.env[key]; } } let entryPoint = yield (_Exp || _load_Exp()).determineEntryPointAsync(projectRoot); let platform = req.headers['exponent-platform'] || 'ios'; entryPoint = (_UrlUtils || _load_UrlUtils()).getPlatformSpecificBundleUrl(entryPoint, platform); let mainModuleName = (_UrlUtils || _load_UrlUtils()).guessMainModulePath(entryPoint); let queryParams = yield (_UrlUtils || _load_UrlUtils()).constructBundleQueryParamsAsync(projectRoot, packagerOpts, req.hostname); let path = `/${mainModuleName}.bundle?platform=${platform}&${queryParams}`; manifest.bundleUrl = (yield (_UrlUtils || _load_UrlUtils()).constructBundleUrlAsync(projectRoot, bundleUrlPackagerOpts, req.hostname)) + path; manifest.debuggerHost = yield (_UrlUtils || _load_UrlUtils()).constructDebuggerHostAsync(projectRoot, req.hostname); manifest.mainModuleName = mainModuleName; manifest.logUrl = `${yield (_UrlUtils || _load_UrlUtils()).constructManifestUrlAsync(projectRoot, { urlType: 'http' }, req.hostname)}/logs`; // Resolve manifest assets to their packager URL yield _resolveManifestAssets(projectRoot, manifest, (() => { var _ref32 = _asyncToGenerator(function* (path) { return manifest.bundleUrl.match(/^https?:\/\/.*?\//)[0] + 'assets/' + path; }); return function (_x45) { return _ref32.apply(this, arguments); }; })()); // the server normally inserts this but if we're offline we'll do it here const hostUUID = yield (_UserSettings || _load_UserSettings()).default.anonymousIdentifier(); if ((_Config || _load_Config()).default.offline) { manifest.id = `@anonymous/${manifest.slug}-${hostUUID}`; } let manifestString = JSON.stringify(manifest); let currentUser; if (!(_Config || _load_Config()).default.offline) { currentUser = yield (_User || _load_User()).default.getCurrentUserAsync(); } if (req.headers['exponent-accept-signature'] && (currentUser || (_Config || _load_Config()).default.offline)) { if (_cachedSignedManifest.manifestString === manifestString) { manifestString = _cachedSignedManifest.signedManifest; } else { if ((_Config || _load_Config()).default.offline) { const unsignedManifest = { manifestString, signature: 'UNSIGNED' }; _cachedSignedManifest.manifestString = manifestString; manifestString = JSON.stringify(unsignedManifest); _cachedSignedManifest.signedManifest = manifestString; } else { let publishInfo = yield (_Exp || _load_Exp()).getPublishInfoAsync(projectRoot); let signedManifest = yield (_Api || _load_Api()).default.callMethodAsync('signManifest', [publishInfo.args], 'post', manifest); _cachedSignedManifest.manifestString = manifestString; _cachedSignedManifest.signedManifest = signedManifest.response; manifestString = signedManifest.response; } } } const hostInfo = { host: hostUUID, server: 'xdl', serverVersion: require('../package.json').version, serverDriver: (_Config || _load_Config()).default.developerTool, serverOS: _os.default.platform(), serverOSVersion: _os.default.release() }; res.append('Exponent-Server', JSON.stringify(hostInfo)); res.send(manifestString); (_Analytics || _load_Analytics()).logEvent('Serve Manifest', { projectRoot }); } catch (e) { (_ProjectUtils || _load_ProjectUtils()).logDebug(projectRoot, 'expo', `Error in manifestHandler: ${e} ${e.stack}`); // 5xx = Server Error HTTP code res.status(520).send({ error: e.toString() }); } }); return function manifestHandler(_x43, _x44) { return _ref31.apply(this, arguments); }; })(); app.get('/', manifestHandler); app.get('/manifest', manifestHandler); app.get('/index.exp', manifestHandler); app.post('/logs', (() => { var _ref33 = _asyncToGenerator(function* (req, res) { try { let deviceId = req.get('Device-Id'); let deviceName = req.get('Device-Name'); if (deviceId && deviceName && req.body) { _handleDeviceLogs(projectRoot, deviceId, deviceName, req.body); } } catch (e) { (_ProjectUtils || _load_ProjectUtils()).logError(projectRoot, 'expo', `Error getting device logs: ${e} ${e.stack}`); } res.send('Success'); }); return function (_x46, _x47) { return _ref33.apply(this, arguments); }; })()); app.post('/shutdown', (() => { var _ref34 = _asyncToGenerator(function* (req, res) { server.close(); res.send('Success'); }); return function (_x48, _x49) { return _ref34.apply(this, arguments); }; })()); let expRc = yield (_ProjectUtils || _load_ProjectUtils()).readExpRcAsync(projectRoot); let expoServerPort = expRc.manifestPort ? expRc.manifestPort : yield _getFreePortAsync(19000); yield (_ProjectSettings || _load_ProjectSettings()).setPackagerInfoAsync(projectRoot, { expoServerPort }); let server = app.listen(expoServerPort, function () { let host = server.address().address; let port = server.address().port; (_ProjectUtils || _load_ProjectUtils()).logDebug(projectRoot, 'expo', `Local server listening at http://${host}:${port}`); }); yield (_Exp || _load_Exp()).saveRecentExpRootAsync(projectRoot); }); return function startExpoServerAsync(_x42) { return _ref30.apply(this, arguments); }; })(); let stopExpoServerAsync = exports.stopExpoServerAsync = (() => { var _ref35 = _asyncToGenerator(function* (projectRoot) { yield (_User || _load_User()).default.ensureLoggedInAsync(); _assertValidProjectRoot(projectRoot); let packagerInfo = yield (_ProjectSettings || _load_ProjectSettings()).readPackagerInfoAsync(projectRoot); if (packagerInfo && packagerInfo.expoServerPort) { try { yield (_request || _load_request()).default.post.promise(`http://localhost:${packagerInfo.expoServerPort}/shutdown`); } catch (e) {} } yield (_ProjectSettings || _load_ProjectSettings()).setPackagerInfoAsync(projectRoot, { expoServerPort: null }); }); return function stopExpoServerAsync(_x50) { return _ref35.apply(this, arguments); }; })(); let _connectToNgrokAsync = (() => { var _ref36 = _asyncToGenerator(function* (projectRoot, args, hostnameAsync, ngrokPid, attempts = 0) { try { let configPath = _path.default.join((_UserSettings || _load_UserSettings()).default.dotExpoHomeDirectory(), 'ngrok.yml'); let hostname = yield hostnameAsync(); let url = yield (_ngrok || _load_ngrok()).default.promise.connect(_extends({ hostname, configPath }, args)); return url; } catch (e) { // Attempt to connect 3 times if (attempts >= 2) { if (e.message) { throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.NGROK_ERROR, e.toString()); } else { throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.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 (e) { (_ProjectUtils || _load_ProjectUtils()).logDebug(projectRoot, 'expo', `Couldn't kill ngrok with PID ${ngrokPid}`); } } else { yield (_ngrok || _load_ngrok()).default.promise.kill(); } } else { // Change randomness to avoid conflict if killing ngrok didn't help yield (_Exp || _load_Exp()).resetProjectRandomnessAsync(projectRoot); } } // Wait 100ms and then try again yield (0, (_delayAsync || _load_delayAsync()).default)(100); return _connectToNgrokAsync(projectRoot, args, hostnameAsync, null, attempts + 1); } }); return function _connectToNgrokAsync(_x51, _x52, _x53, _x54) { return _ref36.apply(this, arguments); }; })(); let startTunnelsAsync = exports.startTunnelsAsync = (() => { var _ref37 = _asyncToGenerator(function* (projectRoot) { const user = yield (_User || _load_User()).default.ensureLoggedInAsync(); if (!user) { throw new Error('Internal error -- tunnel started in offline mode.'); } _assertValidProjectRoot(projectRoot); let packagerInfo = yield (_ProjectSettings || _load_ProjectSettings()).readPackagerInfoAsync(projectRoot); if (!packagerInfo.packagerPort) { throw new (_XDLError || _load_XDLError()).default((_ErrorCode