You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

400 lines
19 KiB

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.configureAsync = configureAsync;
exports.getEmbeddedManifestPath = getEmbeddedManifestPath;
exports.getIOSPaths = getIOSPaths;
exports.shouldEmbedAssetsForExpoUpdates = shouldEmbedAssetsForExpoUpdates;
function _config() {
const data = require("@expo/config");
_config = function () {
return data;
};
return data;
}
function _configPlugins() {
const data = require("@expo/config-plugins");
_configPlugins = function () {
return data;
};
return data;
}
function _plist() {
const data = _interopRequireDefault(require("@expo/plist"));
_plist = 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 _semver() {
const data = _interopRequireDefault(require("semver"));
_semver = function () {
return data;
};
return data;
}
function _internal() {
const data = require("./internal");
_internal = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const PLACEHOLDER_URL = 'YOUR-APP-URL-HERE';
const FYI_URL = 'https://expo.fyi/expo-updates-config';
async function configureAsync(config) {
await _maybeWriteArtifactsToDiskAsync(config);
await _maybeConfigureExpoKitEmbeddedAssetsAsync(config);
await _maybeRunModifiedExpoUpdatesPluginAsync(config);
}
function getEmbeddedManifestPath(platform, projectRoot, exp) {
if (platform === 'ios') {
return exp.ios && exp.ios.publishManifestPath ? exp.ios.publishManifestPath : _getDefaultEmbeddedManifestPath(platform, projectRoot, exp);
} else if (platform === 'android') {
return exp.android && exp.android.publishManifestPath ? exp.android.publishManifestPath : _getDefaultEmbeddedManifestPath(platform, projectRoot, exp);
}
return _getDefaultEmbeddedManifestPath(platform, projectRoot, exp);
}
function _getDefaultEmbeddedManifestPath(platform, projectRoot, exp) {
return _path().default.join(_getDefaultEmbeddedAssetDir(platform, projectRoot, exp), 'app.manifest');
}
function _getDefaultEmbeddedBundlePath(platform, projectRoot, exp) {
return _path().default.join(_getDefaultEmbeddedAssetDir(platform, projectRoot, exp), 'app.bundle');
}
function _getDefaultEmbeddedAssetDir(platform, projectRoot, exp) {
if (platform === 'ios') {
const {
iosSupportingDirectory
} = getIOSPaths(projectRoot);
return iosSupportingDirectory;
} else if (platform === 'android') {
return _path().default.join(projectRoot, 'android', 'app', 'src', 'main', 'assets');
} else {
throw new Error('Embedding assets is not supported for platform ' + platform);
}
}
function shouldEmbedAssetsForExpoUpdates(projectRoot, exp, pkg, target) {
var _pkg$dependencies;
if (!((_pkg$dependencies = pkg.dependencies) !== null && _pkg$dependencies !== void 0 && _pkg$dependencies['expo-updates']) || target !== 'bare') {
return false;
}
// semver.coerce can return null
const expoUpdatesVersion = _semver().default.coerce(pkg.dependencies['expo-updates']);
// expo-updates 0.1.x relies on expo-cli automatically embedding the manifest and bundle
if (expoUpdatesVersion && _semver().default.satisfies(expoUpdatesVersion, '~0.1.0')) {
return true;
}
// We also want to support developers who had expo-updates 0.1.x and upgraded but still rely on
// expo-cli's automatic embedding. If the files already exist we can assume we need to update them
if (_fsExtra().default.existsSync(_getDefaultEmbeddedBundlePath('android', projectRoot, exp)) || _fsExtra().default.existsSync(_getDefaultEmbeddedManifestPath('android', projectRoot, exp)) || _fsExtra().default.existsSync(_getDefaultEmbeddedBundlePath('ios', projectRoot, exp)) || _fsExtra().default.existsSync(_getDefaultEmbeddedManifestPath('ios', projectRoot, exp))) {
return true;
}
return false;
}
async function _maybeWriteArtifactsToDiskAsync(config) {
var _exp$android, _exp$android2, _exp$ios, _exp$ios2;
const {
projectRoot,
pkg,
exp,
iosManifest,
iosBundle,
androidManifest,
androidBundle,
target
} = config;
let androidBundlePath;
let androidManifestPath;
let iosBundlePath;
let iosManifestPath;
if (shouldEmbedAssetsForExpoUpdates(projectRoot, exp, pkg, target)) {
const defaultAndroidDir = _getDefaultEmbeddedAssetDir('android', projectRoot, exp);
const defaultIosDir = _getDefaultEmbeddedAssetDir('ios', projectRoot, exp);
await _fsExtra().default.ensureDir(defaultIosDir);
await _fsExtra().default.ensureDir(defaultAndroidDir);
androidBundlePath = _getDefaultEmbeddedBundlePath('android', projectRoot, exp);
androidManifestPath = _getDefaultEmbeddedManifestPath('android', projectRoot, exp);
iosBundlePath = _getDefaultEmbeddedBundlePath('ios', projectRoot, exp);
iosManifestPath = _getDefaultEmbeddedManifestPath('ios', projectRoot, exp);
if (!_fsExtra().default.existsSync(iosBundlePath) || !_fsExtra().default.existsSync(iosManifestPath)) {
_internal().Logger.global.warn('Creating app.manifest and app.bundle inside of your ios/<project>/Supporting directory.\nBe sure to add these files to your Xcode project. More info at https://expo.fyi/embedded-assets');
}
}
// allow custom overrides
if ((_exp$android = exp.android) !== null && _exp$android !== void 0 && _exp$android.publishBundlePath) {
androidBundlePath = exp.android.publishBundlePath;
}
if ((_exp$android2 = exp.android) !== null && _exp$android2 !== void 0 && _exp$android2.publishManifestPath) {
androidManifestPath = exp.android.publishManifestPath;
}
if ((_exp$ios = exp.ios) !== null && _exp$ios !== void 0 && _exp$ios.publishBundlePath) {
iosBundlePath = exp.ios.publishBundlePath;
}
if ((_exp$ios2 = exp.ios) !== null && _exp$ios2 !== void 0 && _exp$ios2.publishManifestPath) {
iosManifestPath = exp.ios.publishManifestPath;
}
if (androidBundlePath) {
await (0, _internal().writeArtifactSafelyAsync)(projectRoot, 'android.publishBundlePath', androidBundlePath, androidBundle);
}
if (androidManifestPath) {
await (0, _internal().writeArtifactSafelyAsync)(projectRoot, 'android.publishManifestPath', androidManifestPath, JSON.stringify(androidManifest));
}
if (iosBundlePath) {
await (0, _internal().writeArtifactSafelyAsync)(projectRoot, 'ios.publishBundlePath', iosBundlePath, iosBundle);
}
if (iosManifestPath) {
await (0, _internal().writeArtifactSafelyAsync)(projectRoot, 'ios.publishManifestPath', iosManifestPath, JSON.stringify(iosManifest));
}
}
async function _maybeConfigureExpoKitEmbeddedAssetsAsync(config) {
const {
projectRoot,
exp,
releaseChannel,
androidManifestUrl,
androidManifest
} = config;
const context = _internal().StandaloneContext.createUserContext(projectRoot, exp);
const {
supportingDirectory
} = _internal().IosWorkspace.getPaths(context);
// iOS ExpoKit
if (releaseChannel && _fsExtra().default.existsSync(_path().default.join(supportingDirectory, 'EXShell.plist'))) {
// This is an ExpoKit app, set properties in EXShell.plist
await _internal().IosPlist.modifyAsync(supportingDirectory, 'EXShell', shellPlist => {
shellPlist.releaseChannel = releaseChannel;
return shellPlist;
});
}
// Android ExpoKit
const constantsPath = _path().default.join(projectRoot, 'android', 'app', 'src', 'main', 'java', 'host', 'exp', 'exponent', 'generated', 'AppConstants.java');
if (_fsExtra().default.existsSync(constantsPath)) {
// This is an ExpoKit app
// We need to add EmbeddedResponse instances on Android to tell the runtime
// that the shell app manifest and bundle is packaged.
await _internal().ExponentTools.deleteLinesInFileAsync(`START EMBEDDED RESPONSES`, `END EMBEDDED RESPONSES`, constantsPath);
await _internal().ExponentTools.regexFileAsync('// ADD EMBEDDED RESPONSES HERE', `
// ADD EMBEDDED RESPONSES HERE
// START EMBEDDED RESPONSES
embeddedResponses.add(new Constants.EmbeddedResponse("${androidManifestUrl}", "assets://shell-app-manifest.json", "application/json"));
embeddedResponses.add(new Constants.EmbeddedResponse("${androidManifest.bundleUrl}", "assets://shell-app.bundle", "application/javascript"));
// END EMBEDDED RESPONSES`, constantsPath);
if (releaseChannel) {
await _internal().ExponentTools.regexFileAsync(/RELEASE_CHANNEL = "[^"]*"/, `RELEASE_CHANNEL = "${releaseChannel}"`, constantsPath);
}
}
}
/**
* Guess if this is a user's first publish and run a slightly modified expo-updates plugin.
* If it is not their first publish and a config mismatch is noticed, log warnings.
*/
async function _maybeRunModifiedExpoUpdatesPluginAsync(config) {
var _config$pkg$dependenc;
if (!((_config$pkg$dependenc = config.pkg.dependencies) !== null && _config$pkg$dependenc !== void 0 && _config$pkg$dependenc['expo-updates']) || config.target === 'managed') {
return;
}
const {
projectRoot,
exp,
releaseChannel,
iosManifestUrl,
androidManifestUrl
} = config;
const {
iosSupportingDirectory: supportingDirectory
} = getIOSPaths(projectRoot);
// iOS expo-updates
let isLikelyFirstIOSPublish = false;
const expoPlistPath = _path().default.join(supportingDirectory, 'Expo.plist');
if (_fsExtra().default.existsSync(expoPlistPath)) {
let expoPlistForProject = _plist().default.parse(await _fsExtra().default.readFileSync(expoPlistPath, 'utf8'));
const currentlyConfiguredExpoPlist = {
...expoPlistForProject
};
// The username is only used for defining a default updates URL.
// Since we overwrite the URL below the username is superfluous.
expoPlistForProject = _configPlugins().IOSConfig.Updates.setUpdatesConfig(projectRoot, exp, expoPlistForProject, /*expoUsername*/null);
// overwrite the URL defined in IOSConfig.Updates.setUpdatesConfig
expoPlistForProject[_configPlugins().IOSConfig.Updates.Config.UPDATE_URL] = iosManifestUrl;
// set a release channel (not done in updates plugin)
if (releaseChannel) {
expoPlistForProject[_configPlugins().IOSConfig.Updates.Config.RELEASE_CHANNEL] = releaseChannel;
}
// If we guess that this is a users first publish, modify the native code to match
// what is configured.
const configuredIOSUpdatesURL = currentlyConfiguredExpoPlist[_configPlugins().IOSConfig.Updates.Config.UPDATE_URL];
if (configuredIOSUpdatesURL === PLACEHOLDER_URL) {
isLikelyFirstIOSPublish = true;
_fsExtra().default.writeFileSync(expoPlistPath, _plist().default.build(expoPlistForProject));
} else {
// Log warnings if this is not the first publish and critical properties seem misconfigured
const {
UPDATE_URL,
SDK_VERSION,
RUNTIME_VERSION,
RELEASE_CHANNEL
} = _configPlugins().IOSConfig.Updates.Config;
for (const key of [UPDATE_URL, SDK_VERSION, RUNTIME_VERSION, RELEASE_CHANNEL]) {
let currentlyConfiguredValue = currentlyConfiguredExpoPlist[key];
const inferredValue = expoPlistForProject[key];
if (key === RELEASE_CHANNEL && inferredValue) {
var _currentlyConfiguredV;
// A client with an undefined release channel is mapped to
// 'default' in the server, so avoid logging an unneccessary warning.
currentlyConfiguredValue = (_currentlyConfiguredV = currentlyConfiguredValue) !== null && _currentlyConfiguredV !== void 0 ? _currentlyConfiguredV : 'default';
}
if (currentlyConfiguredValue !== inferredValue) {
let message;
switch (key) {
case RELEASE_CHANNEL:
{
message = `The value passed to the --release-channel flag is to "${inferredValue}", but it is set to "${currentlyConfiguredValue}".`;
break;
}
case UPDATE_URL:
case SDK_VERSION:
case RUNTIME_VERSION:
default:
message = `${key} is inferred to be "${inferredValue}", but it is set to "${currentlyConfiguredValue}".`;
}
_configPlugins().WarningAggregator.addWarningIOS(`Expo.plist key: "${key}"`, message, FYI_URL);
}
}
}
}
// Android expo-updates
let isLikelyFirstAndroidPublish = false;
const androidManifestXmlPath = _path().default.join(projectRoot, 'android', 'app', 'src', 'main', 'AndroidManifest.xml');
const AndroidManifestKeyForUpdateURL = _configPlugins().AndroidConfig.Updates.Config.UPDATE_URL;
if (_fsExtra().default.existsSync(androidManifestXmlPath)) {
var _currentConfiguredMan, _currentConfiguredMan2, _currentlyConfiguredM;
const currentlyConfiguredAndroidManifest = await _configPlugins().AndroidConfig.Manifest.readAndroidManifestAsync(androidManifestXmlPath);
const currentConfiguredManifestApplication = _configPlugins().AndroidConfig.Manifest.getMainApplicationOrThrow(currentlyConfiguredAndroidManifest);
const currentlyConfiguredMetaDataAttributes = (_currentConfiguredMan = (_currentConfiguredMan2 = currentConfiguredManifestApplication['meta-data']) === null || _currentConfiguredMan2 === void 0 ? void 0 : _currentConfiguredMan2.map(md => md['$'])) !== null && _currentConfiguredMan !== void 0 ? _currentConfiguredMan : [];
// The username is only used for defining a default updates URL.
// Since we overwrite the URL below the username is superfluous.
const inferredAndroidManifest = _configPlugins().AndroidConfig.Updates.setUpdatesConfig(projectRoot, exp, currentlyConfiguredAndroidManifest, /*username*/null);
const inferredMainApplication = _configPlugins().AndroidConfig.Manifest.getMainApplicationOrThrow(inferredAndroidManifest);
// overwrite the URL defined in AndroidConfig.Updates.setUpdatesConfig
_configPlugins().AndroidConfig.Manifest.addMetaDataItemToMainApplication(inferredMainApplication, AndroidManifestKeyForUpdateURL, androidManifestUrl);
// set a release channel (not done in updates plugin)
if (releaseChannel) {
_configPlugins().AndroidConfig.Manifest.addMetaDataItemToMainApplication(inferredMainApplication, _configPlugins().AndroidConfig.Updates.Config.RELEASE_CHANNEL, releaseChannel);
}
// If we guess that this is a users first publish, modify the native code to match
// what is configured.
const currentlyConfiguredAndroidUpdateURL = (_currentlyConfiguredM = currentlyConfiguredMetaDataAttributes.find(x => x['android:name'] === _configPlugins().AndroidConfig.Updates.Config.UPDATE_URL)) === null || _currentlyConfiguredM === void 0 ? void 0 : _currentlyConfiguredM['android:value'];
if (currentlyConfiguredAndroidUpdateURL === PLACEHOLDER_URL) {
isLikelyFirstAndroidPublish = true;
await _configPlugins().AndroidConfig.Manifest.writeAndroidManifestAsync(androidManifestXmlPath, inferredAndroidManifest);
} else {
var _inferredMainApplicat;
// Log warnings if this is not the first publish and critical properties seem misconfigured
const inferredMainApplication = _configPlugins().AndroidConfig.Manifest.getMainApplicationOrThrow(inferredAndroidManifest);
const inferredMetaDataAttributes = (_inferredMainApplicat = inferredMainApplication['meta-data']) === null || _inferredMainApplicat === void 0 ? void 0 : _inferredMainApplicat.map(md => md['$']);
const {
UPDATE_URL,
SDK_VERSION,
RUNTIME_VERSION,
RELEASE_CHANNEL
} = _configPlugins().AndroidConfig.Updates.Config;
for (const key of [UPDATE_URL, SDK_VERSION, RUNTIME_VERSION, RELEASE_CHANNEL]) {
var _inferredMetaDataAttr, _currentlyConfiguredM2;
const inferredValue = (_inferredMetaDataAttr = inferredMetaDataAttributes.find(x => x['android:name'] === key)) === null || _inferredMetaDataAttr === void 0 ? void 0 : _inferredMetaDataAttr['android:value'];
let currentlyConfiguredValue = (_currentlyConfiguredM2 = currentlyConfiguredMetaDataAttributes.find(x => x['android:name'] === key)) === null || _currentlyConfiguredM2 === void 0 ? void 0 : _currentlyConfiguredM2['android:value'];
if (key === RELEASE_CHANNEL && inferredValue) {
var _currentlyConfiguredV2;
// A client with an undefined release channel is mapped to
// 'default' in the server, so avoid logging an unneccessary warning.
currentlyConfiguredValue = (_currentlyConfiguredV2 = currentlyConfiguredValue) !== null && _currentlyConfiguredV2 !== void 0 ? _currentlyConfiguredV2 : 'default';
}
if (inferredValue !== currentlyConfiguredValue) {
let message;
switch (key) {
case RELEASE_CHANNEL:
{
message = `The value passed to the --release-channel flag is "${inferredValue}", but it is set to "${currentlyConfiguredValue}".`;
break;
}
case UPDATE_URL:
case SDK_VERSION:
case RUNTIME_VERSION:
default:
message = `The inferred value is "${inferredValue}", but it is set to "${currentlyConfiguredValue}".`;
}
_configPlugins().WarningAggregator.addWarningAndroid(`AndroidManifest.xml key "${key}"`, message, FYI_URL);
}
}
}
}
if (isLikelyFirstIOSPublish || isLikelyFirstAndroidPublish) {
let platformSpecificMessage;
if (isLikelyFirstIOSPublish && !isLikelyFirstAndroidPublish) {
platformSpecificMessage = '🚀 It looks like this your first iOS publish for this project! ' + `We've automatically set some configuration values in Expo.plist. `;
} else if (!isLikelyFirstIOSPublish && isLikelyFirstAndroidPublish) {
platformSpecificMessage = '🚀 It looks like this your first Android publish for this project! ' + `We've automatically set some configuration values in AndroidManifest.xml. `;
} else {
platformSpecificMessage = '🚀 It looks like this your first publish for this project! ' + `We've automatically set some configuration values in Expo.plist and AndroidManifest.xml. `;
}
_internal().Logger.global.warn(platformSpecificMessage + `You'll need to make and release a new build before your users can download the update ` + 'you just published.');
}
}
/** The code below here is duplicated from expo-cli currently **/
// TODO: it's silly and kind of fragile that we look at app config to determine
// the ios project paths. Overall this function needs to be revamped, just a
// placeholder for now! Make this more robust when we support applying config
// at any time (currently it's only applied on eject).
function getIOSPaths(projectRoot) {
const {
exp
} = (0, _config().getConfig)(projectRoot, {
skipSDKVersionRequirement: true
});
const projectName = exp.name;
if (!projectName) {
throw new Error('Your project needs a name in app.json/app.config.js.');
}
const iosProjectDirectory = _path().default.join(projectRoot, 'ios', _configPlugins().IOSConfig.XcodeUtils.sanitizedName(projectName));
const iosSupportingDirectory = _path().default.join(projectRoot, 'ios', _configPlugins().IOSConfig.XcodeUtils.sanitizedName(projectName), 'Supporting');
const iconPath = _path().default.join(iosProjectDirectory, 'Assets.xcassets', 'AppIcon.appiconset');
return {
projectName,
iosProjectDirectory,
iosSupportingDirectory,
iconPath
};
}
//# sourceMappingURL=EmbeddedAssets.js.map