1082815dcSEvan Baconimport { ExpoConfig } from '@expo/config-types';
2082815dcSEvan Baconimport { XcodeProject } from 'xcode';
3082815dcSEvan Bacon
4082815dcSEvan Baconimport { InfoPlist } from './IosConfig.types';
5082815dcSEvan Baconimport { findFirstNativeTarget } from './Target';
6082815dcSEvan Baconimport {
7082815dcSEvan Bacon  ConfigurationSectionEntry,
8082815dcSEvan Bacon  getBuildConfigurationsForListId,
9082815dcSEvan Bacon  sanitizedName,
10082815dcSEvan Bacon} from './utils/Xcodeproj';
11*8a424bebSJames Ideimport { ConfigPlugin } from '../Plugin.types';
12*8a424bebSJames Ideimport { createInfoPlistPluginWithPropertyGuard, withXcodeProject } from '../plugins/ios-plugins';
13082815dcSEvan Bacon
14082815dcSEvan Baconexport const withDisplayName = createInfoPlistPluginWithPropertyGuard(
15082815dcSEvan Bacon  setDisplayName,
16082815dcSEvan Bacon  {
17082815dcSEvan Bacon    infoPlistProperty: 'CFBundleDisplayName',
18082815dcSEvan Bacon    expoConfigProperty: 'name',
19082815dcSEvan Bacon  },
20082815dcSEvan Bacon  'withDisplayName'
21082815dcSEvan Bacon);
22082815dcSEvan Bacon
23082815dcSEvan Baconexport const withName = createInfoPlistPluginWithPropertyGuard(
24082815dcSEvan Bacon  setName,
25082815dcSEvan Bacon  {
26082815dcSEvan Bacon    infoPlistProperty: 'CFBundleName',
27082815dcSEvan Bacon    expoConfigProperty: 'name',
28082815dcSEvan Bacon  },
29082815dcSEvan Bacon  'withName'
30082815dcSEvan Bacon);
31082815dcSEvan Bacon
32082815dcSEvan Bacon/** Set the PRODUCT_NAME variable in the xcproj file based on the app.json name property. */
33082815dcSEvan Baconexport const withProductName: ConfigPlugin = (config) => {
34082815dcSEvan Bacon  return withXcodeProject(config, (config) => {
35082815dcSEvan Bacon    config.modResults = setProductName(config, config.modResults);
36082815dcSEvan Bacon    return config;
37082815dcSEvan Bacon  });
38082815dcSEvan Bacon};
39082815dcSEvan Bacon
40082815dcSEvan Baconexport function getName(config: Pick<ExpoConfig, 'name'>) {
41082815dcSEvan Bacon  return typeof config.name === 'string' ? config.name : null;
42082815dcSEvan Bacon}
43082815dcSEvan Bacon
44082815dcSEvan Bacon/**
45082815dcSEvan Bacon * CFBundleDisplayName is used for most things: the name on the home screen, in
46082815dcSEvan Bacon * notifications, and others.
47082815dcSEvan Bacon */
48082815dcSEvan Baconexport function setDisplayName(
49082815dcSEvan Bacon  configOrName: Pick<ExpoConfig, 'name'> | string,
50082815dcSEvan Bacon  { CFBundleDisplayName, ...infoPlist }: InfoPlist
51082815dcSEvan Bacon): InfoPlist {
52082815dcSEvan Bacon  let name: string | null = null;
53082815dcSEvan Bacon  if (typeof configOrName === 'string') {
54082815dcSEvan Bacon    name = configOrName;
55082815dcSEvan Bacon  } else {
56082815dcSEvan Bacon    name = getName(configOrName);
57082815dcSEvan Bacon  }
58082815dcSEvan Bacon
59082815dcSEvan Bacon  if (!name) {
60082815dcSEvan Bacon    return infoPlist;
61082815dcSEvan Bacon  }
62082815dcSEvan Bacon
63082815dcSEvan Bacon  return {
64082815dcSEvan Bacon    ...infoPlist,
65082815dcSEvan Bacon    CFBundleDisplayName: name,
66082815dcSEvan Bacon  };
67082815dcSEvan Bacon}
68082815dcSEvan Bacon
69082815dcSEvan Bacon/**
70082815dcSEvan Bacon * CFBundleName is recommended to be 16 chars or less and is used in lists, eg:
71082815dcSEvan Bacon * sometimes on the App Store
72082815dcSEvan Bacon */
73082815dcSEvan Baconexport function setName(
74082815dcSEvan Bacon  config: Pick<ExpoConfig, 'name'>,
75082815dcSEvan Bacon  { CFBundleName, ...infoPlist }: InfoPlist
76082815dcSEvan Bacon): InfoPlist {
77082815dcSEvan Bacon  const name = getName(config);
78082815dcSEvan Bacon
79082815dcSEvan Bacon  if (!name) {
80082815dcSEvan Bacon    return infoPlist;
81082815dcSEvan Bacon  }
82082815dcSEvan Bacon
83082815dcSEvan Bacon  return {
84082815dcSEvan Bacon    ...infoPlist,
85082815dcSEvan Bacon    CFBundleName: name,
86082815dcSEvan Bacon  };
87082815dcSEvan Bacon}
88082815dcSEvan Bacon
89082815dcSEvan Baconexport function setProductName(
90082815dcSEvan Bacon  config: Pick<ExpoConfig, 'name'>,
91082815dcSEvan Bacon  project: XcodeProject
92082815dcSEvan Bacon): XcodeProject {
93082815dcSEvan Bacon  const name = sanitizedName(getName(config) ?? '');
94082815dcSEvan Bacon
95082815dcSEvan Bacon  if (!name) {
96082815dcSEvan Bacon    return project;
97082815dcSEvan Bacon  }
98082815dcSEvan Bacon  const quotedName = ensureQuotes(name);
99082815dcSEvan Bacon
100082815dcSEvan Bacon  const [, nativeTarget] = findFirstNativeTarget(project);
101082815dcSEvan Bacon
102082815dcSEvan Bacon  getBuildConfigurationsForListId(project, nativeTarget.buildConfigurationList).forEach(
103082815dcSEvan Bacon    ([, item]: ConfigurationSectionEntry) => {
104082815dcSEvan Bacon      item.buildSettings.PRODUCT_NAME = quotedName;
105082815dcSEvan Bacon    }
106082815dcSEvan Bacon  );
107082815dcSEvan Bacon
108082815dcSEvan Bacon  return project;
109082815dcSEvan Bacon}
110082815dcSEvan Bacon
111082815dcSEvan Baconconst ensureQuotes = (value: string) => {
112082815dcSEvan Bacon  if (!value.match(/^['"]/)) {
113082815dcSEvan Bacon    return `"${value}"`;
114082815dcSEvan Bacon  }
115082815dcSEvan Bacon  return value;
116082815dcSEvan Bacon};
117