@bacons/apple-targets 0.0.10 → 0.1.0

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.
package/README.md CHANGED
@@ -9,11 +9,13 @@ An experimental Expo Config Plugin that generates native Apple Targets like Widg
9
9
 
10
10
  ## 🚀 How to use
11
11
 
12
+ > This plugin requires at least CocoaPods 1.16.2 and Xcode 16.
13
+
12
14
  - Add targets to `targets/` directory with an `expo-target.config.json` file.
13
15
  - Currently, if you don't have an `Info.plist`, it'll be generated on `npx expo prebuild`. This may be changed in the future so if you have an `Info.plist` it'll be used, otherwise, it'll be generated.
14
16
  - Any files in a top-level `target/*/assets` directory will be linked as resources of the target. This was added to support Safari Extensions.
15
17
  - A single top-level `*.entitlements` file will be linked as the entitlements of the target. This is not currently used in EAS Capability signing, but may be in the future.
16
- - All top-level swift files will be linked as build sources of the target. There is currently no support for storyboard or `.xib` files because I can't be bothered.
18
+ - All Swift files will be linked as build sources of the target. There is currently no support for storyboard or `.xib` files because I can't be bothered.
17
19
  - All top-level `*.xcassets` will be linked as resources, and accessible in the targets. If you add files outside of Xcode, you'll need to re-run `npx expo prebuild` to link them.
18
20
  - In Expo SDK +52, set the `ios.appleTeamId`, for SDK 51 and below, set the `appleTeamId` prop in the Config Plugin in `app.config.js`:
19
21
 
package/build/index.js CHANGED
@@ -26,6 +26,7 @@ const withTargetsDir = (config, _props) => {
26
26
  appleTeamId,
27
27
  ...require(configPath),
28
28
  directory: path_1.default.relative(projectRoot, path_1.default.dirname(configPath)),
29
+ configPath,
29
30
  });
30
31
  });
31
32
  (0, withPodTargetExtension_1.withPodTargetExtension)(config);
@@ -2,6 +2,7 @@ import { ConfigPlugin } from "@expo/config-plugins";
2
2
  import { Config } from "./config";
3
3
  type Props = Config & {
4
4
  directory: string;
5
+ configPath: string;
5
6
  };
6
7
  declare const withWidget: ConfigPlugin<Props>;
7
8
  export default withWidget;
@@ -113,6 +113,7 @@ const withWidget = (config, props) => {
113
113
  const targetName = (_c = props.name) !== null && _c !== void 0 ? _c : widget;
114
114
  const bundleId = config.ios.bundleIdentifier + "." + widget;
115
115
  (0, withXcodeChanges_1.withXcodeChanges)(config, {
116
+ configPath: props.configPath,
116
117
  name: targetName,
117
118
  cwd: "../" +
118
119
  path_1.default.relative(config._internal.projectRoot, path_1.default.resolve(props.directory)),
@@ -14,5 +14,7 @@ export type XcodeSettings = {
14
14
  teamId?: string;
15
15
  icon?: string;
16
16
  exportJs?: boolean;
17
+ /** File path to the extension config file. */
18
+ configPath: string;
17
19
  };
18
20
  export declare const withXcodeChanges: ConfigPlugin<XcodeSettings>;
@@ -5,7 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.withXcodeChanges = void 0;
7
7
  const xcode_1 = require("@bacons/xcode");
8
- const json_1 = require("@bacons/xcode/json");
9
8
  const fs_1 = __importDefault(require("fs"));
10
9
  const glob_1 = require("glob");
11
10
  const path_1 = __importDefault(require("path"));
@@ -40,14 +39,19 @@ function createNotificationContentConfigurationList(project, { name, cwd, bundle
40
39
  INFOPLIST_KEY_CFBundleDisplayName: name,
41
40
  INFOPLIST_KEY_NSHumanReadableCopyright: "",
42
41
  IPHONEOS_DEPLOYMENT_TARGET: deploymentTarget,
43
- LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks",
42
+ LD_RUNPATH_SEARCH_PATHS: [
43
+ "$(inherited)",
44
+ "@executable_path/Frameworks",
45
+ "@executable_path/../../Frameworks",
46
+ ],
44
47
  MARKETING_VERSION: "1.0",
45
48
  MTL_FAST_MATH: "YES",
46
49
  PRODUCT_BUNDLE_IDENTIFIER: bundleId,
47
50
  PRODUCT_NAME: "$(TARGET_NAME)",
48
51
  SKIP_INSTALL: "YES",
49
52
  SWIFT_EMIT_LOC_STRINGS: "YES",
50
- SWIFT_OPTIMIZATION_LEVEL: "-Owholemodule",
53
+ SWIFT_COMPILATION_MODE: "wholemodule",
54
+ SWIFT_OPTIMIZATION_LEVEL: "-O",
51
55
  SWIFT_VERSION: "5.0",
52
56
  TARGETED_DEVICE_FAMILY: "1,2",
53
57
  };
@@ -135,7 +139,11 @@ function createShareConfigurationList(project, { name, cwd, bundleId, deployment
135
139
  INFOPLIST_KEY_CFBundleDisplayName: name,
136
140
  INFOPLIST_KEY_NSHumanReadableCopyright: "",
137
141
  IPHONEOS_DEPLOYMENT_TARGET: deploymentTarget,
138
- LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks",
142
+ LD_RUNPATH_SEARCH_PATHS: [
143
+ "$(inherited)",
144
+ "@executable_path/Frameworks",
145
+ "@executable_path/../../Frameworks",
146
+ ],
139
147
  MARKETING_VERSION: "1.0",
140
148
  MTL_FAST_MATH: "YES",
141
149
  PRODUCT_BUNDLE_IDENTIFIER: bundleId,
@@ -202,7 +210,11 @@ function createIMessageConfigurationList(project, { name, cwd, bundleId, deploym
202
210
  INFOPLIST_KEY_CFBundleDisplayName: name,
203
211
  INFOPLIST_KEY_NSHumanReadableCopyright: "",
204
212
  IPHONEOS_DEPLOYMENT_TARGET: deploymentTarget,
205
- LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks",
213
+ LD_RUNPATH_SEARCH_PATHS: [
214
+ "$(inherited)",
215
+ "@executable_path/Frameworks",
216
+ "@executable_path/../../Frameworks",
217
+ ],
206
218
  MARKETING_VERSION: "1.0",
207
219
  MTL_FAST_MATH: "YES",
208
220
  PRODUCT_BUNDLE_IDENTIFIER: bundleId,
@@ -260,7 +272,7 @@ function createWatchAppConfigurationList(project, { name, cwd, bundleId, deploym
260
272
  INFOPLIST_KEY_WKCompanionAppBundleIdentifier: mainAppTarget.props.buildSettings.PRODUCT_BUNDLE_IDENTIFIER,
261
273
  // INFOPLIST_KEY_WKCompanionAppBundleIdentifier: "$(BUNDLE_IDENTIFIER)",
262
274
  // INFOPLIST_KEY_WKCompanionAppBundleIdentifier: rootBundleId,
263
- LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks",
275
+ LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks"],
264
276
  MARKETING_VERSION: "1.0",
265
277
  MTL_FAST_MATH: "YES",
266
278
  PRODUCT_BUNDLE_IDENTIFIER: bundleId,
@@ -292,7 +304,8 @@ function createWatchAppConfigurationList(project, { name, cwd, bundleId, deploym
292
304
  buildSettings: {
293
305
  ...common,
294
306
  // Diff
295
- SWIFT_OPTIMIZATION_LEVEL: "-Owholemodule",
307
+ SWIFT_COMPILATION_MODE: "wholemodule",
308
+ SWIFT_OPTIMIZATION_LEVEL: "-O",
296
309
  COPY_PHASE_STRIP: "NO",
297
310
  DEBUG_INFORMATION_FORMAT: "dwarf-with-dsym",
298
311
  },
@@ -321,7 +334,11 @@ function createSafariConfigurationList(project, { name, cwd, bundleId, deploymen
321
334
  INFOPLIST_KEY_CFBundleDisplayName: name,
322
335
  INFOPLIST_KEY_NSHumanReadableCopyright: "",
323
336
  IPHONEOS_DEPLOYMENT_TARGET: deploymentTarget,
324
- LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks",
337
+ LD_RUNPATH_SEARCH_PATHS: [
338
+ "$(inherited)",
339
+ "@executable_path/Frameworks",
340
+ "@executable_path/../../Frameworks",
341
+ ],
325
342
  MARKETING_VERSION: "1.0",
326
343
  MTL_FAST_MATH: "YES",
327
344
  PRODUCT_BUNDLE_IDENTIFIER: bundleId,
@@ -348,7 +365,8 @@ function createSafariConfigurationList(project, { name, cwd, bundleId, deploymen
348
365
  buildSettings: {
349
366
  ...common,
350
367
  // Diff
351
- SWIFT_OPTIMIZATION_LEVEL: "-Owholemodule",
368
+ SWIFT_COMPILATION_MODE: "wholemodule",
369
+ SWIFT_OPTIMIZATION_LEVEL: "-O",
352
370
  COPY_PHASE_STRIP: "NO",
353
371
  DEBUG_INFORMATION_FORMAT: "dwarf-with-dsym",
354
372
  },
@@ -404,7 +422,7 @@ function createAppClipConfigurationList(project, { name, cwd, bundleId, deployme
404
422
  ...infoPlist,
405
423
  ...superCommon,
406
424
  ASSETCATALOG_COMPILER_APPICON_NAME: "AppIcon",
407
- LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks",
425
+ LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks"],
408
426
  MTL_FAST_MATH: "YES",
409
427
  ENABLE_PREVIEWS: "YES",
410
428
  };
@@ -424,7 +442,8 @@ function createAppClipConfigurationList(project, { name, cwd, bundleId, deployme
424
442
  buildSettings: {
425
443
  ...common,
426
444
  // Diff
427
- SWIFT_OPTIMIZATION_LEVEL: "-Owholemodule",
445
+ SWIFT_COMPILATION_MODE: "wholemodule",
446
+ SWIFT_OPTIMIZATION_LEVEL: "-O",
428
447
  COPY_PHASE_STRIP: "NO",
429
448
  DEBUG_INFORMATION_FORMAT: "dwarf-with-dsym",
430
449
  },
@@ -458,7 +477,11 @@ function createConfigurationList(project, { name, cwd, bundleId, deploymentTarge
458
477
  INFOPLIST_KEY_CFBundleDisplayName: name,
459
478
  INFOPLIST_KEY_NSHumanReadableCopyright: "",
460
479
  IPHONEOS_DEPLOYMENT_TARGET: deploymentTarget,
461
- LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks",
480
+ LD_RUNPATH_SEARCH_PATHS: [
481
+ "$(inherited)",
482
+ "@executable_path/Frameworks",
483
+ "@executable_path/../../Frameworks",
484
+ ],
462
485
  MARKETING_VERSION: "1.0",
463
486
  MTL_ENABLE_DEBUG_INFO: "INCLUDE_SOURCE",
464
487
  MTL_FAST_MATH: "YES",
@@ -494,14 +517,19 @@ function createConfigurationList(project, { name, cwd, bundleId, deploymentTarge
494
517
  INFOPLIST_KEY_CFBundleDisplayName: name,
495
518
  INFOPLIST_KEY_NSHumanReadableCopyright: "",
496
519
  IPHONEOS_DEPLOYMENT_TARGET: deploymentTarget,
497
- LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks",
520
+ LD_RUNPATH_SEARCH_PATHS: [
521
+ "$(inherited)",
522
+ "@executable_path/Frameworks",
523
+ "@executable_path/../../Frameworks",
524
+ ],
498
525
  MARKETING_VERSION: "1.0",
499
526
  MTL_FAST_MATH: "YES",
500
527
  PRODUCT_BUNDLE_IDENTIFIER: bundleId,
501
528
  PRODUCT_NAME: "$(TARGET_NAME)",
502
529
  SKIP_INSTALL: "YES",
503
530
  SWIFT_EMIT_LOC_STRINGS: "YES",
504
- SWIFT_OPTIMIZATION_LEVEL: "-Owholemodule",
531
+ SWIFT_COMPILATION_MODE: "wholemodule",
532
+ SWIFT_OPTIMIZATION_LEVEL: "-O",
505
533
  SWIFT_VERSION: "5",
506
534
  TARGETED_DEVICE_FAMILY: "1,2",
507
535
  },
@@ -540,37 +568,6 @@ function createConfigurationListForType(project, props) {
540
568
  return createNotificationContentConfigurationList(project, props);
541
569
  }
542
570
  }
543
- /** It's common for all frameworks to exist in the top-level "Frameworks" folder that shows in Xcode. */
544
- function addFrameworksToDisplayFolder(project, frameworks) {
545
- var _a;
546
- const mainFrameworksGroup = (_a = project.rootObject.props.mainGroup
547
- .getChildGroups()
548
- .find((group) => group.getDisplayName() === "Frameworks")) !== null && _a !== void 0 ? _a :
549
- // If this happens, there's a big problem. But just in case...
550
- project.rootObject.props.mainGroup.createGroup({
551
- name: "Frameworks",
552
- sourceTree: "<group>",
553
- });
554
- frameworks.forEach((file) => {
555
- if (!mainFrameworksGroup.props.children.find((child) => child.uuid === file.uuid)) {
556
- mainFrameworksGroup.props.children.push(file);
557
- }
558
- });
559
- }
560
- function getFramework(project, name) {
561
- const frameworkName = name + ".framework";
562
- for (const [, entry] of project.entries()) {
563
- if (xcode_1.PBXFileReference.is(entry) &&
564
- entry.props.lastKnownFileType === "wrapper.framework" &&
565
- entry.props.sourceTree === "SDKROOT" &&
566
- entry.props.name === frameworkName) {
567
- return entry;
568
- }
569
- }
570
- return xcode_1.PBXFileReference.create(project, {
571
- path: "System/Library/Frameworks/" + frameworkName,
572
- });
573
- }
574
571
  async function applyXcodeChanges(config, project, props) {
575
572
  var _a, _b;
576
573
  const mainAppTarget = (0, target_1.getMainAppTarget)(project);
@@ -592,18 +589,6 @@ async function applyXcodeChanges(config, project, props) {
592
589
  console.log(`Target "${targetToUpdate.props.productName}" already exists, updating instead of creating a new one`);
593
590
  }
594
591
  const magicCwd = path_1.default.join(config._internal.projectRoot, "ios", props.cwd);
595
- function getOrCreateBuildFile(file) {
596
- for (const entry of file.getReferrers()) {
597
- if (xcode_1.PBXBuildFile.is(entry) && entry.props.fileRef.uuid === file.uuid) {
598
- return entry;
599
- }
600
- }
601
- return xcode_1.PBXBuildFile.create(project, {
602
- fileRef: file,
603
- });
604
- }
605
- // Add the widget target to the display folder (cosmetic)
606
- addFrameworksToDisplayFolder(project, props.frameworks.map((framework) => getFramework(project, framework)));
607
592
  const developmentTeamId = (_b = props.teamId) !== null && _b !== void 0 ? _b : mainAppTarget.getDefaultBuildSetting("DEVELOPMENT_TEAM");
608
593
  if (!developmentTeamId) {
609
594
  throw new Error("Couldn't find DEVELOPMENT_TEAM in Xcode project and none were provided in the Expo config.");
@@ -646,19 +631,11 @@ async function applyXcodeChanges(config, project, props) {
646
631
  }
647
632
  function configureTargetWithEntitlements(target) {
648
633
  const entitlements = (0, glob_1.sync)("*.entitlements", {
649
- absolute: true,
634
+ absolute: false,
650
635
  cwd: magicCwd,
651
- }).map((file) => {
652
- return xcode_1.PBXBuildFile.create(project, {
653
- fileRef: xcode_1.PBXFileReference.create(project, {
654
- path: path_1.default.basename(file),
655
- explicitFileType: "text.plist.entitlements",
656
- sourceTree: "<group>",
657
- }),
658
- });
659
636
  });
660
637
  if (entitlements.length > 0) {
661
- target.setBuildSetting("CODE_SIGN_ENTITLEMENTS", props.cwd + "/" + entitlements[0].props.fileRef.props.path);
638
+ target.setBuildSetting("CODE_SIGN_ENTITLEMENTS", props.cwd + "/" + entitlements[0]);
662
639
  }
663
640
  else {
664
641
  target.removeBuildSetting("CODE_SIGN_ENTITLEMENTS");
@@ -674,50 +651,6 @@ async function applyXcodeChanges(config, project, props) {
674
651
  }
675
652
  });
676
653
  }
677
- function buildFileGroupHierarchy(files) {
678
- const root = [];
679
- function getOrCreateGroup(name, segment, group) {
680
- const fullPath = path_1.default.join(magicCwd, name);
681
- let newGroup = group.find((child) => child.props.isa === json_1.ISA.PBXGroup && child.props.path === fullPath);
682
- if (!newGroup) {
683
- newGroup = xcode_1.PBXGroup.create(project, {
684
- name: segment,
685
- path: fullPath,
686
- children: [],
687
- });
688
- group.push(newGroup);
689
- }
690
- return newGroup;
691
- }
692
- files.forEach((filePath) => {
693
- const pathSegments = filePath.split(path_1.default.sep);
694
- let currentLevel = root;
695
- pathSegments.forEach((part, index) => {
696
- const isRoot = part === ".";
697
- const currentPath = pathSegments.slice(0, index + 1).join(path_1.default.sep);
698
- currentLevel = isRoot ? currentLevel : getOrCreateGroup(currentPath, part, currentLevel).props.children;
699
- const isFinalPart = index === pathSegments.length - 1;
700
- if (swiftBuildFiles[filePath] && isFinalPart) {
701
- const filex = swiftBuildFiles[filePath];
702
- currentLevel.push(...filex);
703
- }
704
- });
705
- });
706
- return root;
707
- }
708
- function generateProjectGroups(project, structure, magicCwd) {
709
- return structure.map((item) => {
710
- if (item.props.isa === json_1.ISA.PBXGroup) {
711
- // @ts-ignore
712
- const childGroups = generateProjectGroups(project, item.props.children, path_1.default.join(magicCwd, item.props.name));
713
- item.props.children = childGroups;
714
- return item;
715
- }
716
- else if (item.props.isa === json_1.ISA.PBXBuildFile) {
717
- return item.props.fileRef;
718
- }
719
- });
720
- }
721
654
  function configureTargetWithPreview(target) {
722
655
  const assets = (0, glob_1.sync)("preview/*.xcassets", {
723
656
  absolute: true,
@@ -736,7 +669,9 @@ async function applyXcodeChanges(config, project, props) {
736
669
  const shellScript = mainAppTarget.props.buildPhases.find((phase) => xcode_1.PBXShellScriptBuildPhase.is(phase) &&
737
670
  phase.props.name === "Bundle React Native code and images");
738
671
  if (!shellScript) {
739
- throw new Error('Failed to find the "Bundle React Native code and images" build phase in the main app target.');
672
+ 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: ' +
673
+ props.type);
674
+ return;
740
675
  }
741
676
  const currentShellScript = target.props.buildPhases.find((phase) => xcode_1.PBXShellScriptBuildPhase.is(phase) &&
742
677
  phase.props.name === "Bundle React Native code and images");
@@ -777,7 +712,8 @@ async function applyXcodeChanges(config, project, props) {
777
712
  });
778
713
  targetToUpdate.props.buildConfigurationList.removeFromProject();
779
714
  // Create new build phases
780
- targetToUpdate.props.buildConfigurationList = createConfigurationListForType(project, props);
715
+ targetToUpdate.props.buildConfigurationList =
716
+ createConfigurationListForType(project, props);
781
717
  configureTargetWithEntitlements(targetToUpdate);
782
718
  configureTargetWithPreview(targetToUpdate);
783
719
  configureTargetWithKnownSettings(targetToUpdate);
@@ -786,126 +722,7 @@ async function applyXcodeChanges(config, project, props) {
786
722
  syncMarketingVersions();
787
723
  return project;
788
724
  }
789
- // Build Files
790
- const swiftBuildFiles = {};
791
- (0, glob_1.sync)("**/*.swift", {
792
- absolute: true,
793
- cwd: magicCwd,
794
- }).forEach((file) => {
795
- const fileDir = path_1.default.dirname(path_1.default.relative(magicCwd, file));
796
- const pbxFile = xcode_1.PBXBuildFile.create(project, {
797
- fileRef: xcode_1.PBXFileReference.create(project, {
798
- path: file,
799
- sourceTree: "<group>",
800
- }),
801
- });
802
- if (!swiftBuildFiles[fileDir]) {
803
- swiftBuildFiles[fileDir] = [];
804
- }
805
- swiftBuildFiles[fileDir].push(pbxFile);
806
- return undefined;
807
- });
808
- const swiftStructure = buildFileGroupHierarchy(Object.keys(swiftBuildFiles));
809
- const swiftGroups = generateProjectGroups(project, swiftStructure, magicCwd);
810
- // NOTE: Single-level only
811
- const intentFiles = (0, glob_1.sync)("*.intentdefinition", {
812
- absolute: true,
813
- cwd: magicCwd,
814
- }).map((file) => {
815
- return xcode_1.PBXFileReference.create(project, {
816
- lastKnownFileType: "file.intentdefinition",
817
- path: path_1.default.basename(file),
818
- sourceTree: "<group>",
819
- });
820
- });
821
- const intentBuildFiles = [0, 1].map((_) => intentFiles.map((file) => {
822
- return xcode_1.PBXBuildFile.create(project, {
823
- fileRef: file,
824
- });
825
- }));
826
- let assetFiles = [
827
- // All assets`
828
- // "assets/*",
829
- // NOTE: Single-level only
830
- "*.xcassets",
831
- ]
832
- .map((glob) => (0, glob_1.sync)(glob, {
833
- absolute: true,
834
- cwd: magicCwd,
835
- }).map((file) => {
836
- return xcode_1.PBXBuildFile.create(project, {
837
- fileRef: xcode_1.PBXFileReference.create(project, {
838
- path: path_1.default.basename(file),
839
- sourceTree: "<group>",
840
- }),
841
- });
842
- }))
843
- .flat();
844
- const resAssets = [];
845
- // Support for LaunchScreen files
846
- const baseProj = path_1.default.join(magicCwd, "Base.lproj");
847
- if (fs_1.default.existsSync(baseProj)) {
848
- // Link LaunchScreen.storyboard
849
- fs_1.default.readdirSync(baseProj).forEach((file) => {
850
- if (file === ".DS_Store")
851
- return;
852
- const stat = fs_1.default.statSync(path_1.default.join(baseProj, file));
853
- if (stat.isFile()) {
854
- if (file.endsWith(".storyboard")) {
855
- assetFiles.push(xcode_1.PBXBuildFile.create(project, {
856
- fileRef: xcode_1.PBXVariantGroup.create(project, {
857
- name: file,
858
- sourceTree: "<group>",
859
- children: [
860
- xcode_1.PBXFileReference.create(project, {
861
- lastKnownFileType: "file.storyboard",
862
- name: "Base",
863
- path: path_1.default.join("Base.lproj", file),
864
- sourceTree: "<group>",
865
- }),
866
- ],
867
- }),
868
- }));
869
- }
870
- }
871
- });
872
- }
873
- // TODO: Maybe just limit this to Safari?
874
- if (fs_1.default.existsSync(path_1.default.join(magicCwd, "assets"))) {
875
- // get top-level directories in `assets/` and append them to assetFiles as folder types
876
- fs_1.default.readdirSync(path_1.default.join(magicCwd, "assets")).forEach((file) => {
877
- if (file === ".DS_Store")
878
- return;
879
- const stat = fs_1.default.statSync(path_1.default.join(magicCwd, "assets", file));
880
- if (stat.isDirectory()) {
881
- resAssets.push(xcode_1.PBXBuildFile.create(project, {
882
- fileRef: xcode_1.PBXFileReference.create(project, {
883
- path: file,
884
- sourceTree: "<group>",
885
- lastKnownFileType: "folder",
886
- }),
887
- }));
888
- }
889
- else if (stat.isFile()) {
890
- resAssets.push(xcode_1.PBXBuildFile.create(project, {
891
- fileRef: xcode_1.PBXFileReference.create(project, {
892
- path: file,
893
- explicitFileType: file.endsWith(".js")
894
- ? "sourcecode.javascript"
895
- : file.endsWith(".json")
896
- ? "text.json"
897
- : file.endsWith(".html")
898
- ? "text.html"
899
- : file.endsWith(".css")
900
- ? "text.css"
901
- : "text",
902
- sourceTree: "<group>",
903
- }),
904
- }));
905
- }
906
- });
907
- }
908
- const alphaExtensionAppexBf = xcode_1.PBXBuildFile.create(project, {
725
+ const appExtensionBuildFile = xcode_1.PBXBuildFile.create(project, {
909
726
  fileRef: xcode_1.PBXFileReference.create(project, {
910
727
  explicitFileType: "wrapper.app-extension",
911
728
  includeInIndex: 0,
@@ -918,44 +735,30 @@ async function applyXcodeChanges(config, project, props) {
918
735
  });
919
736
  project.rootObject.ensureProductGroup().props.children.push(
920
737
  // @ts-expect-error
921
- alphaExtensionAppexBf.props.fileRef);
922
- const widgetTarget = project.rootObject.createNativeTarget({
738
+ appExtensionBuildFile.props.fileRef);
739
+ const extensionTarget = project.rootObject.createNativeTarget({
923
740
  buildConfigurationList: createConfigurationListForType(project, props),
924
741
  name: productName,
925
742
  productName,
926
743
  // @ts-expect-error
927
- productReference: alphaExtensionAppexBf.props.fileRef /* alphaExtension.appex */,
744
+ productReference: appExtensionBuildFile.props.fileRef /* alphaExtension.appex */,
928
745
  productType: (0, target_1.productTypeForType)(props.type),
929
746
  });
930
- configureTargetWithKnownSettings(widgetTarget);
931
- const entitlementFiles = configureTargetWithEntitlements(widgetTarget);
932
- configureTargetWithPreview(widgetTarget);
933
- widgetTarget.createBuildPhase(xcode_1.PBXSourcesBuildPhase, {
934
- files: [
935
- ...Object.values(swiftBuildFiles).flat(),
936
- ...intentBuildFiles[0],
937
- // ...entitlementFiles
938
- ],
939
- // CD0706152A2EBE2E009C1192 /* index.swift in Sources */,
940
- // CD07061A2A2EBE2F009C1192 /* alpha.intentdefinition in Sources */,
941
- // CD0706112A2EBE2E009C1192 /* alphaBundle.swift in Sources */,
942
- // CD0706132A2EBE2E009C1192 /* alphaLiveActivity.swift in Sources */,
943
- });
944
- widgetTarget.createBuildPhase(xcode_1.PBXFrameworksBuildPhase, {
945
- files: props.frameworks.map((framework) => getOrCreateBuildFile(getFramework(project, framework))),
946
- });
947
- widgetTarget.createBuildPhase(xcode_1.PBXResourcesBuildPhase, {
948
- files: [...assetFiles, ...resAssets],
949
- });
950
- configureJsExport(widgetTarget);
747
+ configureTargetWithKnownSettings(extensionTarget);
748
+ configureTargetWithEntitlements(extensionTarget);
749
+ configureTargetWithPreview(extensionTarget);
750
+ extensionTarget.ensureFrameworks(props.frameworks);
751
+ extensionTarget.getSourcesBuildPhase();
752
+ extensionTarget.getResourcesBuildPhase();
753
+ configureJsExport(extensionTarget);
951
754
  const containerItemProxy = xcode_1.PBXContainerItemProxy.create(project, {
952
755
  containerPortal: project.rootObject,
953
756
  proxyType: 1,
954
- remoteGlobalIDString: widgetTarget.uuid,
757
+ remoteGlobalIDString: extensionTarget.uuid,
955
758
  remoteInfo: productName,
956
759
  });
957
760
  const targetDependency = xcode_1.PBXTargetDependency.create(project, {
958
- target: widgetTarget,
761
+ target: extensionTarget,
959
762
  targetProxy: containerItemProxy,
960
763
  });
961
764
  // Add the target dependency to the main app, should be only one.
@@ -974,105 +777,63 @@ async function applyXcodeChanges(config, project, props) {
974
777
  });
975
778
  if (copyFilesBuildPhase) {
976
779
  // Assume that this is the first run if there is no matching target that we identified from a previous run.
977
- copyFilesBuildPhase.props.files.push(alphaExtensionAppexBf);
780
+ copyFilesBuildPhase.props.files.push(appExtensionBuildFile);
978
781
  }
979
782
  else {
980
- const dstPath = { clip: "AppClips", watch: "Watch" }[props.type];
981
- if (dstPath) {
982
- mainAppTarget.createBuildPhase(xcode_1.PBXCopyFilesBuildPhase, {
983
- dstPath: "$(CONTENTS_FOLDER_PATH)/" + dstPath,
984
- dstSubfolderSpec: 16,
985
- buildActionMask: 2147483647,
986
- files: [alphaExtensionAppexBf],
987
- name: WELL_KNOWN_COPY_EXTENSIONS_NAME,
988
- runOnlyForDeploymentPostprocessing: 0,
989
- });
990
- }
991
- else {
992
- mainAppTarget.createBuildPhase(xcode_1.PBXCopyFilesBuildPhase, {
993
- dstSubfolderSpec: 13,
994
- buildActionMask: 2147483647,
995
- files: [alphaExtensionAppexBf],
996
- name: WELL_KNOWN_COPY_EXTENSIONS_NAME,
997
- runOnlyForDeploymentPostprocessing: 0,
998
- });
999
- }
783
+ mainAppTarget.createBuildPhase(xcode_1.PBXCopyFilesBuildPhase, {
784
+ files: [appExtensionBuildFile],
785
+ });
1000
786
  }
1001
- const mainSourcesBuildPhase = mainAppTarget.getBuildPhase(xcode_1.PBXSourcesBuildPhase);
1002
- // TODO: Idempotent
1003
- mainSourcesBuildPhase === null || mainSourcesBuildPhase === void 0 ? void 0 : mainSourcesBuildPhase.props.files.push(...intentBuildFiles[1]);
1004
- const protectedGroup = ensureProtectedGroup(project).createGroup({
1005
- // This is where it gets fancy
1006
- // TODO: The user should be able to know that this is safe to modify and won't be overwritten.
1007
- name: path_1.default.basename(props.cwd),
1008
- // Like `../alpha`
1009
- path: props.cwd,
1010
- sourceTree: "<group>",
1011
- children: [
1012
- ...swiftGroups,
1013
- ...intentFiles.sort((a, b) => a.getDisplayName().localeCompare(b.getDisplayName())),
1014
- ...assetFiles
1015
- .map((buildFile) => buildFile.props.fileRef)
1016
- .sort((a, b) => a.getDisplayName().localeCompare(b.getDisplayName())),
1017
- ...entitlementFiles
1018
- .map((buildFile) => buildFile.props.fileRef)
1019
- .sort((a, b) => a.getDisplayName().localeCompare(b.getDisplayName())),
1020
- // CD0706192A2EBE2F009C1192 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
1021
- xcode_1.PBXFileReference.create(project, {
1022
- path: "Info.plist",
1023
- sourceTree: "<group>",
1024
- }),
787
+ const syncException = xcode_1.PBXFileSystemSynchronizedBuildFileExceptionSet.create(project, {
788
+ target: extensionTarget,
789
+ membershipExceptions: [
790
+ // TODO: What other files belong here, why is this here?
791
+ "Info.plist",
792
+ // Exclude the config path
793
+ path_1.default.relative(magicCwd, props.configPath),
1025
794
  ],
1026
- // children = (
1027
- // CD0706102A2EBE2E009C1192 /* alphaBundle.swift */,
1028
- // CD0706122A2EBE2E009C1192 /* alphaLiveActivity.swift */,
1029
- // CD0706142A2EBE2E009C1192 /* index.swift */,
1030
- // CD0706162A2EBE2E009C1192 /* alpha.intentdefinition */,
1031
- // CD0706172A2EBE2F009C1192 /* Assets.xcassets */,
1032
- // CD0706192A2EBE2F009C1192 /* Info.plist */,
1033
- // );
1034
- // name = "expo:alpha";
1035
- // path = "../alpha";
1036
- // sourceTree = "<group>";
1037
795
  });
1038
- if (resAssets.length > 0) {
1039
- protectedGroup.createGroup({
1040
- name: "assets",
1041
- path: "assets",
1042
- sourceTree: "<group>",
1043
- // @ts-expect-error
1044
- children: resAssets
1045
- .map((buildFile) => buildFile.props.fileRef)
1046
- .sort((a, b) => a.getDisplayName().localeCompare(b.getDisplayName())),
1047
- });
796
+ const assetsDir = path_1.default.join(magicCwd, "assets");
797
+ // TODO: Maybe just limit this to Safari extensions?
798
+ const explicitFolders = !fs_1.default.existsSync(assetsDir)
799
+ ? []
800
+ : fs_1.default
801
+ .readdirSync(assetsDir)
802
+ .filter((file) => file !== ".DS_Store" &&
803
+ fs_1.default.statSync(path_1.default.join(assetsDir, file)).isDirectory())
804
+ .map((file) => path_1.default.join("assets", file));
805
+ const syncRootGroup = xcode_1.PBXFileSystemSynchronizedRootGroup.create(project, {
806
+ path: path_1.default.basename(props.cwd),
807
+ exceptions: [syncException],
808
+ explicitFileTypes: {},
809
+ explicitFolders: [
810
+ // Replaces the previous `lastKnownFileType: "folder",` system that's used in things like Safari extensions to include folders of assets.
811
+ // ex: `"Resources/_locales", "Resources/images"`
812
+ ...explicitFolders,
813
+ ],
814
+ sourceTree: "<group>",
815
+ });
816
+ if (!extensionTarget.props.fileSystemSynchronizedGroups) {
817
+ extensionTarget.props.fileSystemSynchronizedGroups = [];
1048
818
  }
819
+ extensionTarget.props.fileSystemSynchronizedGroups.push(syncRootGroup);
820
+ ensureProtectedGroup(project, path_1.default.dirname(props.cwd)).props.children.push(syncRootGroup);
1049
821
  applyDevelopmentTeamIdToTargets();
1050
822
  syncMarketingVersions();
1051
823
  return project;
1052
824
  }
1053
825
  const PROTECTED_GROUP_NAME = "expo:targets";
1054
- function ensureProtectedGroup(project) {
826
+ function ensureProtectedGroup(project, relativePath = "../targets") {
1055
827
  const hasProtectedGroup = project.rootObject.props.mainGroup
1056
828
  .getChildGroups()
1057
829
  .find((group) => group.getDisplayName() === PROTECTED_GROUP_NAME);
1058
830
  const protectedGroup = hasProtectedGroup !== null && hasProtectedGroup !== void 0 ? hasProtectedGroup : xcode_1.PBXGroup.create(project, {
1059
831
  name: PROTECTED_GROUP_NAME,
832
+ path: relativePath,
1060
833
  sourceTree: "<group>",
1061
834
  });
1062
835
  if (!hasProtectedGroup) {
1063
836
  project.rootObject.props.mainGroup.props.children.unshift(protectedGroup);
1064
- // let libIndex = project.rootObject.props.mainGroup
1065
- // .getChildGroups()
1066
- // .findIndex((group) => group.getDisplayName() === "Libraries");
1067
- // if (libIndex === -1) {
1068
- // libIndex = 0;
1069
- // }
1070
- // add above the group named "Libraries"
1071
- // project.rootObject.props.mainGroup.props.children.splice(
1072
- // libIndex,
1073
- // 0,
1074
- // protectedGroup
1075
- // );
1076
837
  }
1077
838
  return protectedGroup;
1078
839
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bacons/apple-targets",
3
- "version": "0.0.10",
3
+ "version": "0.1.0",
4
4
  "description": "Generate Apple Targets with Expo Prebuild",
5
5
  "main": "build/index.js",
6
6
  "files": [
@@ -30,7 +30,7 @@
30
30
  "dependencies": {
31
31
  "@react-native/normalize-colors": "^0.76.1",
32
32
  "glob": "^10.2.6",
33
- "@bacons/xcode": "^1.0.0-alpha.13",
33
+ "@bacons/xcode": "^1.0.0-alpha.21",
34
34
  "fs-extra": "^11.2.0"
35
35
  },
36
36
  "devDependencies": {