"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//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