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.

266 lines
10 KiB

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.bundleAssetsAsync = bundleAssetsAsync;
exports.prepareDetachedBuildAsync = prepareDetachedBuildAsync;
function _config() {
const data = require("@expo/config");
_config = function () {
return data;
};
return data;
}
function _jsonFile() {
const data = _interopRequireDefault(require("@expo/json-file"));
_jsonFile = function () {
return data;
};
return data;
}
function _fsExtra() {
const data = _interopRequireDefault(require("fs-extra"));
_fsExtra = function () {
return data;
};
return data;
}
function _glob() {
const data = require("glob");
_glob = function () {
return data;
};
return data;
}
function _path() {
const data = _interopRequireDefault(require("path"));
_path = 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 SERVICE_CONTEXT_PROJECT_NAME = 'exponent-view-template';
async function ensureBuildConstantsExistsIOSAsync(configFilePath) {
// EXBuildConstants is included in newer ExpoKit projects.
// create it if it doesn't exist.
const doesBuildConstantsExist = _fsExtra().default.existsSync(_path().default.join(configFilePath, 'EXBuildConstants.plist'));
if (!doesBuildConstantsExist) {
await _internal().IosPlist.createBlankAsync(configFilePath, 'EXBuildConstants');
_internal().LoggerDetach.info('Created `EXBuildConstants.plist` because it did not exist yet');
}
}
async function _getIosExpoKitVersionThrowErrorAsync(iosProjectDirectory) {
let expoKitVersion = '';
const podfileLockPath = _path().default.join(iosProjectDirectory, 'Podfile.lock');
try {
const podfileLock = await _fsExtra().default.readFile(podfileLockPath, 'utf8');
const expoKitVersionRegex = /ExpoKit\/Core\W?\(([0-9.]+)\)/gi;
const match = expoKitVersionRegex.exec(podfileLock);
if (!match) {
throw new Error('ExpoKit/Core not found');
}
expoKitVersion = match[1];
} catch (e) {
throw new Error(`Unable to read ExpoKit version from Podfile.lock. Make sure your project depends on ExpoKit. (${e})`);
}
return expoKitVersion;
}
async function readNullableConfigJsonAsync(projectDir) {
try {
return (0, _config().getConfig)(projectDir);
} catch {
return null;
}
}
async function prepareDetachedBuildIosAsync(projectDir, args) {
const config = await readNullableConfigJsonAsync(projectDir);
if (config && config.exp.name !== SERVICE_CONTEXT_PROJECT_NAME) {
return prepareDetachedUserContextIosAsync(projectDir, config.exp, args);
} else {
return prepareDetachedServiceContextIosAsync(projectDir, args);
}
}
async function prepareDetachedServiceContextIosAsync(projectDir, args) {
// service context
// TODO: very brittle hack: the paths here are hard coded to match the single workspace
// path generated inside IosShellApp. When we support more than one path, this needs to
// be smarter.
const expoRootDir = _path().default.join(projectDir, '..', '..');
const workspaceSourcePath = _path().default.join(projectDir, 'ios');
const buildFlags = _internal().StandaloneBuildFlags.createIos('Release', {
workspaceSourcePath
});
// @ts-ignore missing 9th argument
const context = _internal().StandaloneContext.createServiceContext(expoRootDir, null, null, null, /* testEnvironment */'none', buildFlags, null, null);
const {
iosProjectDirectory,
supportingDirectory
} = _internal().IosWorkspace.getPaths(context);
const expoKitVersion = await _getIosExpoKitVersionThrowErrorAsync(iosProjectDirectory);
// use prod api keys if available
const prodApiKeys = await _readDefaultApiKeysAsync(_path().default.join(context.data.expoSourcePath, '__internal__', 'keys.json'));
const {
exp
} = (0, _config().getConfig)(expoRootDir, {
skipSDKVersionRequirement: true
});
await _internal().IosPlist.modifyAsync(supportingDirectory, 'EXBuildConstants', constantsConfig => {
// verify that we are actually in a service context and not a misconfigured project
const contextType = constantsConfig.STANDALONE_CONTEXT_TYPE;
if (contextType !== 'service') {
throw new Error('Unable to configure a project which has no app.json and also no STANDALONE_CONTEXT_TYPE.');
}
constantsConfig.EXPO_RUNTIME_VERSION = expoKitVersion;
constantsConfig.API_SERVER_ENDPOINT = process.env.ENVIRONMENT === 'staging' ? 'https://staging.exp.host/--/api/v2/' : 'https://exp.host/--/api/v2/';
if (prodApiKeys) {
constantsConfig.DEFAULT_API_KEYS = prodApiKeys;
}
if (exp && exp.sdkVersion) {
constantsConfig.TEMPORARY_SDK_VERSION = exp.sdkVersion;
}
return constantsConfig;
});
}
async function _readDefaultApiKeysAsync(jsonFilePath) {
if (_fsExtra().default.existsSync(jsonFilePath)) {
const keys = {};
const allKeys = await new (_jsonFile().default)(jsonFilePath).readAsync();
const validKeys = ['AMPLITUDE_KEY', 'GOOGLE_MAPS_IOS_API_KEY'];
for (const key in allKeys) {
if (allKeys.hasOwnProperty(key) && validKeys.includes(key)) {
keys[key] = allKeys[key];
}
}
return keys;
}
return null;
}
async function prepareDetachedUserContextIosAsync(projectDir, exp, args) {
const context = _internal().StandaloneContext.createUserContext(projectDir, exp);
const {
iosProjectDirectory,
supportingDirectory
} = _internal().IosWorkspace.getPaths(context);
_internal().LoggerDetach.info(`Preparing iOS build at ${iosProjectDirectory}...`);
// These files cause @providesModule naming collisions
// but are not available until after `pod install` has run.
const podsDirectory = _path().default.join(iosProjectDirectory, 'Pods');
if (!_internal().ExponentTools.isDirectory(podsDirectory)) {
throw new Error(`Can't find directory ${podsDirectory}, make sure you've run pod install.`);
}
const rnPodDirectory = _path().default.join(podsDirectory, 'React');
if (_internal().ExponentTools.isDirectory(rnPodDirectory)) {
const rnFilesToDelete = (0, _glob().sync)('**/*.@(js|json)', {
absolute: true,
cwd: rnPodDirectory
});
if (rnFilesToDelete) {
for (let i = 0; i < rnFilesToDelete.length; i++) {
await _fsExtra().default.unlink(rnFilesToDelete[i]);
}
}
}
// insert expo development url into iOS config
if (!args.skipXcodeConfig) {
// populate EXPO_RUNTIME_VERSION from ExpoKit pod version
const expoKitVersion = await _getIosExpoKitVersionThrowErrorAsync(iosProjectDirectory);
// populate development url
const devUrl = await _internal().UrlUtils.constructManifestUrlAsync(projectDir);
// populate default api keys
const defaultApiKeys = await _readDefaultApiKeysAsync(_path().default.join(podsDirectory, 'ExpoKit', 'template-files', 'keys.json'));
await ensureBuildConstantsExistsIOSAsync(supportingDirectory);
await _internal().IosPlist.modifyAsync(supportingDirectory, 'EXBuildConstants', constantsConfig => {
constantsConfig.developmentUrl = devUrl;
constantsConfig.EXPO_RUNTIME_VERSION = expoKitVersion;
if (defaultApiKeys) {
constantsConfig.DEFAULT_API_KEYS = defaultApiKeys;
}
if (exp.sdkVersion) {
constantsConfig.TEMPORARY_SDK_VERSION = exp.sdkVersion;
}
return constantsConfig;
});
}
}
async function prepareDetachedBuildAsync(projectDir, args) {
if (args.platform === 'ios') {
await prepareDetachedBuildIosAsync(projectDir, args);
} else {
const expoBuildConstantsMatches = (0, _glob().sync)('android/**/DetachBuildConstants.java', {
absolute: true,
cwd: projectDir
});
if (expoBuildConstantsMatches && expoBuildConstantsMatches.length) {
const expoBuildConstants = expoBuildConstantsMatches[0];
const devUrl = await _internal().UrlUtils.constructManifestUrlAsync(projectDir);
await _internal().ExponentTools.regexFileAsync(/DEVELOPMENT_URL = "[^"]*";/, `DEVELOPMENT_URL = "${devUrl}";`, expoBuildConstants);
}
}
}
// args.dest: string,
// This is the path where assets will be copied to. It should be
// `$CONFIGURATION_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH` on iOS
// (see `exponent-view-template.xcodeproj/project.pbxproj` for an example)
// and `$buildDir/intermediates/assets/$targetPath` on Android (see
// `android/app/expo.gradle` for an example).
async function bundleAssetsAsync(projectDir, args) {
const options = await readNullableConfigJsonAsync(projectDir);
if (!options || options.exp.name === SERVICE_CONTEXT_PROJECT_NAME) {
// Don't run assets bundling for the service context.
return;
}
const {
exp
} = options;
const bundledManifestPath = _internal().EmbeddedAssets.getEmbeddedManifestPath(args.platform, projectDir, exp);
if (!bundledManifestPath) {
_internal().LoggerDetach.warn(`Skipped assets bundling because the '${args.platform}.publishManifestPath' key is not specified in the app manifest.`);
return;
}
let manifest;
try {
manifest = JSON.parse(await _fsExtra().default.readFile(bundledManifestPath, 'utf8'));
} catch (ex) {
throw new Error(`Error reading the manifest file. Make sure the path '${bundledManifestPath}' is correct.\n\nError: ${ex.message}`);
}
if (!manifest || !Object.keys(manifest).length) {
throw new Error(`The manifest at '${bundledManifestPath}' was empty or invalid.`);
}
await _internal().AssetBundle.bundleAsync(null, manifest.bundledAssets, args.dest, getExportUrl(manifest));
}
/**
* This function extracts the exported public URL that is set in the manifest
* when the developer runs `expo export --public-url x`. We use this to ensure
* that we fetch the resources from the appropriate place when doing builds
* against self-hosted apps.
*/
function getExportUrl(manifest) {
const {
bundleUrl
} = manifest;
if (bundleUrl.includes(_internal().AssetBundle.DEFAULT_CDN_HOST)) {
return null;
}
try {
const bundleUrlParts = bundleUrl.split('/');
return bundleUrlParts.slice(0, bundleUrlParts.length - 2).join('/');
} catch {
throw Error(`Expected bundleUrl to be of the format https://domain/bundles/bundle-hash-id, ${bundleUrl} does not follow this format.`);
}
}
//# sourceMappingURL=Detach.js.map