1import type { ExpoConfig } from '@expo/config-types';
2
3import type { ConfigPlugin } from '../Plugin.types';
4import { withGradleProperties } from '../plugins/android-plugins';
5import { BuildPropertiesConfig, ConfigToPropertyRuleType } from '../utils/BuildProperties.types';
6import type { PropertiesItem } from './Properties';
7
8/**
9 * Creates a `withGradleProperties` config-plugin based on given config to property mapping rules.
10 *
11 * The factory supports two modes from generic type inference
12 * ```ts
13 * // config-plugin without `props`, it will implicitly use the expo config as source config.
14 * createBuildGradlePropsConfigPlugin<ExpoConfig>(): ConfigPlugin<void>;
15 *
16 * // config-plugin with a parameter `props: CustomType`, it will use the `props` as source config.
17 * createBuildGradlePropsConfigPlugin<CustomType>(): ConfigPlugin<CustomType>;
18 * ```
19 *
20 * @param configToPropertyRules config to property mapping rules
21 * @param name the config plugin name
22 */
23export function createBuildGradlePropsConfigPlugin<SourceConfigType extends BuildPropertiesConfig>(
24  configToPropertyRules: ConfigToPropertyRuleType<SourceConfigType>[],
25  name?: string
26) {
27  const withUnknown: ConfigPlugin<SourceConfigType extends ExpoConfig ? void : SourceConfigType> = (
28    config,
29    sourceConfig
30  ) =>
31    withGradleProperties(config, (config) => {
32      config.modResults = updateAndroidBuildPropertiesFromConfig(
33        (sourceConfig ?? config) as SourceConfigType,
34        config.modResults,
35        configToPropertyRules
36      );
37      return config;
38    });
39  if (name) {
40    Object.defineProperty(withUnknown, 'name', {
41      value: name,
42    });
43  }
44  return withUnknown;
45}
46
47/**
48 * A config-plugin to update `android/gradle.properties` from the `jsEngine` in expo config
49 */
50export const withJsEngineGradleProps = createBuildGradlePropsConfigPlugin<ExpoConfig>(
51  [
52    {
53      propName: 'expo.jsEngine',
54      propValueGetter: (config) => config.android?.jsEngine ?? config.jsEngine ?? 'jsc',
55    },
56  ],
57  'withJsEngineGradleProps'
58);
59
60export function updateAndroidBuildPropertiesFromConfig<
61  SourceConfigType extends BuildPropertiesConfig
62>(
63  config: SourceConfigType,
64  gradleProperties: PropertiesItem[],
65  configToPropertyRules: ConfigToPropertyRuleType<SourceConfigType>[]
66) {
67  for (const configToProperty of configToPropertyRules) {
68    const value = configToProperty.propValueGetter(config);
69    updateAndroidBuildProperty(gradleProperties, configToProperty.propName, value);
70  }
71
72  return gradleProperties;
73}
74
75export function updateAndroidBuildProperty(
76  gradleProperties: PropertiesItem[],
77  name: string,
78  value: string | null | undefined,
79  options?: { removePropWhenValueIsNull?: boolean }
80) {
81  const oldPropIndex = gradleProperties.findIndex(
82    (prop) => prop.type === 'property' && prop.key === name
83  );
84
85  if (value) {
86    // found the matched value, add or merge new property
87    const newProp: PropertiesItem = {
88      type: 'property',
89      key: name,
90      value,
91    };
92
93    if (oldPropIndex >= 0) {
94      gradleProperties[oldPropIndex] = newProp;
95    } else {
96      gradleProperties.push(newProp);
97    }
98  } else if (options?.removePropWhenValueIsNull && oldPropIndex >= 0) {
99    gradleProperties.splice(oldPropIndex, 1);
100  }
101
102  return gradleProperties;
103}
104