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