"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getExpoConfigSourcesAsync = getExpoConfigSourcesAsync; exports.createHashSourceExternalFileAsync = createHashSourceExternalFileAsync; exports.getEasBuildSourcesAsync = getEasBuildSourcesAsync; exports.getExpoAutolinkingAndroidSourcesAsync = getExpoAutolinkingAndroidSourcesAsync; exports.getExpoCNGPatchSourcesAsync = getExpoCNGPatchSourcesAsync; exports.getExpoAutolinkingIosSourcesAsync = getExpoAutolinkingIosSourcesAsync; exports.sortExpoAutolinkingAndroidConfig = sortExpoAutolinkingAndroidConfig; exports.getConfigPluginProps = getConfigPluginProps; const spawn_async_1 = __importDefault(require("@expo/spawn-async")); const chalk_1 = __importDefault(require("chalk")); const path_1 = __importDefault(require("path")); const semver_1 = __importDefault(require("semver")); const ExpoResolver_1 = require("../ExpoResolver"); const SourceSkips_1 = require("./SourceSkips"); const Utils_1 = require("./Utils"); const Path_1 = require("../utils/Path"); const debug = require('debug')('expo:fingerprint:sourcer:Expo'); async function getExpoConfigSourcesAsync(projectRoot, config, loadedModules, options) { if (options.sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigAll) { return []; } if (config == null) { return []; } const results = []; let expoConfig = normalizeExpoConfig(config.exp, projectRoot, options); // external files in config const isAndroid = options.platforms.includes('android'); const isIos = options.platforms.includes('ios'); const splashScreenPluginProps = getConfigPluginProps(expoConfig, 'expo-splash-screen'); const externalFiles = [ // icons expoConfig.icon, isAndroid ? expoConfig.android?.icon : undefined, ...(isIos ? collectIosIcons(expoConfig.ios?.icon) : []), isAndroid ? expoConfig.android?.adaptiveIcon?.foregroundImage : undefined, isAndroid ? expoConfig.android?.adaptiveIcon?.backgroundImage : undefined, expoConfig.notification?.icon, // expo-splash-screen images splashScreenPluginProps?.image, splashScreenPluginProps?.dark?.image, isAndroid ? splashScreenPluginProps?.android?.image : undefined, isAndroid ? splashScreenPluginProps?.android?.mdpi : undefined, isAndroid ? splashScreenPluginProps?.android?.hdpi : undefined, isAndroid ? splashScreenPluginProps?.android?.xhdpi : undefined, isAndroid ? splashScreenPluginProps?.android?.xxhdpi : undefined, isAndroid ? splashScreenPluginProps?.android?.xxxhdpi : undefined, isAndroid ? splashScreenPluginProps?.android?.dark?.image : undefined, isAndroid ? splashScreenPluginProps?.android?.dark?.mdpi : undefined, isAndroid ? splashScreenPluginProps?.android?.dark?.hdpi : undefined, isAndroid ? splashScreenPluginProps?.android?.dark?.xhdpi : undefined, isAndroid ? splashScreenPluginProps?.android?.dark?.xxhdpi : undefined, isAndroid ? splashScreenPluginProps?.android?.dark?.xxxhdpi : undefined, isIos ? splashScreenPluginProps?.ios?.image : undefined, isIos ? splashScreenPluginProps?.ios?.tabletImage : undefined, isIos ? splashScreenPluginProps?.ios?.dark?.image : undefined, isIos ? splashScreenPluginProps?.ios?.dark?.tabletImage : undefined, // legacy splash images expoConfig.splash?.image, isAndroid ? expoConfig.android?.splash?.image : undefined, isAndroid ? expoConfig.android?.splash?.mdpi : undefined, isAndroid ? expoConfig.android?.splash?.hdpi : undefined, isAndroid ? expoConfig.android?.splash?.xhdpi : undefined, isAndroid ? expoConfig.android?.splash?.xxhdpi : undefined, isAndroid ? expoConfig.android?.splash?.xxxhdpi : undefined, isIos ? expoConfig.ios?.splash?.image : undefined, isIos ? expoConfig.ios?.splash?.tabletImage : undefined, // google service files isAndroid ? expoConfig.android?.googleServicesFile : undefined, isIos ? expoConfig.ios?.googleServicesFile : undefined, ] .filter((file) => Boolean(file)) .map((filePath) => ensureRelativePath(projectRoot, filePath)); const externalFileSources = (await Promise.all(externalFiles.map((file) => createHashSourceExternalFileAsync({ projectRoot, file, reason: 'expoConfigExternalFile' })))).filter(Boolean); results.push(...externalFileSources); expoConfig = postUpdateExpoConfig(expoConfig, projectRoot); results.push({ type: 'contents', id: 'expoConfig', contents: (0, Utils_1.stringifyJsonSorted)(expoConfig), reasons: ['expoConfig'], }); // config plugins const configPluginModules = (loadedModules ?? []).map((modulePath) => ({ type: 'file', filePath: (0, Path_1.toPosixPath)(modulePath), reasons: ['expoConfigPlugins'], })); results.push(...configPluginModules); return results; } function normalizeExpoConfig(config, projectRoot, options) { // Deep clone by JSON.parse/stringify that assumes the config is serializable. const normalizedConfig = JSON.parse(JSON.stringify(config)); const { sourceSkips } = options; delete normalizedConfig._internal; if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigVersions) { delete normalizedConfig.version; delete normalizedConfig.android?.versionCode; delete normalizedConfig.ios?.buildNumber; } if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigRuntimeVersionIfString) { if (typeof normalizedConfig.runtimeVersion === 'string') { delete normalizedConfig.runtimeVersion; } if (typeof normalizedConfig.android?.runtimeVersion === 'string') { delete normalizedConfig.android.runtimeVersion; } if (typeof normalizedConfig.ios?.runtimeVersion === 'string') { delete normalizedConfig.ios.runtimeVersion; } if (typeof normalizedConfig.web?.runtimeVersion === 'string') { delete normalizedConfig.web.runtimeVersion; } } if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigNames) { normalizedConfig.name = ''; delete normalizedConfig.description; delete normalizedConfig.web?.name; delete normalizedConfig.web?.shortName; delete normalizedConfig.web?.description; } if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigAndroidPackage) { delete normalizedConfig.android?.package; } if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigIosBundleIdentifier) { delete normalizedConfig.ios?.bundleIdentifier; } if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigSchemes) { delete normalizedConfig.scheme; normalizedConfig.slug = ''; } if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigEASProject) { delete normalizedConfig.owner; delete normalizedConfig?.extra?.eas; delete normalizedConfig?.updates?.url; } if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigAssets) { delete normalizedConfig.icon; delete normalizedConfig.splash; delete normalizedConfig.android?.adaptiveIcon; delete normalizedConfig.android?.icon; delete normalizedConfig.android?.splash; delete normalizedConfig.ios?.icon; delete normalizedConfig.ios?.splash; delete normalizedConfig.web?.favicon; delete normalizedConfig.web?.splash; } if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigExtraSection) { delete normalizedConfig.extra; } return (0, Utils_1.relativizeJsonPaths)(normalizedConfig, projectRoot); } /** * Gives the last chance to modify the ExpoConfig. * For example, we can remove some fields that are already included in the fingerprint. */ function postUpdateExpoConfig(config, projectRoot) { // The config is already a clone, so we can modify it in place for performance. // googleServicesFile may contain absolute paths on EAS with file-based secrets. // Given we include googleServicesFile as external files already, we can remove it from the config. delete config.android?.googleServicesFile; delete config.ios?.googleServicesFile; return config; } /** * Collect iOS icon to flattened file paths. */ function collectIosIcons(icon) { if (icon == null) { return []; } if (typeof icon === 'string') { return [icon]; } return [icon.light, icon.dark, icon.tinted].filter((file) => Boolean(file)); } /** * The filePath in config could be relative (`./assets/icon.png`, `assets/icon.png`) or even absolute. * We need to normalize the path and return as relative path without `./` prefix. */ function ensureRelativePath(projectRoot, filePath) { const absolutePath = path_1.default.resolve(projectRoot, filePath); return path_1.default.relative(projectRoot, absolutePath); } async function createHashSourceExternalFileAsync({ projectRoot, file, reason, }) { const hashSource = await (0, Utils_1.getFileBasedHashSourceAsync)(projectRoot, file, reason); if (hashSource) { debug(`Adding config external file - ${chalk_1.default.dim(file)}`); if (hashSource.type === 'file' || hashSource.type === 'dir') { // We include the expo config contents in the fingerprint, // the `filePath` hashing for the external files is not necessary. // Especially people using EAS environment variables for the google service files, // the `filePath` will be different between local and remote builds. // We use a fixed override hash key and basically ignore the `filePath` hashing. hashSource.overrideHashKey = 'expoConfigExternalFile:contentsOnly'; } } return hashSource; } async function getEasBuildSourcesAsync(projectRoot, options) { const files = ['eas.json', '.easignore']; const results = (await Promise.all(files.map(async (file) => { const result = await (0, Utils_1.getFileBasedHashSourceAsync)(projectRoot, file, 'easBuild'); if (result != null) { debug(`Adding eas file - ${chalk_1.default.dim(file)}`); } return result; }))).filter(Boolean); return results; } async function getExpoAutolinkingAndroidSourcesAsync(projectRoot, options, expoAutolinkingVersion) { if (!options.platforms.includes('android')) { return []; } try { const reasons = ['expoAutolinkingAndroid']; const results = []; const { stdout } = await (0, spawn_async_1.default)('node', [(0, ExpoResolver_1.resolveExpoAutolinkingCliPath)(projectRoot), 'resolve', '-p', 'android', '--json'], { cwd: projectRoot }); const config = sortExpoAutolinkingAndroidConfig(JSON.parse(stdout)); for (const module of config.modules) { for (const project of module.projects) { const filePath = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, project.sourceDir)); project.sourceDir = filePath; // use relative path for the dir debug(`Adding expo-modules-autolinking android dir - ${chalk_1.default.dim(filePath)}`); results.push({ type: 'dir', filePath, reasons }); // `aarProjects` is present in project starting from SDK 53+. if (project.aarProjects) { for (const aarProject of project.aarProjects) { // use relative path for aarProject fields aarProject.aarFilePath = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, aarProject.aarFilePath)); aarProject.projectDir = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, aarProject.projectDir)); } } if (typeof project.shouldUsePublicationScriptPath === 'string') { project.shouldUsePublicationScriptPath = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, project.shouldUsePublicationScriptPath)); } } if (module.plugins) { for (const plugin of module.plugins) { const filePath = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, plugin.sourceDir)); plugin.sourceDir = filePath; // use relative path for the dir debug(`Adding expo-modules-autolinking android dir - ${chalk_1.default.dim(filePath)}`); results.push({ type: 'dir', filePath, reasons }); } } // Backward compatibility for SDK versions earlier than 53 if (module.aarProjects) { for (const aarProject of module.aarProjects) { // use relative path for aarProject fields aarProject.aarFilePath = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, aarProject.aarFilePath)); aarProject.projectDir = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, aarProject.projectDir)); } } } results.push({ type: 'contents', id: 'expoAutolinkingConfig:android', contents: JSON.stringify(config), reasons, }); return results; } catch { return []; } } /** * Gets the patch sources for the `patch-project`. */ async function getExpoCNGPatchSourcesAsync(projectRoot, options) { const result = await (0, Utils_1.getFileBasedHashSourceAsync)(projectRoot, 'cng-patches', 'expoCNGPatches'); if (result != null) { debug(`Adding dir - ${chalk_1.default.dim('cng-patches')}`); return [result]; } return []; } async function getExpoAutolinkingIosSourcesAsync(projectRoot, options, expoAutolinkingVersion) { if (!options.platforms.includes('ios')) { return []; } // expo-modules-autolinking 1.10.0 added support for apple platform const platform = semver_1.default.lt(expoAutolinkingVersion, '1.10.0') ? 'ios' : 'apple'; try { const reasons = ['expoAutolinkingIos']; const results = []; const { stdout } = await (0, spawn_async_1.default)('node', [(0, ExpoResolver_1.resolveExpoAutolinkingCliPath)(projectRoot), 'resolve', '-p', platform, '--json'], { cwd: projectRoot }); const config = JSON.parse(stdout); for (const module of config.modules) { for (const pod of module.pods) { const filePath = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, pod.podspecDir)); pod.podspecDir = filePath; // use relative path for the dir debug(`Adding expo-modules-autolinking ios dir - ${chalk_1.default.dim(filePath)}`); results.push({ type: 'dir', filePath, reasons }); } } results.push({ type: 'contents', id: 'expoAutolinkingConfig:ios', contents: JSON.stringify(config), reasons, }); return results; } catch { return []; } } /** * Sort the expo-modules-autolinking android config to make it stable from hashing. */ function sortExpoAutolinkingAndroidConfig(config) { for (const module of config.modules) { // Sort the projects by project.name module.projects.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0); } return config; } /** * Get the props for a config-plugin */ function getConfigPluginProps(config, pluginName) { const plugin = (config.plugins ?? []).find((plugin) => { if (Array.isArray(plugin)) { return plugin[0] === pluginName; } return plugin === pluginName; }); if (Array.isArray(plugin)) { return (plugin[1] ?? null); } return null; } //# sourceMappingURL=Expo.js.map