1import { ExpoConfig } from '@expo/config-types'; 2import path from 'path'; 3 4import { ConfigPlugin } from '../Plugin.types'; 5import { withAppBuildGradle, withProjectBuildGradle } from '../plugins/android-plugins'; 6import { withDangerousMod } from '../plugins/withDangerousMod'; 7import { copyFilePathToPathAsync } from '../utils/fs'; 8import { addWarningAndroid } from '../utils/warnings'; 9 10const DEFAULT_TARGET_PATH = './android/app/google-services.json'; 11 12const googleServicesClassPath = 'com.google.gms:google-services'; 13const googleServicesPlugin = 'com.google.gms.google-services'; 14 15// NOTE(brentvatne): This may be annoying to keep up to date... 16const googleServicesVersion = '4.3.3'; 17 18export const withClassPath: ConfigPlugin = (config) => { 19 return withProjectBuildGradle(config, (config) => { 20 if (config.modResults.language === 'groovy') { 21 config.modResults.contents = setClassPath(config, config.modResults.contents); 22 } else { 23 addWarningAndroid( 24 'android.googleServicesFile', 25 `Cannot automatically configure project build.gradle if it's not groovy` 26 ); 27 } 28 return config; 29 }); 30}; 31 32export const withApplyPlugin: ConfigPlugin = (config) => { 33 return withAppBuildGradle(config, (config) => { 34 if (config.modResults.language === 'groovy') { 35 config.modResults.contents = applyPlugin(config, config.modResults.contents); 36 } else { 37 addWarningAndroid( 38 'android.googleServicesFile', 39 `Cannot automatically configure app build.gradle if it's not groovy` 40 ); 41 } 42 return config; 43 }); 44}; 45 46/** 47 * Add `google-services.json` to project 48 */ 49export const withGoogleServicesFile: ConfigPlugin = (config) => { 50 return withDangerousMod(config, [ 51 'android', 52 async (config) => { 53 await setGoogleServicesFile(config, config.modRequest.projectRoot); 54 return config; 55 }, 56 ]); 57}; 58 59export function getGoogleServicesFilePath(config: Pick<ExpoConfig, 'android'>) { 60 return config.android?.googleServicesFile ?? null; 61} 62 63export async function setGoogleServicesFile( 64 config: Pick<ExpoConfig, 'android'>, 65 projectRoot: string, 66 targetPath: string = DEFAULT_TARGET_PATH 67) { 68 const partialSourcePath = getGoogleServicesFilePath(config); 69 if (!partialSourcePath) { 70 return false; 71 } 72 73 const completeSourcePath = path.resolve(projectRoot, partialSourcePath); 74 const destinationPath = path.resolve(projectRoot, targetPath); 75 76 try { 77 await copyFilePathToPathAsync(completeSourcePath, destinationPath); 78 } catch (e) { 79 console.log(e); 80 throw new Error( 81 `Cannot copy google-services.json from ${completeSourcePath} to ${destinationPath}. Please make sure the source and destination paths exist.` 82 ); 83 } 84 return true; 85} 86 87/** 88 * Adding the Google Services plugin 89 * NOTE(brentvatne): string replacement is a fragile approach! we need a 90 * better solution than this. 91 */ 92export function setClassPath(config: Pick<ExpoConfig, 'android'>, buildGradle: string) { 93 const googleServicesFile = getGoogleServicesFilePath(config); 94 if (!googleServicesFile) { 95 return buildGradle; 96 } 97 98 if (buildGradle.includes(googleServicesClassPath)) { 99 return buildGradle; 100 } 101 102 // 103 return buildGradle.replace( 104 /dependencies\s?{/, 105 `dependencies { 106 classpath '${googleServicesClassPath}:${googleServicesVersion}'` 107 ); 108} 109 110export function applyPlugin(config: Pick<ExpoConfig, 'android'>, appBuildGradle: string) { 111 const googleServicesFile = getGoogleServicesFilePath(config); 112 if (!googleServicesFile) { 113 return appBuildGradle; 114 } 115 116 // Make sure the project does not have the plugin already 117 const pattern = new RegExp(`apply\\s+plugin:\\s+['"]${googleServicesPlugin}['"]`); 118 if (appBuildGradle.match(pattern)) { 119 return appBuildGradle; 120 } 121 122 // Add it to the end of the file 123 return appBuildGradle + `\napply plugin: '${googleServicesPlugin}'`; 124} 125