@expo/xdl
Version:
The Expo Development Library
668 lines (521 loc) • 18.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.buildPublishBundlesAsync = buildPublishBundlesAsync;
exports.publishAsync = publishAsync;
function _config() {
const data = require("@expo/config");
_config = function () {
return data;
};
return data;
}
function _devServer() {
const data = require("@expo/dev-server");
_devServer = function () {
return data;
};
return data;
}
function _axios() {
const data = _interopRequireDefault(require("axios"));
_axios = function () {
return data;
};
return data;
}
function _chalk() {
const data = _interopRequireDefault(require("chalk"));
_chalk = function () {
return data;
};
return data;
}
function _formData() {
const data = _interopRequireDefault(require("form-data"));
_formData = function () {
return data;
};
return data;
}
function _fsExtra() {
const data = _interopRequireDefault(require("fs-extra"));
_fsExtra = function () {
return data;
};
return data;
}
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _Analytics() {
const data = _interopRequireDefault(require("../Analytics"));
_Analytics = function () {
return data;
};
return data;
}
function _ApiV() {
const data = _interopRequireDefault(require("../ApiV2"));
_ApiV = function () {
return data;
};
return data;
}
function _Config() {
const data = _interopRequireDefault(require("../Config"));
_Config = function () {
return data;
};
return data;
}
function EmbeddedAssets() {
const data = _interopRequireWildcard(require("../EmbeddedAssets"));
EmbeddedAssets = function () {
return data;
};
return data;
}
function _Env() {
const data = require("../Env");
_Env = function () {
return data;
};
return data;
}
function _Logger() {
const data = _interopRequireDefault(require("../Logger"));
_Logger = function () {
return data;
};
return data;
}
function _ProjectAssets() {
const data = require("../ProjectAssets");
_ProjectAssets = function () {
return data;
};
return data;
}
function Sentry() {
const data = _interopRequireWildcard(require("../Sentry"));
Sentry = function () {
return data;
};
return data;
}
function UrlUtils() {
const data = _interopRequireWildcard(require("../UrlUtils"));
UrlUtils = function () {
return data;
};
return data;
}
function _User() {
const data = _interopRequireDefault(require("../User"));
_User = function () {
return data;
};
return data;
}
function _XDLError() {
const data = _interopRequireDefault(require("../XDLError"));
_XDLError = function () {
return data;
};
return data;
}
function ExponentTools() {
const data = _interopRequireWildcard(require("../detach/ExponentTools"));
ExponentTools = function () {
return data;
};
return data;
}
function TableText() {
const data = _interopRequireWildcard(require("../logs/TableText"));
TableText = function () {
return data;
};
return data;
}
function _TerminalLink() {
const data = require("../logs/TerminalLink");
_TerminalLink = function () {
return data;
};
return data;
}
function _startLegacyReactNativeServerAsync() {
const data = require("../start/startLegacyReactNativeServerAsync");
_startLegacyReactNativeServerAsync = function () {
return data;
};
return data;
}
function _resolveEntryPoint() {
const data = require("../tools/resolveEntryPoint");
_resolveEntryPoint = function () {
return data;
};
return data;
}
function Doctor() {
const data = _interopRequireWildcard(require("./Doctor"));
Doctor = function () {
return data;
};
return data;
}
function ProjectUtils() {
const data = _interopRequireWildcard(require("./ProjectUtils"));
ProjectUtils = function () {
return data;
};
return data;
}
function _getPublishExpConfigAsync() {
const data = require("./getPublishExpConfigAsync");
_getPublishExpConfigAsync = function () {
return data;
};
return data;
}
function _runHook() {
const data = require("./runHook");
_runHook = function () {
return data;
};
return data;
}
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const MINIMUM_BUNDLE_SIZE = 500;
async function buildPublishBundlesAsync(projectRoot, publishOptions = {}, bundleOptions) {
if (!bundleOptions.useDevServer) {
try {
await (0, _startLegacyReactNativeServerAsync().startReactNativeServerAsync)({
projectRoot,
options: {
nonPersistent: true,
maxWorkers: publishOptions.maxWorkers,
target: publishOptions.target,
reset: publishOptions.resetCache
},
verbose: !publishOptions.quiet
});
return await fetchPublishBundlesAsync(projectRoot);
} finally {
await (0, _startLegacyReactNativeServerAsync().stopReactNativeServerAsync)(projectRoot);
}
}
const isLegacy = (0, _config().isLegacyImportsEnabled)((0, _config().getConfig)(projectRoot, {
skipSDKVersionRequirement: true
}).exp); // If not legacy, delete the target option to prevent warnings from being thrown.
if (!isLegacy) {
delete publishOptions.target;
}
const platforms = ['android', 'ios'];
const [android, ios] = await (0, _devServer().bundleAsync)(projectRoot, {
target: publishOptions.target,
resetCache: publishOptions.resetCache,
logger: ProjectUtils().getLogger(projectRoot),
quiet: publishOptions.quiet
}, platforms.map(platform => ({
platform,
entryPoint: (0, _resolveEntryPoint().resolveEntryPoint)(projectRoot, platform),
dev: bundleOptions.dev
})));
return {
android,
ios
};
} // Fetch iOS and Android bundles for publishing
async function fetchPublishBundlesAsync(projectRoot, opts) {
const entryPoint = (0, _resolveEntryPoint().resolveEntryPoint)(projectRoot);
const publishUrl = await UrlUtils().constructPublishUrlAsync(projectRoot, entryPoint, undefined, opts);
const sourceMapUrl = await UrlUtils().constructSourceMapUrlAsync(projectRoot, entryPoint);
const assetsUrl = await UrlUtils().constructAssetsUrlAsync(projectRoot, entryPoint);
_Logger().default.global.info('Building iOS bundle');
const iosBundle = await _getForPlatformAsync(projectRoot, publishUrl, 'ios', {
errorCode: 'INVALID_BUNDLE',
minLength: MINIMUM_BUNDLE_SIZE
});
_Logger().default.global.info('Building Android bundle');
const androidBundle = await _getForPlatformAsync(projectRoot, publishUrl, 'android', {
errorCode: 'INVALID_BUNDLE',
minLength: MINIMUM_BUNDLE_SIZE
});
_Logger().default.global.info('Building source maps');
const iosSourceMap = await _getForPlatformAsync(projectRoot, sourceMapUrl, 'ios', {
errorCode: 'INVALID_BUNDLE',
minLength: MINIMUM_BUNDLE_SIZE
});
const androidSourceMap = await _getForPlatformAsync(projectRoot, sourceMapUrl, 'android', {
errorCode: 'INVALID_BUNDLE',
minLength: MINIMUM_BUNDLE_SIZE
});
_Logger().default.global.info('Building asset maps');
const iosAssetsJson = await _getForPlatformAsync(projectRoot, assetsUrl, 'ios', {
errorCode: 'INVALID_ASSETS'
});
const androidAssetsJson = await _getForPlatformAsync(projectRoot, assetsUrl, 'android', {
errorCode: 'INVALID_ASSETS'
});
return {
android: {
code: androidBundle,
map: androidSourceMap,
assets: JSON.parse(androidAssetsJson)
},
ios: {
code: iosBundle,
map: iosSourceMap,
assets: JSON.parse(iosAssetsJson)
}
};
}
async function _getForPlatformAsync(projectRoot, url, platform, {
errorCode,
minLength
}) {
const fullUrl = `${url}&platform=${platform}`;
let response;
try {
response = await _axios().default.request({
url: fullUrl,
responseType: 'text',
// Workaround for https://github.com/axios/axios/issues/907.
// Without transformResponse, axios will parse the body as JSON regardless of the responseType/
transformResponse: [data => data],
proxy: false,
validateStatus: status => status === 200,
headers: {
'Exponent-Platform': platform
}
});
} catch (error) {
if (error.response) {
if (error.response.data) {
let body;
try {
body = JSON.parse(error.response.data);
} catch (e) {
ProjectUtils().logError(projectRoot, 'expo', error.response.data);
}
if (body) {
if (body.message) {
ProjectUtils().logError(projectRoot, 'expo', body.message);
} else {
ProjectUtils().logError(projectRoot, 'expo', error.response.data);
}
}
}
throw new (_XDLError().default)(errorCode, `Packager URL ${fullUrl} returned unexpected code ${error.response.status}. ` + '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.');
} else {
throw error;
}
}
if (!response.data || minLength && response.data.length < minLength) {
throw new (_XDLError().default)(errorCode, `Body is: ${response.data}`);
}
return response.data;
}
async function publishAsync(projectRoot, options = {}) {
var _options$target, _exp$android, _exp$ios, _exp$ios2, _exp$android2, _options$releaseChann;
options.target = (_options$target = options.target) !== null && _options$target !== void 0 ? _options$target : (0, _config().getDefaultTarget)(projectRoot);
const target = options.target;
const user = await _User().default.ensureLoggedInAsync();
if ((0, _Env().isDebug)()) {
console.log();
console.log('Publish Assets:');
console.log(`- Asset target: ${target}`);
console.log();
}
_Analytics().default.logEvent('Publish', {
projectRoot,
developerTool: _Config().default.developerTool
});
const validationStatus = await Doctor().validateWithNetworkAsync(projectRoot);
if (validationStatus === Doctor().ERROR || validationStatus === Doctor().FATAL) {
throw new (_XDLError().default)('PUBLISH_VALIDATION_ERROR', "Couldn't publish because errors were found. (See logs above.) Please fix the errors and try again.");
} // Get project config
const {
exp,
pkg,
hooks
} = await (0, _getPublishExpConfigAsync().getPublishExpConfigAsync)(projectRoot, options); // Exit early if kernel builds are created with robot users
if (exp.isKernel && user.kind === 'robot') {
throw new (_XDLError().default)('ROBOT_ACCOUNT_ERROR', 'Kernel builds are not available for robot users');
} // TODO: refactor this out to a function, throw error if length doesn't match
const validPostPublishHooks = (0, _runHook().prepareHooks)(hooks, 'postPublish', projectRoot);
const bundles = await buildPublishBundlesAsync(projectRoot, options, {
useDevServer: (0, _Env().shouldUseDevServer)(exp)
});
const androidBundle = bundles.android.code;
const iosBundle = bundles.ios.code;
const files = [['index.ios.js', bundles.ios.code], ['index.android.js', bundles.android.code]]; // Account for inline source maps
if (bundles.ios.map) {
files.push([_chalk().default.dim('index.ios.js.map'), bundles.ios.map]);
}
if (bundles.android.map) {
files.push([_chalk().default.dim('index.android.js.map'), bundles.android.map]);
}
_Logger().default.global.info('');
_Logger().default.global.info(TableText().createFilesTable(files));
_Logger().default.global.info('');
_Logger().default.global.info(`💡 JavaScript bundle sizes affect startup time. ${_chalk().default.dim((0, _TerminalLink().learnMore)(`https://expo.fyi/javascript-bundle-sizes`))}`);
_Logger().default.global.info('');
await (0, _ProjectAssets().publishAssetsAsync)({
projectRoot,
exp,
bundles
});
const hasHooks = validPostPublishHooks.length > 0;
const shouldPublishAndroidMaps = !!((_exp$android = exp.android) === null || _exp$android === void 0 ? void 0 : _exp$android.publishSourceMapPath);
const shouldPublishIosMaps = !!((_exp$ios = exp.ios) === null || _exp$ios === void 0 ? void 0 : _exp$ios.publishSourceMapPath);
const androidSourceMap = hasHooks || shouldPublishAndroidMaps ? bundles.android.map : null;
const iosSourceMap = hasHooks || shouldPublishIosMaps ? bundles.ios.map : null;
let response;
try {
response = await _uploadArtifactsAsync({
pkg,
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.json or app.config.js.`);
}
Sentry().captureException(e);
throw e;
}
let androidManifest = {};
let iosManifest = {};
if (validPostPublishHooks.length || ((_exp$ios2 = exp.ios) === null || _exp$ios2 === void 0 ? void 0 : _exp$ios2.publishManifestPath) || ((_exp$android2 = exp.android) === null || _exp$android2 === void 0 ? void 0 : _exp$android2.publishManifestPath) || EmbeddedAssets().shouldEmbedAssetsForExpoUpdates(projectRoot, exp, pkg, target)) {
[androidManifest, iosManifest] = await Promise.all([ExponentTools().getManifestAsync(response.url, {
'Exponent-SDK-Version': exp.sdkVersion,
'Exponent-Platform': 'android',
'Expo-Release-Channel': options.releaseChannel,
Accept: 'application/expo+json,application/json'
}), ExponentTools().getManifestAsync(response.url, {
'Exponent-SDK-Version': exp.sdkVersion,
'Exponent-Platform': 'ios',
'Expo-Release-Channel': options.releaseChannel,
Accept: 'application/expo+json,application/json'
})]);
const hookOptions = {
url: response.url,
exp,
iosBundle,
iosSourceMap,
iosManifest,
androidBundle,
androidSourceMap,
androidManifest,
projectRoot,
log: msg => {
_Logger().default.global.info({
quiet: true
}, msg);
}
};
for (const hook of validPostPublishHooks) {
_Logger().default.global.info(`Running postPublish hook: ${hook.file}`);
try {
(0, _runHook().runHook)(hook, hookOptions);
} catch (e) {
_Logger().default.global.warn(`Warning: postPublish hook '${hook.file}' failed: ${e.stack}`);
}
}
}
const fullManifestUrl = response.url.replace('exp://', 'https://');
await EmbeddedAssets().configureAsync({
projectRoot,
pkg,
exp,
releaseChannel: (_options$releaseChann = options.releaseChannel) !== null && _options$releaseChann !== void 0 ? _options$releaseChann : 'default',
iosManifestUrl: fullManifestUrl,
iosManifest,
iosBundle,
iosSourceMap,
androidManifestUrl: fullManifestUrl,
androidManifest,
androidBundle,
androidSourceMap,
target
}); // TODO: move to postPublish hook
// This method throws early when a robot account is used for a kernel build
if (exp.isKernel && user.kind !== 'robot') {
await _handleKernelPublishedAsync({
user,
exp,
projectRoot,
url: response.url
});
}
return { ...response,
url: options.releaseChannel && options.releaseChannel !== 'default' ? `${response.url}?release-channel=${options.releaseChannel}` : response.url
};
}
async function _uploadArtifactsAsync({
exp,
iosBundle,
androidBundle,
options,
pkg
}) {
_Logger().default.global.info('');
_Logger().default.global.info('Uploading JavaScript bundles');
const formData = new (_formData().default)();
formData.append('expJson', JSON.stringify(exp));
formData.append('packageJson', JSON.stringify(pkg));
formData.append('iosBundle', iosBundle, 'iosBundle');
formData.append('androidBundle', androidBundle, 'androidBundle');
formData.append('options', JSON.stringify(options));
const user = await _User().default.ensureLoggedInAsync();
const api = _ApiV().default.clientForUser(user);
return await api.uploadFormDataAsync('publish/new', formData);
}
async function _handleKernelPublishedAsync({
projectRoot,
user,
exp,
url
}) {
var _exp$kernel, _exp$kernel2;
let kernelBundleUrl = `${_Config().default.api.scheme}://${_Config().default.api.host}`;
if (_Config().default.api.port) {
kernelBundleUrl = `${kernelBundleUrl}:${_Config().default.api.port}`;
}
kernelBundleUrl = `${kernelBundleUrl}/@${user.username}/${exp.slug}/bundle`;
if ((_exp$kernel = exp.kernel) === null || _exp$kernel === void 0 ? void 0 : _exp$kernel.androidManifestPath) {
const manifest = await ExponentTools().getManifestAsync(url, {
'Exponent-SDK-Version': exp.sdkVersion,
'Exponent-Platform': 'android',
Accept: 'application/expo+json,application/json'
});
manifest.bundleUrl = kernelBundleUrl;
manifest.sdkVersion = 'UNVERSIONED';
await _fsExtra().default.writeFile(_path().default.resolve(projectRoot, exp.kernel.androidManifestPath), JSON.stringify(manifest));
}
if ((_exp$kernel2 = exp.kernel) === null || _exp$kernel2 === void 0 ? void 0 : _exp$kernel2.iosManifestPath) {
const manifest = await ExponentTools().getManifestAsync(url, {
'Exponent-SDK-Version': exp.sdkVersion,
'Exponent-Platform': 'ios',
Accept: 'application/expo+json,application/json'
});
manifest.bundleUrl = kernelBundleUrl;
manifest.sdkVersion = 'UNVERSIONED';
await _fsExtra().default.writeFile(_path().default.resolve(projectRoot, exp.kernel.iosManifestPath), JSON.stringify(manifest));
}
}
//# sourceMappingURL=../__sourcemaps__/project/publishAsync.js.map