xdl
Version:
The Expo Development Library
1,216 lines (1,060 loc) • 71.2 kB
JavaScript
'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