UNPKG

expo-build-properties

Version:

Config plugin to customize native build properties on prebuild

250 lines (249 loc) 11.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.withAndroidDayNightTheme = exports.withAndroidQueries = exports.withAndroidCleartextTraffic = exports.withAndroidPurgeProguardRulesOnce = exports.withAndroidProguardRules = exports.withAndroidBuildProperties = void 0; exports.updateAndroidProguardRules = updateAndroidProguardRules; const config_plugins_1 = require("expo/config-plugins"); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const androidQueryUtils_1 = require("./androidQueryUtils"); const fileContentsUtils_1 = require("./fileContentsUtils"); const { createBuildGradlePropsConfigPlugin } = config_plugins_1.AndroidConfig.BuildProperties; exports.withAndroidBuildProperties = createBuildGradlePropsConfigPlugin([ { propName: 'newArchEnabled', propValueGetter: (config) => { if (config.android?.newArchEnabled !== undefined) { config_plugins_1.WarningAggregator.addWarningAndroid('withAndroidBuildProperties', 'android.newArchEnabled is deprecated, use app config `newArchEnabled` instead.', 'https://docs.expo.dev/versions/latest/config/app/#newarchenabled'); } return config.android?.newArchEnabled?.toString(); }, }, { propName: 'android.minSdkVersion', propValueGetter: (config) => config.android?.minSdkVersion?.toString(), }, { propName: 'android.compileSdkVersion', propValueGetter: (config) => config.android?.compileSdkVersion?.toString(), }, { propName: 'android.targetSdkVersion', propValueGetter: (config) => config.android?.targetSdkVersion?.toString(), }, { propName: 'android.buildToolsVersion', propValueGetter: (config) => config.android?.buildToolsVersion, }, { propName: 'android.kotlinVersion', propValueGetter: (config) => config.android?.kotlinVersion, }, { propName: 'android.packagingOptions.pickFirsts', propValueGetter: (config) => config.android?.packagingOptions?.pickFirst?.join(','), }, { propName: 'android.packagingOptions.excludes', propValueGetter: (config) => config.android?.packagingOptions?.exclude?.join(','), }, { propName: 'android.packagingOptions.merges', propValueGetter: (config) => config.android?.packagingOptions?.merge?.join(','), }, { propName: 'android.packagingOptions.doNotStrip', propValueGetter: (config) => config.android?.packagingOptions?.doNotStrip?.join(','), }, { propName: 'android.enableProguardInReleaseBuilds', propValueGetter: (config) => config.android?.enableProguardInReleaseBuilds?.toString(), }, { propName: 'android.enableShrinkResourcesInReleaseBuilds', propValueGetter: (config) => config.android?.enableShrinkResourcesInReleaseBuilds?.toString(), }, { propName: 'android.enablePngCrunchInReleaseBuilds', propValueGetter: (config) => config.android?.enablePngCrunchInReleaseBuilds?.toString(), }, { propName: 'EX_DEV_CLIENT_NETWORK_INSPECTOR', propValueGetter: (config) => (config.android?.networkInspector ?? true).toString(), }, { propName: 'expo.useLegacyPackaging', propValueGetter: (config) => config.android?.useLegacyPackaging?.toString(), }, { propName: 'android.extraMavenRepos', propValueGetter: (config) => { const extraMavenRepos = (config.android?.extraMavenRepos ?? []).map((item) => { if (typeof item === 'string') { return { url: item }; } return item; }); return extraMavenRepos.length > 0 ? JSON.stringify(extraMavenRepos) : undefined; }, }, { propName: 'android.useDayNightTheme', propValueGetter: (config) => config.android?.useDayNightTheme?.toString(), }, { propName: 'android.enableBundleCompression', propValueGetter: (config) => config.android?.enableBundleCompression?.toString(), }, ], 'withAndroidBuildProperties'); /** * Appends `props.android.extraProguardRules` content into `android/app/proguard-rules.pro` */ const withAndroidProguardRules = (config, props) => { return (0, config_plugins_1.withDangerousMod)(config, [ 'android', async (config) => { const extraProguardRules = props.android?.extraProguardRules ?? null; const proguardRulesFile = path_1.default.join(config.modRequest.platformProjectRoot, 'app', 'proguard-rules.pro'); const contents = await fs_1.default.promises.readFile(proguardRulesFile, 'utf8'); const newContents = updateAndroidProguardRules(contents, extraProguardRules, 'append'); if (contents !== newContents) { await fs_1.default.promises.writeFile(proguardRulesFile, newContents); } return config; }, ]); }; exports.withAndroidProguardRules = withAndroidProguardRules; /** * Purge generated proguard contents from previous prebuild. * This plugin only runs once in the prebuilding phase and should execute before any `withAndroidProguardRules` calls. */ const withAndroidPurgeProguardRulesOnce = (config) => { return (0, config_plugins_1.withDangerousMod)(config, [ 'android', async (config) => { const RUN_ONCE_NAME = 'expo-build-properties-android-purge-proguard-rules-once'; /** * The `withRunOnce` plugin will delay this plugin's execution. * To make sure this plugin executes before any `withAndroidProguardRules`. * We use the `withRunOnce` internal History functions to do the check. * Example calls to demonstrate the case: * ```ts * config = withBuildProperties(config as ExpoConfig, { * android: { * kotlinVersion: "1.6.10", * }, * }); * config = withBuildProperties(config as ExpoConfig, { * android: { * enableProguardInReleaseBuilds: true, * extraProguardRules: "-keep class com.mycompany.** { *; }", * }, * }); * ``` */ if (config_plugins_1.History.getHistoryItem(config, RUN_ONCE_NAME)) { return config; } else { config_plugins_1.History.addHistoryItem(config, { name: RUN_ONCE_NAME }); } const proguardRulesFile = path_1.default.join(config.modRequest.platformProjectRoot, 'app', 'proguard-rules.pro'); const contents = await fs_1.default.promises.readFile(proguardRulesFile, 'utf8'); const newContents = updateAndroidProguardRules(contents, '', 'overwrite'); if (contents !== newContents) { await fs_1.default.promises.writeFile(proguardRulesFile, newContents); } return config; }, ]); }; exports.withAndroidPurgeProguardRulesOnce = withAndroidPurgeProguardRulesOnce; /** * Update `newProguardRules` to original `proguard-rules.pro` contents if needed * * @param contents the original `proguard-rules.pro` contents * @param newProguardRules new proguard rules to add. If the value is null, the returned value will be original `contents`. * @returns return updated contents */ function updateAndroidProguardRules(contents, newProguardRules, updateMode) { if (newProguardRules == null) { return contents; } const options = { tag: 'expo-build-properties', commentPrefix: '#' }; let newContents = contents; if (updateMode === 'overwrite') { newContents = (0, fileContentsUtils_1.purgeContents)(contents, options); } if (newProguardRules !== '') { newContents = (0, fileContentsUtils_1.appendContents)(newContents, newProguardRules, options); } return newContents; } const withAndroidCleartextTraffic = (config, props) => { return (0, config_plugins_1.withAndroidManifest)(config, (config) => { if (props.android?.usesCleartextTraffic == null) { return config; } config.modResults = setUsesCleartextTraffic(config.modResults, props.android?.usesCleartextTraffic); return config; }); }; exports.withAndroidCleartextTraffic = withAndroidCleartextTraffic; function setUsesCleartextTraffic(androidManifest, value) { const mainApplication = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(androidManifest); if (mainApplication?.$) { mainApplication.$['android:usesCleartextTraffic'] = String(value); } return androidManifest; } const withAndroidQueries = (config, props) => { return (0, config_plugins_1.withAndroidManifest)(config, (config) => { if (props.android?.manifestQueries == null) { return config; } const { manifestQueries } = props.android; // Default template adds a single intent to the `queries` tag const defaultIntents = config.modResults.manifest.queries.map((q) => q.intent ?? []).flat() ?? []; const defaultPackages = config.modResults.manifest.queries.map((q) => q.package ?? []).flat() ?? []; const defaultProviders = config.modResults.manifest.queries.map((q) => q.provider ?? []).flat() ?? []; const newQueries = { package: [...defaultPackages, ...(0, androidQueryUtils_1.renderQueryPackages)(manifestQueries.package)], intent: [...defaultIntents, ...(0, androidQueryUtils_1.renderQueryIntents)(manifestQueries.intent)], provider: [...defaultProviders, ...(0, androidQueryUtils_1.renderQueryProviders)(manifestQueries.provider)], }; config.modResults.manifest.queries = [newQueries]; return config; }); }; exports.withAndroidQueries = withAndroidQueries; const withAndroidDayNightTheme = (config, props) => { return (0, config_plugins_1.withAndroidStyles)(config, (config) => { if (!props.android?.useDayNightTheme) { return config; } const { style = [] } = config.modResults.resources; if (!style.length) { return config; } // Replace `AppTheme` and remove `ResetEditText` const excludedStyles = ['AppTheme', 'ResetEditText']; // Remove the hardcoded colors. const excludedAttributes = ['android:textColor', 'android:editTextStyle']; config.modResults.resources.style = [ { $: { name: 'AppTheme', parent: 'Theme.AppCompat.DayNight.NoActionBar', }, item: [...style[0].item.filter(({ $ }) => !excludedAttributes.includes($.name))], }, ...style.filter(({ $ }) => !excludedStyles.includes($.name)), ]; return config; }); }; exports.withAndroidDayNightTheme = withAndroidDayNightTheme;