@bacons/apple-targets 3.0.5 → 3.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +9 -5
  2. package/build/colorset/{withIosColorset.js → with-ios-colorset.js} +2 -2
  3. package/build/config-plugin.js +9 -10
  4. package/build/config.d.ts +0 -2
  5. package/build/{withXcodeChanges.d.ts → configuration-list.d.ts} +2 -2
  6. package/build/configuration-list.js +656 -0
  7. package/build/target.d.ts +473 -2
  8. package/build/target.js +281 -239
  9. package/build/util.d.ts +18 -0
  10. package/build/util.js +48 -0
  11. package/build/{withXcparse.js → with-bacons-xcode.js} +5 -2
  12. package/build/{withEasCredentials.js → with-eas-credentials.js} +2 -2
  13. package/build/{withWidget.js → with-widget.js} +24 -65
  14. package/build/with-xcode-changes.d.ts +3 -0
  15. package/build/with-xcode-changes.js +401 -0
  16. package/package.json +7 -2
  17. package/build/template/XCBuildConfiguration.json +0 -759
  18. package/build/withXcodeChanges.js +0 -1039
  19. /package/build/colorset/{customColorFromCSS.d.ts → custom-color-from-css.d.ts} +0 -0
  20. /package/build/colorset/{customColorFromCSS.js → custom-color-from-css.js} +0 -0
  21. /package/build/colorset/{withIosColorset.d.ts → with-ios-colorset.d.ts} +0 -0
  22. /package/build/icon/{withImageAsset.d.ts → with-image-asset.d.ts} +0 -0
  23. /package/build/icon/{withImageAsset.js → with-image-asset.js} +0 -0
  24. /package/build/icon/{withIosIcon.d.ts → with-ios-icon.d.ts} +0 -0
  25. /package/build/icon/{withIosIcon.js → with-ios-icon.js} +0 -0
  26. /package/build/{withXcparse.d.ts → with-bacons-xcode.d.ts} +0 -0
  27. /package/build/{withEasCredentials.d.ts → with-eas-credentials.d.ts} +0 -0
  28. /package/build/{withPodTargetExtension.d.ts → with-pod-target-extension.d.ts} +0 -0
  29. /package/build/{withPodTargetExtension.js → with-pod-target-extension.js} +0 -0
  30. /package/build/{withWidget.d.ts → with-widget.d.ts} +0 -0
@@ -22,12 +22,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
25
28
  Object.defineProperty(exports, "__esModule", { value: true });
26
29
  exports.withXcodeProjectBetaBaseMod = exports.withXcodeProjectBeta = void 0;
27
30
  const xcode_1 = require("@bacons/xcode");
28
31
  const xcodeParse = __importStar(require("@bacons/xcode/json"));
29
32
  const config_plugins_1 = require("@expo/config-plugins");
30
- const fs = __importStar(require("fs"));
33
+ const fs_1 = __importDefault(require("fs"));
31
34
  const customModName = "xcodeProjectBeta2";
32
35
  const withXcodeProjectBeta = (config, action) => {
33
36
  return (0, config_plugins_1.withMod)(config, {
@@ -67,7 +70,7 @@ const withXcodeProjectBetaBaseModInternal = (config) => {
67
70
  }
68
71
  const contents = xcodeParse.build(modResults.toJSON());
69
72
  if (contents.trim().length) {
70
- await fs.promises.writeFile(filePath, contents);
73
+ await fs_1.default.promises.writeFile(filePath, contents);
71
74
  }
72
75
  },
73
76
  }),
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getEASCredentialsForXcodeProject = exports.withAutoEasExtensionCredentials = exports.withEASTargets = void 0;
4
4
  const target_1 = require("./target");
5
- const withXcparse_1 = require("./withXcparse");
5
+ const with_bacons_xcode_1 = require("./with-bacons-xcode");
6
6
  const debug = require("debug")("expo:target:eas");
7
7
  function safeSet(obj, key, value) {
8
8
  const segments = key.split(".");
@@ -52,7 +52,7 @@ const withEASTargets = (config, { bundleIdentifier, targetName, entitlements })
52
52
  };
53
53
  exports.withEASTargets = withEASTargets;
54
54
  const withAutoEasExtensionCredentials = (config) => {
55
- return (0, withXcparse_1.withXcodeProjectBeta)(config, async (config) => {
55
+ return (0, with_bacons_xcode_1.withXcodeProjectBeta)(config, async (config) => {
56
56
  safeSet(config, "extra.eas.build.experimental.ios.appExtensions", []);
57
57
  const creds = getEASCredentialsForXcodeProject(config.modResults);
58
58
  // Warn about duplicates
@@ -9,45 +9,17 @@ const fs_1 = __importDefault(require("fs"));
9
9
  const glob_1 = require("glob");
10
10
  const path_1 = __importDefault(require("path"));
11
11
  const chalk_1 = __importDefault(require("chalk"));
12
- const withIosColorset_1 = require("./colorset/withIosColorset");
13
- const withImageAsset_1 = require("./icon/withImageAsset");
14
- const withIosIcon_1 = require("./icon/withIosIcon");
12
+ const with_ios_colorset_1 = require("./colorset/with-ios-colorset");
13
+ const with_image_asset_1 = require("./icon/with-image-asset");
14
+ const with_ios_icon_1 = require("./icon/with-ios-icon");
15
15
  const target_1 = require("./target");
16
- const withEasCredentials_1 = require("./withEasCredentials");
17
- const withXcodeChanges_1 = require("./withXcodeChanges");
16
+ const with_eas_credentials_1 = require("./with-eas-credentials");
17
+ const with_xcode_changes_1 = require("./with-xcode-changes");
18
+ const util_1 = require("./util");
18
19
  const DEFAULT_DEPLOYMENT_TARGET = "18.0";
19
- function memoize(fn) {
20
- const cache = new Map();
21
- return ((...args) => {
22
- const key = JSON.stringify(args);
23
- if (cache.has(key)) {
24
- return cache.get(key);
25
- }
26
- const result = fn(...args);
27
- cache.set(key, result);
28
- return result;
29
- });
30
- }
31
- const warnOnce = memoize(console.warn);
32
- const logOnce = memoize(console.log);
33
- function createLogQueue() {
34
- const queue = [];
35
- const flush = () => {
36
- queue.forEach((fn) => fn());
37
- queue.length = 0;
38
- };
39
- return {
40
- flush,
41
- add: (fn) => {
42
- queue.push(fn);
43
- },
44
- };
45
- }
46
- // Queue up logs so they only run when prebuild is actually running and not during standard config reads.
47
- const prebuildLogQueue = createLogQueue();
48
20
  const withWidget = (config, props) => {
49
21
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
50
- prebuildLogQueue.add(() => warnOnce((0, chalk_1.default) `\nUsing experimental Config Plugin {bold @bacons/apple-targets} that is subject to breaking changes.`));
22
+ util_1.LOG_QUEUE.add(() => (0, util_1.warnOnce)((0, chalk_1.default) `\nUsing experimental Config Plugin {bold @bacons/apple-targets} that is subject to breaking changes.`));
51
23
  // TODO: Magically based on the top-level folders in the `ios-widgets/` folder
52
24
  if (props.icon && !/https?:\/\//.test(props.icon)) {
53
25
  props.icon = path_1.default.join(props.directory, props.icon);
@@ -55,9 +27,9 @@ const withWidget = (config, props) => {
55
27
  // This value should be used for the target name and other internal uses.
56
28
  const targetDirName = path_1.default.basename(path_1.default.dirname(props.configPath));
57
29
  // Sanitized for general usage. This name just needs to resemble the input value since it shouldn't be used for user-facing values such as the home screen or app store.
58
- const productName = sanitizeNameForNonDisplayUse(props.name || targetDirName) ||
59
- sanitizeNameForNonDisplayUse(targetDirName) ||
60
- sanitizeNameForNonDisplayUse(props.type);
30
+ const productName = (0, util_1.sanitizeNameForNonDisplayUse)(props.name || targetDirName) ||
31
+ (0, util_1.sanitizeNameForNonDisplayUse)(targetDirName) ||
32
+ (0, util_1.sanitizeNameForNonDisplayUse)(props.type);
61
33
  // This should never happen.
62
34
  if (!productName) {
63
35
  throw new Error(`[bacons/apple-targets][${props.type}] Target name does not contain any valid characters: ${targetDirName}`);
@@ -89,7 +61,7 @@ const withWidget = (config, props) => {
89
61
  if (!associatedDomains ||
90
62
  !Array.isArray(associatedDomains) ||
91
63
  associatedDomains.length === 0) {
92
- warnOnce((0, chalk_1.default) `{yellow [${targetDirName}]} Apple App Clip may require the associated domains entitlement but none were found in the Expo config.\nExample:\n${JSON.stringify({
64
+ (0, util_1.warnOnce)((0, chalk_1.default) `{yellow [${targetDirName}]} Apple App Clip may require the associated domains entitlement but none were found in the Expo config.\nExample:\n${JSON.stringify({
93
65
  ios: {
94
66
  associatedDomains: [`applinks:placeholder.expo.app`],
95
67
  },
@@ -112,7 +84,7 @@ const withWidget = (config, props) => {
112
84
  .filter(Boolean);
113
85
  const unique = [...new Set(sanitizedUrls)];
114
86
  if (unique.length) {
115
- warnOnce((0, chalk_1.default) `{gray [${targetDirName}]} Apple App Clip expo-target.config.js missing associated domains entitlements in the target config. Using the following defaults:\n${JSON.stringify({
87
+ (0, util_1.warnOnce)((0, chalk_1.default) `{gray [${targetDirName}]} Apple App Clip expo-target.config.js missing associated domains entitlements in the target config. Using the following defaults:\n${JSON.stringify({
116
88
  entitlements: {
117
89
  [associatedDomainsKey]: [
118
90
  `appclips:${unique[0] || "mywebsite.expo.app"}`,
@@ -138,14 +110,14 @@ const withWidget = (config, props) => {
138
110
  if (Array.isArray(mainAppGroups) && mainAppGroups.length > 0) {
139
111
  // Then set the target app groups to match the main app.
140
112
  entitlements[APP_GROUP_KEY] = mainAppGroups;
141
- prebuildLogQueue.add(() => {
142
- logOnce((0, chalk_1.default) `[${targetDirName}] Syncing app groups with main app. {dim Define entitlements[${JSON.stringify(APP_GROUP_KEY)}] in the {bold expo-target.config} file to override.}`);
113
+ util_1.LOG_QUEUE.add(() => {
114
+ (0, util_1.logOnce)((0, chalk_1.default) `[${targetDirName}] Syncing app groups with main app. {dim Define entitlements[${JSON.stringify(APP_GROUP_KEY)}] in the {bold expo-target.config} file to override.}`);
143
115
  });
144
116
  }
145
117
  else {
146
- prebuildLogQueue.add(() => {
118
+ util_1.LOG_QUEUE.add(() => {
147
119
  var _a, _b;
148
- return warnOnce((0, chalk_1.default) `{yellow [${targetDirName}]} Apple target may require the App Groups entitlement but none were found in the Expo config.\nExample:\n${JSON.stringify({
120
+ return (0, util_1.warnOnce)((0, chalk_1.default) `{yellow [${targetDirName}]} Apple target may require the App Groups entitlement but none were found in the Expo config.\nExample:\n${JSON.stringify({
149
121
  ios: {
150
122
  entitlements: {
151
123
  [APP_GROUP_KEY]: [
@@ -191,10 +163,10 @@ const withWidget = (config, props) => {
191
163
  (0, config_plugins_1.withDangerousMod)(config, [
192
164
  "ios",
193
165
  async (config) => {
194
- prebuildLogQueue.flush();
166
+ util_1.LOG_QUEUE.flush();
195
167
  fs_1.default.mkdirSync(targetDirAbsolutePath, { recursive: true });
196
168
  const files = [
197
- ["Info.plist", (0, target_1.getTargetInfoPlistForType)(props.type)],
169
+ ["Info.plist", plist_1.default.build((0, target_1.getTargetInfoPlistForType)(props.type))],
198
170
  ];
199
171
  // if (props.type === "widget") {
200
172
  // files.push(
@@ -236,14 +208,14 @@ const withWidget = (config, props) => {
236
208
  bundleId += ".";
237
209
  // Generate the bundle identifier. This logic needs to remain generally stable since it's used for a permanent value.
238
210
  // Key here is simplicity and predictability since it's already appended to the main app's bundle identifier.
239
- return mainAppBundleId + "." + getSanitizedBundleIdentifier(props.type);
211
+ return mainAppBundleId + "." + (0, util_1.getSanitizedBundleIdentifier)(props.type);
240
212
  })();
241
213
  const deviceFamilies = ((_d = config.ios) === null || _d === void 0 ? void 0 : _d.isTabletOnly)
242
214
  ? ["tablet"]
243
215
  : ((_e = config.ios) === null || _e === void 0 ? void 0 : _e.supportsTablet)
244
216
  ? ["phone", "tablet"]
245
217
  : ["phone"];
246
- (0, withXcodeChanges_1.withXcodeChanges)(config, {
218
+ (0, with_xcode_changes_1.withXcodeChanges)(config, {
247
219
  productName,
248
220
  configPath: props.configPath,
249
221
  name: targetDisplayName,
@@ -266,14 +238,14 @@ const withWidget = (config, props) => {
266
238
  // Assume App Clips are used for React Native.
267
239
  props.type === "clip",
268
240
  });
269
- config = (0, withEasCredentials_1.withEASTargets)(config, {
241
+ config = (0, with_eas_credentials_1.withEASTargets)(config, {
270
242
  targetName: productName,
271
243
  bundleIdentifier: bundleId,
272
244
  entitlements: entitlementsJson,
273
245
  });
274
246
  if (props.images) {
275
247
  Object.entries(props.images).forEach(([name, image]) => {
276
- (0, withImageAsset_1.withImageAsset)(config, {
248
+ (0, with_image_asset_1.withImageAsset)(config, {
277
249
  image,
278
250
  name,
279
251
  cwd: props.directory,
@@ -282,7 +254,7 @@ const withWidget = (config, props) => {
282
254
  }
283
255
  withConfigColors(config, props);
284
256
  if (props.icon) {
285
- (0, withIosIcon_1.withIosIcon)(config, {
257
+ (0, with_ios_icon_1.withIosIcon)(config, {
286
258
  type: props.type,
287
259
  cwd: props.directory,
288
260
  // TODO: read from the top-level icon.png file in the folder -- ERR this doesn't allow for URLs
@@ -304,7 +276,7 @@ const withConfigColors = (config, props) => {
304
276
  // if (props.accentColor) colors["AccentColor"] = props.accentColor;
305
277
  if (props.colors) {
306
278
  Object.entries(props.colors).forEach(([name, color]) => {
307
- (0, withIosColorset_1.withIosColorset)(config, {
279
+ (0, with_ios_colorset_1.withIosColorset)(config, {
308
280
  cwd: props.directory,
309
281
  name,
310
282
  color: typeof color === "string" ? color : color.light,
@@ -316,16 +288,3 @@ const withConfigColors = (config, props) => {
316
288
  return config;
317
289
  };
318
290
  exports.default = withWidget;
319
- function getSanitizedBundleIdentifier(value) {
320
- // According to the behavior observed when using the UI in Xcode.
321
- // Must start with a letter, period, or hyphen (not number).
322
- // Can only contain alphanumeric characters, periods, and hyphens.
323
- // Can have empty segments (e.g. com.example..app).
324
- return value.replace(/(^[^a-zA-Z.-]|[^a-zA-Z0-9-.])/g, "-");
325
- }
326
- function sanitizeNameForNonDisplayUse(name) {
327
- return name
328
- .replace(/[\W_]+/g, "")
329
- .normalize("NFD")
330
- .replace(/[\u0300-\u036f]/g, "");
331
- }
@@ -0,0 +1,3 @@
1
+ import { ConfigPlugin } from "@expo/config-plugins";
2
+ import { XcodeSettings } from "./configuration-list";
3
+ export declare const withXcodeChanges: ConfigPlugin<XcodeSettings>;
@@ -0,0 +1,401 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.withXcodeChanges = void 0;
7
+ const xcode_1 = require("@bacons/xcode");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const glob_1 = require("glob");
10
+ const path_1 = __importDefault(require("path"));
11
+ const target_1 = require("./target");
12
+ const with_bacons_xcode_1 = require("./with-bacons-xcode");
13
+ const assert_1 = __importDefault(require("assert"));
14
+ const configuration_list_1 = require("./configuration-list");
15
+ const util_1 = require("./util");
16
+ const withXcodeChanges = (config, props) => {
17
+ return (0, with_bacons_xcode_1.withXcodeProjectBeta)(config, async (config) => {
18
+ await applyXcodeChanges(config, config.modResults, props);
19
+ return config;
20
+ });
21
+ };
22
+ exports.withXcodeChanges = withXcodeChanges;
23
+ // function getMainMarketingVersion(project: XcodeProject) {
24
+ // const mainTarget = getMainAppTarget(project);
25
+ // const config = mainTarget.getDefaultConfiguration();
26
+ // const info = config.getInfoPlist();
27
+ // const version = info.CFBundleShortVersionString;
28
+ // // console.log('getMainMarketingVersion', mainTarget.getDisplayName(), version)
29
+ // if (!version || version === "$(MARKETING_VERSION)") {
30
+ // // console.log('getMainMarketingVersion.fallback', config.props.buildSettings.MARKETING_VERSION)
31
+ // return config.props.buildSettings.MARKETING_VERSION;
32
+ // }
33
+ // return version;
34
+ // }
35
+ async function applyXcodeChanges(config, project, props) {
36
+ var _a, _b;
37
+ var _c;
38
+ const mainAppTarget = (0, target_1.getMainAppTarget)(project);
39
+ // Special setting for share extensions.
40
+ if ((0, target_1.needsEmbeddedSwift)(props.type)) {
41
+ // Add ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES to the main app target
42
+ mainAppTarget.setBuildSetting("ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");
43
+ }
44
+ function getExtensionTargets() {
45
+ return project.rootObject.props.targets.filter((target) => {
46
+ return (xcode_1.PBXNativeTarget.is(target) && (0, target_1.isNativeTargetOfType)(target, props.type));
47
+ });
48
+ }
49
+ const targets = getExtensionTargets();
50
+ const productName = props.productName;
51
+ let targetToUpdate = (_a = targets.find((target) => target.props.productName === productName)) !== null && _a !== void 0 ? _a : targets[0];
52
+ if (targetToUpdate) {
53
+ (0, util_1.warnOnce)(`Target "${targetToUpdate.props.productName}" already exists, updating instead of creating a new one`);
54
+ }
55
+ const magicCwd = path_1.default.join(config._internal.projectRoot, "ios", props.cwd);
56
+ function applyDevelopmentTeamIdToTargets() {
57
+ var _a, _b;
58
+ var _c, _d, _e;
59
+ // Set to the provided value or any value.
60
+ const devTeamId = props.teamId ||
61
+ project.rootObject.props.targets
62
+ .map((target) => target.getDefaultBuildSetting("DEVELOPMENT_TEAM"))
63
+ .find(Boolean);
64
+ project.rootObject.props.targets.forEach((target) => {
65
+ if (devTeamId) {
66
+ target.setBuildSetting("DEVELOPMENT_TEAM", devTeamId);
67
+ }
68
+ else {
69
+ target.removeBuildSetting("DEVELOPMENT_TEAM");
70
+ }
71
+ });
72
+ for (const target of project.rootObject.props.targets) {
73
+ (_a = (_c = project.rootObject.props.attributes).TargetAttributes) !== null && _a !== void 0 ? _a : (_c.TargetAttributes = {});
74
+ // idk, attempting to prevent EAS Build from failing when it codesigns
75
+ (_b = (_d = project.rootObject.props.attributes.TargetAttributes)[_e = target.uuid]) !== null && _b !== void 0 ? _b : (_d[_e] = {
76
+ CreatedOnToolsVersion: "14.3",
77
+ ProvisioningStyle: "Automatic",
78
+ DevelopmentTeam: devTeamId,
79
+ });
80
+ }
81
+ }
82
+ function configureTargetWithKnownSettings(target) {
83
+ var _a, _b;
84
+ if ((_a = props.colors) === null || _a === void 0 ? void 0 : _a.$accent) {
85
+ target.setBuildSetting("ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME", "$accent");
86
+ }
87
+ else {
88
+ target.removeBuildSetting("ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME");
89
+ }
90
+ if ((_b = props.colors) === null || _b === void 0 ? void 0 : _b.$widgetBackground) {
91
+ target.setBuildSetting("ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME", "$widgetBackground");
92
+ }
93
+ else {
94
+ target.removeBuildSetting("ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME");
95
+ }
96
+ }
97
+ function configureTargetWithEntitlements(target) {
98
+ const entitlements = (0, glob_1.globSync)("*.entitlements", {
99
+ absolute: false,
100
+ cwd: magicCwd,
101
+ });
102
+ if (entitlements.length > 0) {
103
+ target.setBuildSetting("CODE_SIGN_ENTITLEMENTS", props.cwd + "/" + entitlements[0]);
104
+ }
105
+ else {
106
+ target.removeBuildSetting("CODE_SIGN_ENTITLEMENTS");
107
+ }
108
+ return entitlements;
109
+ // CODE_SIGN_ENTITLEMENTS = MattermostShare/MattermostShare.entitlements;
110
+ }
111
+ function syncMarketingVersions() {
112
+ var _a;
113
+ // In Expo managed projects, the version will always be overwritten.
114
+ // Because the overwrite happens after this script runs, we just set it to the config value.
115
+ const mainVersion = ((_a = config.ios) === null || _a === void 0 ? void 0 : _a.version) || config.version || '1.0.0';
116
+ // const mainVersion = getMainMarketingVersion(project);
117
+ project.rootObject.props.targets.forEach((target) => {
118
+ if (xcode_1.PBXNativeTarget.is(target)) {
119
+ target.setBuildSetting("MARKETING_VERSION", mainVersion);
120
+ }
121
+ });
122
+ }
123
+ function configureTargetWithPreview(target) {
124
+ const assets = (0, glob_1.globSync)("preview/*.xcassets", {
125
+ absolute: true,
126
+ cwd: magicCwd,
127
+ })[0];
128
+ if (assets) {
129
+ target.setBuildSetting("DEVELOPMENT_ASSET_PATHS", `"${props.cwd + "/preview"}"`);
130
+ }
131
+ else {
132
+ target.removeBuildSetting("DEVELOPMENT_ASSET_PATHS");
133
+ }
134
+ return assets;
135
+ }
136
+ function configureJsExport(target) {
137
+ if (props.exportJs) {
138
+ const shellScript = mainAppTarget.props.buildPhases.find((phase) => xcode_1.PBXShellScriptBuildPhase.is(phase) &&
139
+ phase.props.name === "Bundle React Native code and images");
140
+ if (!shellScript) {
141
+ console.warn('Failed to find the "Bundle React Native code and images" build phase in the main app target. Will not be able to configure: ' +
142
+ props.type);
143
+ return;
144
+ }
145
+ const currentShellScript = target.props.buildPhases.find((phase) => xcode_1.PBXShellScriptBuildPhase.is(phase) &&
146
+ phase.props.name === "Bundle React Native code and images");
147
+ if (!currentShellScript) {
148
+ // Link the same build script across targets to simplify updates.
149
+ target.props.buildPhases.push(shellScript);
150
+ // Alternatively, create a duplicate.
151
+ // target.createBuildPhase(PBXShellScriptBuildPhase, {
152
+ // ...shellScript.props,
153
+ // });
154
+ }
155
+ else {
156
+ // If there already is a bundler shell script and it's not the one from the main target, then update it.
157
+ if (currentShellScript.uuid !== shellScript.uuid) {
158
+ for (const key in shellScript.props) {
159
+ // @ts-expect-error
160
+ currentShellScript.props[key] = shellScript.props[key];
161
+ }
162
+ }
163
+ }
164
+ }
165
+ else {
166
+ // Remove the shell script build phase if it exists from a subsequent build.
167
+ const shellScript = target.props.buildPhases.findIndex((phase) => xcode_1.PBXShellScriptBuildPhase.is(phase) &&
168
+ phase.props.name === "Bundle React Native code and images");
169
+ if (shellScript !== -1) {
170
+ target.props.buildPhases.splice(shellScript, 1);
171
+ }
172
+ }
173
+ }
174
+ if (targetToUpdate) {
175
+ // Remove existing build phases
176
+ targetToUpdate.props.buildConfigurationList.props.buildConfigurations.forEach((config) => {
177
+ config.getReferrers().forEach((ref) => {
178
+ ref.removeReference(config.uuid);
179
+ });
180
+ config.removeFromProject();
181
+ });
182
+ // Remove existing build configuration list
183
+ targetToUpdate.props.buildConfigurationList
184
+ .getReferrers()
185
+ .forEach((ref) => {
186
+ ref.removeReference(targetToUpdate.props.buildConfigurationList.uuid);
187
+ });
188
+ targetToUpdate.props.buildConfigurationList.removeFromProject();
189
+ // Create new build phases
190
+ targetToUpdate.props.buildConfigurationList =
191
+ (0, configuration_list_1.createConfigurationListForType)(project, props);
192
+ }
193
+ else {
194
+ const productType = (0, target_1.productTypeForType)(props.type);
195
+ const isExtension = productType === "com.apple.product-type.app-extension";
196
+ const isExtensionKit = productType === "com.apple.product-type.extensionkit-extension";
197
+ const appExtensionBuildFile = xcode_1.PBXBuildFile.create(project, {
198
+ fileRef: xcode_1.PBXFileReference.create(project, {
199
+ explicitFileType: isExtensionKit
200
+ ? "wrapper.extensionkit-extension"
201
+ : "wrapper.app-extension",
202
+ includeInIndex: 0,
203
+ path: props.name + (isExtension ? ".appex" : ".app"),
204
+ sourceTree: "BUILT_PRODUCTS_DIR",
205
+ }),
206
+ settings: {
207
+ ATTRIBUTES: ["RemoveHeadersOnCopy"],
208
+ },
209
+ });
210
+ project.rootObject.ensureProductGroup().props.children.push(
211
+ // @ts-expect-error
212
+ appExtensionBuildFile.props.fileRef);
213
+ targetToUpdate = project.rootObject.createNativeTarget({
214
+ buildConfigurationList: (0, configuration_list_1.createConfigurationListForType)(project, props),
215
+ name: props.name,
216
+ productName,
217
+ // @ts-expect-error
218
+ productReference: appExtensionBuildFile.props.fileRef /* alphaExtension.appex */,
219
+ productType: productType,
220
+ });
221
+ const copyPhase = mainAppTarget.getCopyBuildPhaseForTarget(targetToUpdate);
222
+ if (!copyPhase.getBuildFile(appExtensionBuildFile.props.fileRef)) {
223
+ copyPhase.props.files.push(appExtensionBuildFile);
224
+ }
225
+ }
226
+ configureTargetWithKnownSettings(targetToUpdate);
227
+ configureTargetWithEntitlements(targetToUpdate);
228
+ configureTargetWithPreview(targetToUpdate);
229
+ targetToUpdate.ensureFrameworks(props.frameworks);
230
+ targetToUpdate.getSourcesBuildPhase();
231
+ targetToUpdate.getResourcesBuildPhase();
232
+ configureJsExport(targetToUpdate);
233
+ mainAppTarget.addDependency(targetToUpdate);
234
+ const assetsDir = path_1.default.join(magicCwd, "assets");
235
+ // TODO: Maybe just limit this to Safari extensions?
236
+ const explicitFolders = !fs_1.default.existsSync(assetsDir)
237
+ ? []
238
+ : fs_1.default
239
+ .readdirSync(assetsDir)
240
+ .filter((file) => file !== ".DS_Store" &&
241
+ fs_1.default.statSync(path_1.default.join(assetsDir, file)).isDirectory())
242
+ .map((file) => path_1.default.join("assets", file));
243
+ const protectedGroup = ensureProtectedGroup(project, path_1.default.dirname(props.cwd));
244
+ const sharedAssets = (0, glob_1.globSync)("_shared/*", {
245
+ absolute: false,
246
+ cwd: magicCwd,
247
+ });
248
+ // Also look for global shared assets in the parent targets/_shared directory
249
+ const targetsDir = path_1.default.dirname(magicCwd);
250
+ const globalSharedAssets = (0, glob_1.globSync)("_shared/*", {
251
+ absolute: false,
252
+ cwd: targetsDir,
253
+ });
254
+ let syncRootGroup = protectedGroup.props.children.find((child) => child.props.path === path_1.default.basename(props.cwd));
255
+ if (!syncRootGroup) {
256
+ syncRootGroup = xcode_1.PBXFileSystemSynchronizedRootGroup.create(project, {
257
+ path: path_1.default.basename(props.cwd),
258
+ exceptions: [
259
+ xcode_1.PBXFileSystemSynchronizedBuildFileExceptionSet.create(project, {
260
+ target: targetToUpdate,
261
+ membershipExceptions: [
262
+ // TODO: What other files belong here, why is this here?
263
+ "Info.plist",
264
+ // Exclude the config path
265
+ path_1.default.relative(magicCwd, props.configPath),
266
+ ].sort(),
267
+ }),
268
+ ],
269
+ explicitFileTypes: {},
270
+ explicitFolders: [
271
+ // Replaces the previous `lastKnownFileType: "folder",` system that's used in things like Safari extensions to include folders of assets.
272
+ // ex: `"Resources/_locales", "Resources/images"`
273
+ ...explicitFolders,
274
+ ],
275
+ sourceTree: "<group>",
276
+ });
277
+ if (!targetToUpdate.props.fileSystemSynchronizedGroups) {
278
+ targetToUpdate.props.fileSystemSynchronizedGroups = [];
279
+ }
280
+ targetToUpdate.props.fileSystemSynchronizedGroups.push(syncRootGroup);
281
+ protectedGroup.props.children.push(syncRootGroup);
282
+ }
283
+ // If there's a `_shared` folder, create a PBXFileSystemSynchronizedBuildFileExceptionSet and set the `target` to the main app target. Then add exceptions to the new target's PBXFileSystemSynchronizedRootGroup's exceptions. Finally, ensure the relative paths for each file in the _shared folder are added to the `membershipExceptions` array.
284
+ (0, assert_1.default)(syncRootGroup instanceof xcode_1.PBXFileSystemSynchronizedRootGroup);
285
+ (_b = (_c = syncRootGroup.props).exceptions) !== null && _b !== void 0 ? _b : (_c.exceptions = []);
286
+ const existingExceptionSet = syncRootGroup.props.exceptions.find((exception) => exception instanceof xcode_1.PBXFileSystemSynchronizedBuildFileExceptionSet &&
287
+ exception.props.target === mainAppTarget);
288
+ if (sharedAssets.length) {
289
+ const exceptionSet = existingExceptionSet ||
290
+ xcode_1.PBXFileSystemSynchronizedBuildFileExceptionSet.create(project, {
291
+ target: mainAppTarget,
292
+ });
293
+ exceptionSet.props.membershipExceptions = sharedAssets.sort();
294
+ syncRootGroup.props.exceptions.push(exceptionSet);
295
+ }
296
+ else {
297
+ // Remove the exception set if there are no shared assets.
298
+ existingExceptionSet === null || existingExceptionSet === void 0 ? void 0 : existingExceptionSet.removeFromProject();
299
+ }
300
+ function configureTargetWithGlobalSharedAssets(target) {
301
+ var _a;
302
+ var _b;
303
+ if (!globalSharedAssets.length)
304
+ return;
305
+ // Create or find the global shared synchronized root group
306
+ let globalSharedSyncGroup = protectedGroup.props.children.find((child) => child.props.path === "_shared" &&
307
+ child instanceof xcode_1.PBXFileSystemSynchronizedRootGroup);
308
+ if (!globalSharedSyncGroup) {
309
+ globalSharedSyncGroup = xcode_1.PBXFileSystemSynchronizedRootGroup.create(project, {
310
+ path: "_shared",
311
+ exceptions: [
312
+ // Create exception set for the main app target
313
+ xcode_1.PBXFileSystemSynchronizedBuildFileExceptionSet.create(project, {
314
+ target: mainAppTarget,
315
+ membershipExceptions: globalSharedAssets.sort(),
316
+ }),
317
+ // Create exception set for the extension target
318
+ xcode_1.PBXFileSystemSynchronizedBuildFileExceptionSet.create(project, {
319
+ target: target,
320
+ membershipExceptions: globalSharedAssets.sort(),
321
+ }),
322
+ ],
323
+ explicitFileTypes: {},
324
+ explicitFolders: [],
325
+ sourceTree: "<group>",
326
+ });
327
+ // Add to both targets' fileSystemSynchronizedGroups
328
+ if (!mainAppTarget.props.fileSystemSynchronizedGroups) {
329
+ mainAppTarget.props.fileSystemSynchronizedGroups = [];
330
+ }
331
+ mainAppTarget.props.fileSystemSynchronizedGroups.push(globalSharedSyncGroup);
332
+ if (!target.props.fileSystemSynchronizedGroups) {
333
+ target.props.fileSystemSynchronizedGroups = [];
334
+ }
335
+ target.props.fileSystemSynchronizedGroups.push(globalSharedSyncGroup);
336
+ protectedGroup.props.children.push(globalSharedSyncGroup);
337
+ }
338
+ else {
339
+ // Update existing synchronized group with current global shared assets
340
+ (_a = (_b = globalSharedSyncGroup.props).exceptions) !== null && _a !== void 0 ? _a : (_b.exceptions = []);
341
+ // Update or create exception set for main app target
342
+ let mainAppExceptionSet = globalSharedSyncGroup.props.exceptions.find((exception) => exception instanceof xcode_1.PBXFileSystemSynchronizedBuildFileExceptionSet &&
343
+ exception.props.target === mainAppTarget);
344
+ if (!mainAppExceptionSet) {
345
+ mainAppExceptionSet =
346
+ xcode_1.PBXFileSystemSynchronizedBuildFileExceptionSet.create(project, {
347
+ target: mainAppTarget,
348
+ membershipExceptions: globalSharedAssets.sort(),
349
+ });
350
+ globalSharedSyncGroup.props.exceptions.push(mainAppExceptionSet);
351
+ }
352
+ else {
353
+ mainAppExceptionSet.props.membershipExceptions =
354
+ globalSharedAssets.sort();
355
+ }
356
+ // Update or create exception set for extension target
357
+ let extensionExceptionSet = globalSharedSyncGroup.props.exceptions.find((exception) => exception instanceof xcode_1.PBXFileSystemSynchronizedBuildFileExceptionSet &&
358
+ exception.props.target === target);
359
+ if (!extensionExceptionSet) {
360
+ extensionExceptionSet =
361
+ xcode_1.PBXFileSystemSynchronizedBuildFileExceptionSet.create(project, {
362
+ target: target,
363
+ membershipExceptions: globalSharedAssets.sort(),
364
+ });
365
+ globalSharedSyncGroup.props.exceptions.push(extensionExceptionSet);
366
+ }
367
+ else {
368
+ extensionExceptionSet.props.membershipExceptions =
369
+ globalSharedAssets.sort();
370
+ }
371
+ // Ensure the current target has the synchronized group in its fileSystemSynchronizedGroups
372
+ if (!target.props.fileSystemSynchronizedGroups) {
373
+ target.props.fileSystemSynchronizedGroups = [];
374
+ }
375
+ // Check if this target already has the synchronized group
376
+ const hasGroup = target.props.fileSystemSynchronizedGroups.some((group) => group === globalSharedSyncGroup);
377
+ if (!hasGroup) {
378
+ target.props.fileSystemSynchronizedGroups.push(globalSharedSyncGroup);
379
+ }
380
+ }
381
+ }
382
+ configureTargetWithGlobalSharedAssets(targetToUpdate);
383
+ applyDevelopmentTeamIdToTargets();
384
+ syncMarketingVersions();
385
+ return project;
386
+ }
387
+ const PROTECTED_GROUP_NAME = "expo:targets";
388
+ function ensureProtectedGroup(project, relativePath = "../targets") {
389
+ const hasProtectedGroup = project.rootObject.props.mainGroup
390
+ .getChildGroups()
391
+ .find((group) => group.getDisplayName() === PROTECTED_GROUP_NAME);
392
+ const protectedGroup = hasProtectedGroup !== null && hasProtectedGroup !== void 0 ? hasProtectedGroup : xcode_1.PBXGroup.create(project, {
393
+ name: PROTECTED_GROUP_NAME,
394
+ path: relativePath,
395
+ sourceTree: "<group>",
396
+ });
397
+ if (!hasProtectedGroup) {
398
+ project.rootObject.props.mainGroup.props.children.unshift(protectedGroup);
399
+ }
400
+ return protectedGroup;
401
+ }
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@bacons/apple-targets",
3
- "version": "3.0.5",
3
+ "version": "3.0.7",
4
4
  "description": "Generate Apple Targets with Expo Prebuild",
5
5
  "main": "build/ExtensionStorage.js",
6
6
  "types": "build/ExtensionStorage.d.ts",
7
+ "homepage": "https://github.com/evanbacon/expo-apple-targets",
7
8
  "files": [
8
9
  "app.plugin.js",
9
10
  "app.plugin.d.ts",
@@ -30,7 +31,11 @@
30
31
  "config-plugins",
31
32
  "apple-targets"
32
33
  ],
33
- "author": "Evan Bacon",
34
+ "author": {
35
+ "email": "baconbrix@gmail.com",
36
+ "name": "Evan Bacon",
37
+ "url": "https://evanbacon.dev"
38
+ },
34
39
  "license": "MIT",
35
40
  "dependencies": {
36
41
  "@bacons/xcode": "1.0.0-alpha.27",