1916 lines
73 KiB
JavaScript
1916 lines
73 KiB
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.getLatestReleaseAsync = 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(`${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(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.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 ((_fsExtra || _load_fsExtra()).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 getLatestReleaseAsync = exports.getLatestReleaseAsync = (() => {
|
|
var _ref10 = _asyncToGenerator(function* (projectRoot, options) {
|
|
// TODO(ville): move request from multipart/form-data to JSON once supported by the endpoint.
|
|
let formData = new (_FormData || _load_FormData()).default();
|
|
formData.append('queryType', 'history');
|
|
formData.append('slug', (yield getSlugAsync(projectRoot)));
|
|
formData.append('version', '2');
|
|
formData.append('count', '1');
|
|
formData.append('releaseChannel', options.releaseChannel);
|
|
formData.append('platform', options.platform);
|
|
const { queryResult } = yield (_Api || _load_Api()).default.callMethodAsync('publishInfo', [], 'post', null, {
|
|
formData
|
|
});
|
|
if (queryResult && queryResult.length > 0) {
|
|
return queryResult[0];
|
|
} else {
|
|
return null;
|
|
}
|
|
});
|
|
|
|
return function getLatestReleaseAsync(_x15, _x16) {
|
|
return _ref10.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let publishAsync = exports.publishAsync = (() => {
|
|
var _ref11 = _asyncToGenerator(function* (projectRoot, options = {}) {
|
|
const user = yield (_User || _load_User()).default.ensureLoggedInAsync();
|
|
yield _validatePackagerReadyAsync(projectRoot);
|
|
(_Analytics || _load_Analytics()).logEvent('Publish', { projectRoot });
|
|
|
|
const validationStatus = yield (_Doctor || _load_Doctor()).validateWithNetworkAsync(projectRoot);
|
|
if (validationStatus == (_Doctor || _load_Doctor()).ERROR || validationStatus === (_Doctor || _load_Doctor()).FATAL) {
|
|
throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.PUBLISH_VALIDATION_ERROR, "Couldn't publish because errors were found. (See logs above.) Please fix the errors and try again.");
|
|
}
|
|
|
|
// 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(_x17) {
|
|
return _ref11.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let _uploadArtifactsAsync = (() => {
|
|
var _ref12 = _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(_x18) {
|
|
return _ref12.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let _validatePackagerReadyAsync = (() => {
|
|
var _ref13 = _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(_x19) {
|
|
return _ref13.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let _getPublishExpConfigAsync = (() => {
|
|
var _ref14 = _asyncToGenerator(function* (projectRoot, options) {
|
|
let schema = (_joi || _load_joi()).default.object().keys({
|
|
releaseChannel: (_joi || _load_joi()).default.string()
|
|
});
|
|
|
|
// Validate schema
|
|
try {
|
|
yield joiValidateAsync(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(_x20, _x21) {
|
|
return _ref14.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
// Fetch iOS and Android bundles for publishing
|
|
|
|
|
|
let _buildPublishBundlesAsync = (() => {
|
|
var _ref15 = _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(_x22) {
|
|
return _ref15.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 _ref16 = _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(_x23, _x24) {
|
|
return _ref16.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let _fetchAndUploadAssetsAsync = (() => {
|
|
var _ref17 = _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 _ref18 = _asyncToGenerator(function* (assetPath) {
|
|
const absolutePath = _path.default.resolve(projectRoot, assetPath);
|
|
const contents = yield (_fsExtra || _load_fsExtra()).default.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 (_x27) {
|
|
return _ref18.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.');
|
|
}
|
|
|
|
// Convert asset patterns to a list of asset strings that match them.
|
|
// Assets strings are formatted as `asset_<hash>.<type>` and represent
|
|
// the name that the file will have in the app bundle. The `asset_` prefix is
|
|
// needed because android doesn't support assets that start with numbers.
|
|
if (exp.assetBundlePatterns) {
|
|
const fullPatterns = exp.assetBundlePatterns.map(function (p) {
|
|
return _path.default.join(projectRoot, p);
|
|
});
|
|
// The assets returned by the RN packager has duplicates so make sure we
|
|
// only bundle each once.
|
|
const bundledAssets = new Set();
|
|
for (const asset of assets) {
|
|
const file = asset.files && asset.files[0];
|
|
if (asset.__packager_asset && file && fullPatterns.some(function (p) {
|
|
return (0, (_minimatch || _load_minimatch()).default)(file, p);
|
|
})) {
|
|
bundledAssets.add('asset_' + asset.hash + (asset.type ? '.' + asset.type : ''));
|
|
}
|
|
}
|
|
exp.bundledAssets = [...bundledAssets];
|
|
delete exp.assetBundlePatterns;
|
|
}
|
|
|
|
return exp;
|
|
});
|
|
|
|
return function _fetchAndUploadAssetsAsync(_x25, _x26) {
|
|
return _ref17.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let _writeArtifactSafelyAsync = (() => {
|
|
var _ref19 = _asyncToGenerator(function* (projectRoot, keyName, artifactPath, artifact) {
|
|
const pathToWrite = _path.default.resolve(projectRoot, artifactPath);
|
|
if (!(_fsExtra || _load_fsExtra()).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 (_fsExtra || _load_fsExtra()).default.writeFile(pathToWrite, artifact);
|
|
}
|
|
});
|
|
|
|
return function _writeArtifactSafelyAsync(_x28, _x29, _x30, _x31) {
|
|
return _ref19.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let _maybeWriteArtifactsToDiskAsync = (() => {
|
|
var _ref20 = _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(_x32) {
|
|
return _ref20.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let _handleKernelPublishedAsync = (() => {
|
|
var _ref21 = _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 (_fsExtra || _load_fsExtra()).default.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 (_fsExtra || _load_fsExtra()).default.writeFile(_path.default.resolve(projectRoot, exp.kernel.iosManifestPath), JSON.stringify(manifest));
|
|
}
|
|
});
|
|
|
|
return function _handleKernelPublishedAsync(_x33) {
|
|
return _ref21.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
// TODO(jesse): Add analytics for upload
|
|
|
|
|
|
let uploadAssetsAsync = (() => {
|
|
var _ref22 = _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 _ref23 = _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 (_x36) {
|
|
return _ref23.apply(this, arguments);
|
|
};
|
|
})()));
|
|
});
|
|
|
|
return function uploadAssetsAsync(_x34, _x35) {
|
|
return _ref22.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let _readFileForUpload = (() => {
|
|
var _ref24 = _asyncToGenerator(function* (path) {
|
|
if ((0, (_EnvironmentHelper || _load_EnvironmentHelper()).isNode)()) {
|
|
return (_fsExtra || _load_fsExtra()).default.createReadStream(path);
|
|
} else {
|
|
const data = yield (_fsExtra || _load_fsExtra()).default.readFile(path);
|
|
return new Blob([data]);
|
|
}
|
|
});
|
|
|
|
return function _readFileForUpload(_x37) {
|
|
return _ref24.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let buildAsync = exports.buildAsync = (() => {
|
|
var _ref25 = _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 joiValidateAsync(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(_x38) {
|
|
return _ref25.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let _waitForRunningAsync = (() => {
|
|
var _ref26 = _asyncToGenerator(function* (url) {
|
|
try {
|
|
let response = yield request(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(_x39) {
|
|
return _ref26.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let _restartWatchmanAsync = (() => {
|
|
var _ref27 = _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(_x40) {
|
|
return _ref27.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let startReactNativeServerAsync = exports.startReactNativeServerAsync = (() => {
|
|
var _ref28 = _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'],
|
|
nonPersistent: !!options.nonPersistent
|
|
};
|
|
|
|
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 (val && typeof val === 'boolean') {
|
|
opts.push(`--${key}`);
|
|
} else if (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.pipe((0, (_split || _load_split()).default)()).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 _ref29 = _asyncToGenerator(function* (code) {
|
|
(_ProjectUtils || _load_ProjectUtils()).logDebug(projectRoot, 'expo', `packager process exited with code ${code}`);
|
|
});
|
|
|
|
return function (_x42) {
|
|
return _ref29.apply(this, arguments);
|
|
};
|
|
})());
|
|
let packagerUrl = yield (_UrlUtils || _load_UrlUtils()).constructBundleUrlAsync(projectRoot, {
|
|
urlType: 'http',
|
|
hostType: 'localhost'
|
|
});
|
|
yield _waitForRunningAsync(`${packagerUrl}/status`);
|
|
});
|
|
|
|
return function startReactNativeServerAsync(_x41) {
|
|
return _ref28.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 _ref30 = _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 treekillAsync(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(_x43) {
|
|
return _ref30.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let startExpoServerAsync = exports.startExpoServerAsync = (() => {
|
|
var _ref31 = _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 _ref32 = _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()).constructLogUrlAsync(projectRoot, req.hostname);
|
|
yield _resolveManifestAssets(projectRoot, manifest, (() => {
|
|
var _ref33 = _asyncToGenerator(function* (path) {
|
|
return manifest.bundleUrl.match(/^https?:\/\/.*?\//)[0] + 'assets/' + path;
|
|
});
|
|
|
|
return function (_x47) {
|
|
return _ref33.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(_x45, _x46) {
|
|
return _ref32.apply(this, arguments);
|
|
};
|
|
})();
|
|
app.get('/', manifestHandler);
|
|
app.get('/manifest', manifestHandler);
|
|
app.get('/index.exp', manifestHandler);
|
|
app.post('/logs', (() => {
|
|
var _ref34 = _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 (_x48, _x49) {
|
|
return _ref34.apply(this, arguments);
|
|
};
|
|
})());
|
|
app.post('/shutdown', (() => {
|
|
var _ref35 = _asyncToGenerator(function* (req, res) {
|
|
server.close();
|
|
res.send('Success');
|
|
});
|
|
|
|
return function (_x50, _x51) {
|
|
return _ref35.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(_x44) {
|
|
return _ref31.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let stopExpoServerAsync = exports.stopExpoServerAsync = (() => {
|
|
var _ref36 = _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.post(`http://localhost:${packagerInfo.expoServerPort}/shutdown`);
|
|
} catch (e) {}
|
|
}
|
|
yield (_ProjectSettings || _load_ProjectSettings()).setPackagerInfoAsync(projectRoot, {
|
|
expoServerPort: null
|
|
});
|
|
});
|
|
|
|
return function stopExpoServerAsync(_x52) {
|
|
return _ref36.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let _connectToNgrokAsync = (() => {
|
|
var _ref37 = _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 ngrokConnectAsync(_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 ngrokKillAsync();
|
|
}
|
|
} 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(_x53, _x54, _x55, _x56) {
|
|
return _ref37.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let startTunnelsAsync = exports.startTunnelsAsync = (() => {
|
|
var _ref38 = _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 || _load_ErrorCode()).default.NO_PACKAGER_PORT, `No packager found for project at ${projectRoot}.`);
|
|
}
|
|
if (!packagerInfo.expoServerPort) {
|
|
throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.NO_EXPO_SERVER_PORT, `No Expo server found for project at ${projectRoot}.`);
|
|
}
|
|
yield stopTunnelsAsync(projectRoot);
|
|
if (yield (_Android || _load_Android()).startAdbReverseAsync(projectRoot)) {
|
|
(_ProjectUtils || _load_ProjectUtils()).logInfo(projectRoot, 'expo', 'Successfully ran `adb reverse`. Localhost urls should work on the connected Android device.', 'project-adb-reverse');
|
|
} else {
|
|
(_ProjectUtils || _load_ProjectUtils()).clearNotification(projectRoot, 'project-adb-reverse');
|
|
}
|
|
const { username } = user;
|
|
let packageShortName = _path.default.parse(projectRoot).base;
|
|
let expRc = yield (_ProjectUtils || _load_ProjectUtils()).readExpRcAsync(projectRoot);
|
|
|
|
(_ngrok || _load_ngrok()).default.addListener('statuschange', function (status) {
|
|
if (status === 'reconnecting') {
|
|
(_ProjectUtils || _load_ProjectUtils()).logError(projectRoot, 'expo', 'We noticed your tunnel is having issues. This may be due to intermittent problems with our tunnel provider. If you have trouble connecting to your app, try to Restart the project, or switch Host to LAN.');
|
|
} else if (status === 'online') {
|
|
(_ProjectUtils || _load_ProjectUtils()).logInfo(projectRoot, 'expo', 'Tunnel connected.');
|
|
}
|
|
});
|
|
|
|
try {
|
|
let startedTunnelsSuccessfully = false;
|
|
|
|
// Some issues with ngrok cause it to hang indefinitely. After
|
|
// TUNNEL_TIMEOUTms we just throw an error.
|
|
yield Promise.race([_asyncToGenerator(function* () {
|
|
yield (0, (_delayAsync || _load_delayAsync()).default)(TUNNEL_TIMEOUT);
|
|
if (!startedTunnelsSuccessfully) {
|
|
throw new Error('Starting tunnels timed out');
|
|
}
|
|
})(), _asyncToGenerator(function* () {
|
|
let expoServerNgrokUrl = yield _connectToNgrokAsync(projectRoot, {
|
|
authtoken: (_Config || _load_Config()).default.ngrok.authToken,
|
|
port: packagerInfo.expoServerPort,
|
|
proto: 'http'
|
|
}, _asyncToGenerator(function* () {
|
|
let randomness = expRc.manifestTunnelRandomness ? expRc.manifestTunnelRandomness : yield (_Exp || _load_Exp()).getProjectRandomnessAsync(projectRoot);
|
|
return [randomness, (_UrlUtils || _load_UrlUtils()).domainify(username), (_UrlUtils || _load_UrlUtils()).domainify(packageShortName), (_Config || _load_Config()).default.ngrok.domain].join('.');
|
|
}), packagerInfo.ngrokPid);
|
|
let packagerNgrokUrl = yield _connectToNgrokAsync(projectRoot, {
|
|
authtoken: (_Config || _load_Config()).default.ngrok.authToken,
|
|
port: packagerInfo.packagerPort,
|
|
proto: 'http'
|
|
}, _asyncToGenerator(function* () {
|
|
let randomness = expRc.manifestTunnelRandomness ? expRc.manifestTunnelRandomness : yield (_Exp || _load_Exp()).getProjectRandomnessAsync(projectRoot);
|
|
return ['packager', randomness, (_UrlUtils || _load_UrlUtils()).domainify(username), (_UrlUtils || _load_UrlUtils()).domainify(packageShortName), (_Config || _load_Config()).default.ngrok.domain].join('.');
|
|
}), packagerInfo.ngrokPid);
|
|
yield (_ProjectSettings || _load_ProjectSettings()).setPackagerInfoAsync(projectRoot, {
|
|
expoServerNgrokUrl,
|
|
packagerNgrokUrl,
|
|
ngrokPid: (_ngrok || _load_ngrok()).default.process().pid
|
|
});
|
|
|
|
startedTunnelsSuccessfully = true;
|
|
})()]);
|
|
} catch (e) {
|
|
(_ProjectUtils || _load_ProjectUtils()).logError(projectRoot, 'expo', `Error starting tunnel: ${e.toString()}`);
|
|
throw e;
|
|
}
|
|
});
|
|
|
|
return function startTunnelsAsync(_x57) {
|
|
return _ref38.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let stopTunnelsAsync = exports.stopTunnelsAsync = (() => {
|
|
var _ref43 = _asyncToGenerator(function* (projectRoot) {
|
|
yield (_User || _load_User()).default.ensureLoggedInAsync();
|
|
_assertValidProjectRoot(projectRoot); // This will kill all ngrok tunnels in the process. // We'll need to change this if we ever support more than one project // open at a time in XDE.
|
|
let packagerInfo = yield (_ProjectSettings || _load_ProjectSettings()).readPackagerInfoAsync(projectRoot);
|
|
let ngrokProcess = (_ngrok || _load_ngrok()).default.process();
|
|
let ngrokProcessPid = ngrokProcess ? ngrokProcess.pid : null;
|
|
(_ngrok || _load_ngrok()).default.removeAllListeners('statuschange');
|
|
if (packagerInfo.ngrokPid && packagerInfo.ngrokPid !== ngrokProcessPid) {
|
|
// Ngrok is running in some other process. Kill at the os level.
|
|
try {
|
|
process.kill(packagerInfo.ngrokPid);
|
|
} catch (e) {
|
|
(_ProjectUtils || _load_ProjectUtils()).logDebug(projectRoot, 'expo', `Couldn't kill ngrok with PID ${packagerInfo.ngrokPid}`);
|
|
}
|
|
} else {
|
|
// Ngrok is running from the current process. Kill using ngrok api.
|
|
yield ngrokKillAsync();
|
|
}
|
|
yield (_ProjectSettings || _load_ProjectSettings()).setPackagerInfoAsync(projectRoot, {
|
|
expoServerNgrokUrl: null,
|
|
packagerNgrokUrl: null,
|
|
ngrokPid: null
|
|
});
|
|
yield (_Android || _load_Android()).stopAdbReverseAsync(projectRoot);
|
|
});
|
|
|
|
return function stopTunnelsAsync(_x58) {
|
|
return _ref43.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let setOptionsAsync = exports.setOptionsAsync = (() => {
|
|
var _ref44 = _asyncToGenerator(function* (projectRoot, options) {
|
|
yield (_User || _load_User()).default.ensureLoggedInAsync();
|
|
_assertValidProjectRoot(projectRoot); // Check to make sure all options are valid
|
|
let schema = (_joi || _load_joi()).default.object().keys({
|
|
packagerPort: (_joi || _load_joi()).default.number().integer()
|
|
});
|
|
try {
|
|
yield joiValidateAsync(options, schema);
|
|
} catch (e) {
|
|
throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.INVALID_OPTIONS, e.toString());
|
|
}
|
|
yield (_ProjectSettings || _load_ProjectSettings()).setPackagerInfoAsync(projectRoot, options);
|
|
});
|
|
|
|
return function setOptionsAsync(_x59, _x60) {
|
|
return _ref44.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let getUrlAsync = exports.getUrlAsync = (() => {
|
|
var _ref45 = _asyncToGenerator(function* (projectRoot, options = {}) {
|
|
yield (_User || _load_User()).default.ensureLoggedInAsync();
|
|
_assertValidProjectRoot(projectRoot);
|
|
return yield (_UrlUtils || _load_UrlUtils()).constructManifestUrlAsync(projectRoot, options);
|
|
});
|
|
|
|
return function getUrlAsync(_x61) {
|
|
return _ref45.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let startAsync = exports.startAsync = (() => {
|
|
var _ref46 = _asyncToGenerator(function* (projectRoot, options = {}, verbose = true) {
|
|
yield (_User || _load_User()).default.ensureLoggedInAsync();
|
|
_assertValidProjectRoot(projectRoot);
|
|
(_Analytics || _load_Analytics()).logEvent('Start Project', {
|
|
projectRoot
|
|
});
|
|
yield startExpoServerAsync(projectRoot);
|
|
yield startReactNativeServerAsync(projectRoot, options, verbose);
|
|
if (!(_Config || _load_Config()).default.offline) {
|
|
try {
|
|
yield startTunnelsAsync(projectRoot);
|
|
} catch (e) {
|
|
(_ProjectUtils || _load_ProjectUtils()).logDebug(projectRoot, 'expo', `Error starting ngrok ${e.message}`);
|
|
}
|
|
}
|
|
let { exp } = yield (_ProjectUtils || _load_ProjectUtils()).readConfigJsonAsync(projectRoot);
|
|
return exp;
|
|
});
|
|
|
|
return function startAsync(_x62) {
|
|
return _ref46.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let _stopInternalAsync = (() => {
|
|
var _ref47 = _asyncToGenerator(function* (projectRoot) {
|
|
yield stopExpoServerAsync(projectRoot);
|
|
yield stopReactNativeServerAsync(projectRoot);
|
|
if (!(_Config || _load_Config()).default.offline) {
|
|
try {
|
|
yield stopTunnelsAsync(projectRoot);
|
|
} catch (e) {
|
|
(_ProjectUtils || _load_ProjectUtils()).logDebug(projectRoot, 'expo', `Error stopping ngrok ${e.message}`);
|
|
}
|
|
}
|
|
});
|
|
|
|
return function _stopInternalAsync(_x63) {
|
|
return _ref47.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
let stopAsync = exports.stopAsync = (() => {
|
|
var _ref48 = _asyncToGenerator(function* (projectDir) {
|
|
const result = yield Promise.race([_stopInternalAsync(projectDir), new Promise(function (resolve, reject) {
|
|
return setTimeout(resolve, 2000, 'stopFailed');
|
|
})]);
|
|
if (result === 'stopFailed') {
|
|
// find RN packager and ngrok pids, attempt to kill them manually
|
|
const { packagerPid, ngrokPid } = yield (_ProjectSettings || _load_ProjectSettings()).readPackagerInfoAsync(projectDir);
|
|
if (packagerPid) {
|
|
try {
|
|
process.kill(packagerPid);
|
|
} catch (e) {}
|
|
}
|
|
if (ngrokPid) {
|
|
try {
|
|
process.kill(ngrokPid);
|
|
} catch (e) {}
|
|
}
|
|
yield (_ProjectSettings || _load_ProjectSettings()).setPackagerInfoAsync(projectDir, {
|
|
expoServerPort: null,
|
|
packagerPort: null,
|
|
packagerPid: null,
|
|
expoServerNgrokUrl: null,
|
|
packagerNgrokUrl: null,
|
|
ngrokPid: null
|
|
});
|
|
}
|
|
});
|
|
|
|
return function stopAsync(_x64) {
|
|
return _ref48.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
var _bodyParser;
|
|
|
|
function _load_bodyParser() {
|
|
return _bodyParser = _interopRequireDefault(require('body-parser'));
|
|
}
|
|
|
|
var _child_process = _interopRequireDefault(require('child_process'));
|
|
|
|
var _delayAsync;
|
|
|
|
function _load_delayAsync() {
|
|
return _delayAsync = _interopRequireDefault(require('delay-async'));
|
|
}
|
|
|
|
var _decache;
|
|
|
|
function _load_decache() {
|
|
return _decache = _interopRequireDefault(require('decache'));
|
|
}
|
|
|
|
var _express;
|
|
|
|
function _load_express() {
|
|
return _express = _interopRequireDefault(require('express'));
|
|
}
|
|
|
|
var _freeportAsync;
|
|
|
|
function _load_freeportAsync() {
|
|
return _freeportAsync = _interopRequireDefault(require('freeport-async'));
|
|
}
|
|
|
|
var _fsExtra;
|
|
|
|
function _load_fsExtra() {
|
|
return _fsExtra = _interopRequireDefault(require('fs-extra'));
|
|
}
|
|
|
|
var _joi;
|
|
|
|
function _load_joi() {
|
|
return _joi = _interopRequireDefault(require('joi'));
|
|
}
|
|
|
|
var _util;
|
|
|
|
function _load_util() {
|
|
return _util = _interopRequireDefault(require('util.promisify'));
|
|
}
|
|
|
|
var _lodash;
|
|
|
|
function _load_lodash() {
|
|
return _lodash = _interopRequireDefault(require('lodash'));
|
|
}
|
|
|
|
var _isEmpty;
|
|
|
|
function _load_isEmpty() {
|
|
return _isEmpty = _interopRequireDefault(require('lodash/isEmpty'));
|
|
}
|
|
|
|
var _minimatch;
|
|
|
|
function _load_minimatch() {
|
|
return _minimatch = _interopRequireDefault(require('minimatch'));
|
|
}
|
|
|
|
var _ngrok;
|
|
|
|
function _load_ngrok() {
|
|
return _ngrok = _interopRequireDefault(require('@expo/ngrok'));
|
|
}
|
|
|
|
var _os = _interopRequireDefault(require('os'));
|
|
|
|
var _path = _interopRequireDefault(require('path'));
|
|
|
|
var _requestPromiseNative;
|
|
|
|
function _load_requestPromiseNative() {
|
|
return _requestPromiseNative = _interopRequireDefault(require('request-promise-native'));
|
|
}
|
|
|
|
var _spawnAsync;
|
|
|
|
function _load_spawnAsync() {
|
|
return _spawnAsync = _interopRequireDefault(require('@expo/spawn-async'));
|
|
}
|
|
|
|
var _split;
|
|
|
|
function _load_split() {
|
|
return _split = _interopRequireDefault(require('split'));
|
|
}
|
|
|
|
var _treeKill;
|
|
|
|
function _load_treeKill() {
|
|
return _treeKill = _interopRequireDefault(require('tree-kill'));
|
|
}
|
|
|
|
var _md5hex;
|
|
|
|
function _load_md5hex() {
|
|
return _md5hex = _interopRequireDefault(require('md5hex'));
|
|
}
|
|
|
|
var _Analytics;
|
|
|
|
function _load_Analytics() {
|
|
return _Analytics = _interopRequireWildcard(require('./Analytics'));
|
|
}
|
|
|
|
var _Android;
|
|
|
|
function _load_Android() {
|
|
return _Android = _interopRequireWildcard(require('./Android'));
|
|
}
|
|
|
|
var _Api;
|
|
|
|
function _load_Api() {
|
|
return _Api = _interopRequireDefault(require('./Api'));
|
|
}
|
|
|
|
var _Config;
|
|
|
|
function _load_Config() {
|
|
return _Config = _interopRequireDefault(require('./Config'));
|
|
}
|
|
|
|
var _Doctor;
|
|
|
|
function _load_Doctor() {
|
|
return _Doctor = _interopRequireWildcard(require('./project/Doctor'));
|
|
}
|
|
|
|
var _ErrorCode;
|
|
|
|
function _load_ErrorCode() {
|
|
return _ErrorCode = _interopRequireDefault(require('./ErrorCode'));
|
|
}
|
|
|
|
var _Logger;
|
|
|
|
function _load_Logger() {
|
|
return _Logger = _interopRequireDefault(require('./Logger'));
|
|
}
|
|
|
|
var _ExponentTools;
|
|
|
|
function _load_ExponentTools() {
|
|
return _ExponentTools = _interopRequireWildcard(require('./detach/ExponentTools'));
|
|
}
|
|
|
|
var _Exp;
|
|
|
|
function _load_Exp() {
|
|
return _Exp = _interopRequireWildcard(require('./Exp'));
|
|
}
|
|
|
|
var _ExpSchema;
|
|
|
|
function _load_ExpSchema() {
|
|
return _ExpSchema = _interopRequireWildcard(require('./project/ExpSchema'));
|
|
}
|
|
|
|
var _FormData;
|
|
|
|
function _load_FormData() {
|
|
return _FormData = _interopRequireDefault(require('./tools/FormData'));
|
|
}
|
|
|
|
var _EnvironmentHelper;
|
|
|
|
function _load_EnvironmentHelper() {
|
|
return _EnvironmentHelper = require('./tools/EnvironmentHelper');
|
|
}
|
|
|
|
var _ProjectSettings;
|
|
|
|
function _load_ProjectSettings() {
|
|
return _ProjectSettings = _interopRequireWildcard(require('./ProjectSettings'));
|
|
}
|
|
|
|
var _ProjectUtils;
|
|
|
|
function _load_ProjectUtils() {
|
|
return _ProjectUtils = _interopRequireWildcard(require('./project/ProjectUtils'));
|
|
}
|
|
|
|
var _UrlUtils;
|
|
|
|
function _load_UrlUtils() {
|
|
return _UrlUtils = _interopRequireWildcard(require('./UrlUtils'));
|
|
}
|
|
|
|
var _User;
|
|
|
|
function _load_User() {
|
|
return _User = _interopRequireDefault(require('./User'));
|
|
}
|
|
|
|
var _UserSettings;
|
|
|
|
function _load_UserSettings() {
|
|
return _UserSettings = _interopRequireDefault(require('./UserSettings'));
|
|
}
|
|
|
|
var _Versions;
|
|
|
|
function _load_Versions() {
|
|
return _Versions = _interopRequireWildcard(require('./Versions'));
|
|
}
|
|
|
|
var _Watchman;
|
|
|
|
function _load_Watchman() {
|
|
return _Watchman = _interopRequireWildcard(require('./Watchman'));
|
|
}
|
|
|
|
var _XDLError;
|
|
|
|
function _load_XDLError() {
|
|
return _XDLError = _interopRequireDefault(require('./XDLError'));
|
|
}
|
|
|
|
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
|
|
|
|
//eslint-disable-line
|
|
|
|
const MINIMUM_BUNDLE_SIZE = 500;
|
|
const TUNNEL_TIMEOUT = 10 * 1000;
|
|
|
|
const joiValidateAsync = (0, (_util || _load_util()).default)((_joi || _load_joi()).default.validate);
|
|
const treekillAsync = (0, (_util || _load_util()).default)((_treeKill || _load_treeKill()).default);
|
|
const ngrokConnectAsync = (0, (_util || _load_util()).default)((_ngrok || _load_ngrok()).default.connect);
|
|
const ngrokKillAsync = (0, (_util || _load_util()).default)((_ngrok || _load_ngrok()).default.kill);
|
|
|
|
const request = (_requestPromiseNative || _load_requestPromiseNative()).default.defaults({
|
|
resolveWithFullResponse: true
|
|
});
|
|
|
|
let _cachedSignedManifest = {
|
|
manifestString: null,
|
|
signedManifest: null
|
|
};
|
|
|
|
function _requireFromProject(modulePath, projectRoot) {
|
|
try {
|
|
if (modulePath.indexOf('.') === 0) {
|
|
let fullPath = _path.default.resolve(projectRoot, modulePath);
|
|
|
|
// Clear the require cache for this module so get a fresh version of it
|
|
// without requiring the user to restart XDE
|
|
(0, (_decache || _load_decache()).default)(fullPath);
|
|
|
|
// $FlowIssue: doesn't work with dynamic requires
|
|
return require(fullPath);
|
|
} else {
|
|
let fullPath = _path.default.resolve(projectRoot, 'node_modules', modulePath);
|
|
|
|
// Clear the require cache for this module so get a fresh version of it
|
|
// without requiring the user to restart XDE
|
|
(0, (_decache || _load_decache()).default)(fullPath);
|
|
|
|
// $FlowIssue: doesn't work with dynamic requires
|
|
return require(fullPath);
|
|
}
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function _createBlob(string) {
|
|
if ((0, (_EnvironmentHelper || _load_EnvironmentHelper()).isNode)()) {
|
|
return string;
|
|
} else {
|
|
return new Blob([string]);
|
|
}
|
|
}
|
|
|
|
function _stripPackagerOutputBox(output) {
|
|
let re = /Running packager on port (\d+)/;
|
|
let found = output.match(re);
|
|
if (found && found.length >= 2) {
|
|
return `Running packager on port ${found[1]}\n`;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function _processPackagerLine(line) {
|
|
// [10:02:59 AM]
|
|
let timestampRe = /\s*\[\d+\:\d+\:\d+\ (AM)?(PM)?\]\s+/;
|
|
// [11/8/2016, 10:02:59 AM]
|
|
let sdk11AndUpTimestampRe = /\s*\[\d+\/\d+\/\d+, \d+\:\d+\:\d+\ (AM)?(PM)?\]\s+/;
|
|
return line.replace(timestampRe, '').replace(sdk11AndUpTimestampRe, '');
|
|
}
|
|
|
|
function _parseModuleResolutionError(projectRoot, errorMessage) {
|
|
let parts = errorMessage.split(' from ');
|
|
let moduleName = parts[0].replace(/.*?Unable to resolve module /, '').replace(/`/g, '').trim();
|
|
let path = parts[1].replace(/`: Module .*/, '').replace(/`/, '').trim();
|
|
let relativePath = path.replace(projectRoot, '').trim();
|
|
|
|
return {
|
|
moduleName,
|
|
relativePath,
|
|
path
|
|
};
|
|
}
|
|
|
|
const NODE_STDLIB_MODULES = ['assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'https', 'net', 'os', 'path', 'punycode', 'querystring', 'readline', 'repl', 'stream', 'string_decoder', 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'zlib'];
|
|
|
|
function _logModuleResolutionError(projectRoot, errorMessage) {
|
|
let { moduleName, relativePath, path } = _parseModuleResolutionError(projectRoot, errorMessage);
|
|
|
|
const DOCS_PAGE_URL = 'https://docs.expo.io/versions/latest/introduction/faq.html#can-i-use-nodejs-packages-with-expo';
|
|
|
|
if (NODE_STDLIB_MODULES.includes(moduleName)) {
|
|
if (path.includes('node_modules')) {
|
|
(_ProjectUtils || _load_ProjectUtils()).logError(projectRoot, 'packager', `The package at ".${relativePath}" attempted to import the Node standard library module "${moduleName}". It failed because React Native does not include the Node standard library. Read more at ${DOCS_PAGE_URL}`);
|
|
} else {
|
|
(_ProjectUtils || _load_ProjectUtils()).logError(projectRoot, 'packager', `You attempted attempted to import the Node standard library module "${moduleName}" from ".${relativePath}". It failed because React Native does not include the Node standard library. Read more at ${DOCS_PAGE_URL}`);
|
|
}
|
|
} else {
|
|
(_ProjectUtils || _load_ProjectUtils()).logError(projectRoot, 'packager', `Unable to resolve ${moduleName}" from "./${relativePath}"`);
|
|
}
|
|
}
|
|
|
|
function _logPackagerOutput(projectRoot, level, data) {
|
|
let output = data.toString();
|
|
if (output.includes('─────')) {
|
|
output = _stripPackagerOutputBox(output);
|
|
if (output) {
|
|
(_ProjectUtils || _load_ProjectUtils()).logInfo(projectRoot, 'expo', output);
|
|
}
|
|
return;
|
|
}
|
|
if (!output) {
|
|
return;
|
|
} // Fix watchman if it's being dumb
|
|
if ((_Watchman || _load_Watchman()).isPlatformSupported() && output.includes('watchman watch-del')) {
|
|
// Skip this as it is likely no longer needed. We may want to add a message
|
|
// in this place in the event that there are still issues reported that could
|
|
// be resolved by restarting watchman when the log output includes this message.
|
|
// _restartWatchmanAsync(projectRoot);
|
|
return;
|
|
}
|
|
|
|
if (output.includes('Unable to resolve module')) {
|
|
_logModuleResolutionError(projectRoot, output);
|
|
}
|
|
|
|
// Temporarily hide warnings about duplicate providesModule declarations
|
|
// under react-native
|
|
if (_isIgnorableDuplicateModuleWarning(projectRoot, level, output)) {
|
|
(_ProjectUtils || _load_ProjectUtils()).logDebug(projectRoot, 'expo', `Suppressing @providesModule warning: ${output}`, 'project-suppress-providesmodule-warning');
|
|
return;
|
|
}
|
|
let lines = output.split(/\r?\n/);
|
|
for (let i = 0; i < lines.length; i++) {
|
|
lines[i] = _processPackagerLine(lines[i]);
|
|
}
|
|
output = lines.join('\n');
|
|
if (level === 'info') {
|
|
(_ProjectUtils || _load_ProjectUtils()).logInfo(projectRoot, 'packager', output);
|
|
} else {
|
|
(_ProjectUtils || _load_ProjectUtils()).logError(projectRoot, 'packager', output);
|
|
}
|
|
}
|
|
|
|
function _isIgnorableDuplicateModuleWarning(projectRoot, level, output) {
|
|
if (level !== 'error' || !output.startsWith('jest-haste-map: @providesModule naming collision:')) {
|
|
return false;
|
|
}
|
|
|
|
let reactNativeNodeModulesPath = _path.default.join(projectRoot, 'node_modules', 'react-native', 'node_modules');
|
|
let reactNativeNodeModulesPattern = (_lodash || _load_lodash()).default.escapeRegExp(reactNativeNodeModulesPath);
|
|
let reactNativeNodeModulesCollisionRegex = new RegExp(`Paths: ${reactNativeNodeModulesPattern}.+ collides with ${reactNativeNodeModulesPattern}.+`);
|
|
return reactNativeNodeModulesCollisionRegex.test(output);
|
|
}
|
|
|
|
function _handleDeviceLogs(projectRoot, deviceId, deviceName, logs) {
|
|
for (let i = 0; i < logs.length; i++) {
|
|
let log = logs[i];
|
|
let body = typeof log.body === 'string' ? [log.body] : log.body;
|
|
let string = body.map(obj => {
|
|
if (typeof obj === 'undefined') {
|
|
return 'undefined';
|
|
}
|
|
if (obj === 'null') {
|
|
return 'null';
|
|
}
|
|
if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') {
|
|
return obj;
|
|
}
|
|
try {
|
|
return JSON.stringify(obj);
|
|
} catch (e) {
|
|
return obj.toString();
|
|
}
|
|
}).join(' ');
|
|
let level = log.level;
|
|
let groupDepth = log.groupDepth;
|
|
let shouldHide = log.shouldHide;
|
|
let includesStack = log.includesStack;
|
|
|
|
(_ProjectUtils || _load_ProjectUtils()).logWithLevel(projectRoot, level, {
|
|
tag: 'device',
|
|
deviceId,
|
|
deviceName,
|
|
groupDepth,
|
|
shouldHide,
|
|
includesStack
|
|
}, string);
|
|
}
|
|
}
|
|
function _nodePathForProjectRoot(projectRoot) {
|
|
let paths = [];
|
|
let directory = _path.default.resolve(projectRoot);
|
|
while (true) {
|
|
paths.push(_path.default.join(directory, 'node_modules'));
|
|
let parentDirectory = _path.default.dirname(directory);
|
|
if (directory === parentDirectory) {
|
|
break;
|
|
}
|
|
directory = parentDirectory;
|
|
}
|
|
return paths.join(_path.default.delimiter);
|
|
}
|
|
//# sourceMappingURL=__sourcemaps__/Project.js.map
|