@xcodekit/xcode 0.2.0 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +94 -42
  2. package/index.d.ts +23 -0
  3. package/package.json +6 -6
package/README.md CHANGED
@@ -42,10 +42,18 @@ writeFileSync("project.pbxproj", output);
42
42
 
43
43
  Parse a `.pbxproj` string into a JSON-compatible object. Matches the output of `@bacons/xcode/json`'s `parse()`.
44
44
 
45
+ ```js
46
+ const project = parse(readFileSync("project.pbxproj", "utf8"));
47
+ ```
48
+
45
49
  #### `build(project: object): string`
46
50
 
47
51
  Serialize a JSON object back to `.pbxproj` format. Produces byte-identical output to `@bacons/xcode/json`'s `build()`.
48
52
 
53
+ ```js
54
+ writeFileSync("project.pbxproj", build(project));
55
+ ```
56
+
49
57
  #### `buildFromJSON(json: string): string`
50
58
 
51
59
  Same as `build()` but accepts `JSON.stringify(project)` directly. Faster because it avoids napi's recursive JS-to-Rust object marshalling.
@@ -64,32 +72,74 @@ const output = parseAndBuild(readFileSync("project.pbxproj", "utf8"));
64
72
 
65
73
  ### High-Level (XcodeProject)
66
74
 
67
- #### `XcodeProject.open(filePath: string): XcodeProject`
75
+ #### `XcodeProject.open(filePath)` / `XcodeProject.fromString(content)`
68
76
 
69
- Open and parse a `.pbxproj` file from disk.
77
+ Open from disk or parse from a string (e.g. content fetched over the network).
70
78
 
71
79
  ```js
72
80
  import { XcodeProject } from "@xcodekit/xcode";
73
81
 
82
+ // From disk
74
83
  const project = XcodeProject.open("ios/MyApp.xcodeproj/project.pbxproj");
75
84
 
85
+ // From string (no file on disk needed)
86
+ const project = XcodeProject.fromString(pbxprojContent);
87
+
76
88
  // Properties
77
89
  project.archiveVersion; // 1
78
- project.objectVersion; // 46
79
- project.filePath; // the path it was opened from
90
+ project.objectVersion; // 46
91
+ project.filePath; // path if opened from disk, null if fromString
92
+ project.mainGroupUuid; // root group UUID
80
93
 
81
94
  // Targets
82
95
  const targets = project.getNativeTargets(); // UUID[]
83
96
  const mainApp = project.findMainAppTarget("ios"); // UUID | null
97
+ project.getTargetName(mainApp); // "MyApp"
98
+ project.setTargetName(mainApp, "NewName");
84
99
 
85
100
  // Build settings
86
101
  project.getBuildSetting(targetUuid, "PRODUCT_BUNDLE_IDENTIFIER");
87
102
  project.setBuildSetting(targetUuid, "SWIFT_VERSION", "5.0");
88
103
  project.removeBuildSetting(targetUuid, "CODE_SIGN_IDENTITY");
89
104
 
105
+ // Files & groups
106
+ const fileUuid = project.addFile(project.mainGroupUuid, "Sources/App.swift");
107
+ const groupUuid = project.addGroup(project.mainGroupUuid, "Features");
108
+ const children = project.getGroupChildren(groupUuid);
109
+
110
+ // Build phases
111
+ const phase = project.ensureBuildPhase(targetUuid, "PBXSourcesBuildPhase");
112
+ project.addBuildFile(phase, fileUuid);
113
+
114
+ // Frameworks
115
+ project.addFramework(targetUuid, "SwiftUI");
116
+
117
+ // Create targets
118
+ const widgetTarget = project.createNativeTarget(
119
+ "MyWidget",
120
+ "com.apple.product-type.app-extension",
121
+ "com.example.mywidget",
122
+ );
123
+
124
+ // Embed extension into host app
125
+ project.addDependency(mainApp, widgetTarget);
126
+ project.embedExtension(mainApp, widgetTarget);
127
+
128
+ // Xcode 16+ file system sync groups
129
+ project.addFileSystemSyncGroup(widgetTarget, "MyWidget");
130
+
131
+ // Generic object access
132
+ project.getObjectProperty(uuid, "path");
133
+ project.setObjectProperty(uuid, "path", "new/path");
134
+ const proxies = project.findObjectsByIsa("PBXContainerItemProxy");
135
+
136
+ // Validation
137
+ const orphans = project.findOrphanedReferences();
138
+ // [{ referrerUuid, referrerIsa, property, orphanUuid }]
139
+
90
140
  // Serialize
91
141
  const pbxproj = project.toBuild(); // string
92
- const json = project.toJSON(); // object
142
+ const json = project.toJSON(); // object
93
143
 
94
144
  // Write back to disk
95
145
  project.save();
@@ -98,46 +148,48 @@ project.save();
98
148
  const uuid = project.getUniqueId("my-seed-string"); // 24-char hex
99
149
  ```
100
150
 
151
+ All `XcodeProject` methods operate in Rust — only primitive strings cross the JS/Rust boundary. This is the fastest path for project manipulation.
152
+
101
153
  ## Performance
102
154
 
103
155
  Benchmarked on Apple M4 Pro, Node.js v24. Median of 200 iterations.
104
156
 
105
157
  ### Parse
106
158
 
107
- | Fixture | Rust | TypeScript | Speedup |
108
- |---------|------|-----------|---------|
109
- | swift-protobuf (257 KB) | 3.7 ms | 43.5 ms | **11.6x** |
110
- | Cocoa-Application (166 KB) | 3.2 ms | 17.3 ms | **5.5x** |
111
- | AFNetworking (99 KB) | 1.8 ms | 6.6 ms | **3.8x** |
112
- | watch (48 KB) | 0.9 ms | 2.1 ms | **2.2x** |
113
- | project (19 KB) | 0.4 ms | 0.8 ms | **2.2x** |
159
+ | Fixture | Rust | TypeScript | Speedup |
160
+ | -------------------------- | ------ | ---------- | --------- |
161
+ | swift-protobuf (257 KB) | 3.7 ms | 43.5 ms | **11.6x** |
162
+ | Cocoa-Application (166 KB) | 3.2 ms | 17.3 ms | **5.5x** |
163
+ | AFNetworking (99 KB) | 1.8 ms | 6.6 ms | **3.8x** |
164
+ | watch (48 KB) | 0.9 ms | 2.1 ms | **2.2x** |
165
+ | project (19 KB) | 0.4 ms | 0.8 ms | **2.2x** |
114
166
 
115
167
  ### Round-Trip (parse + build)
116
168
 
117
- | Fixture | Rust | TypeScript | Speedup |
118
- |---------|------|-----------|---------|
119
- | swift-protobuf (257 KB) | 9.1 ms | 63.1 ms | **6.9x** |
120
- | Cocoa-Application (166 KB) | 8.0 ms | 22.2 ms | **2.8x** |
121
- | AFNetworking (99 KB) | 4.2 ms | 9.3 ms | **2.2x** |
122
- | watch (48 KB) | 2.1 ms | 2.7 ms | **1.3x** |
123
- | project (19 KB) | 0.8 ms | 1.0 ms | **1.2x** |
169
+ | Fixture | Rust | TypeScript | Speedup |
170
+ | -------------------------- | ------ | ---------- | -------- |
171
+ | swift-protobuf (257 KB) | 9.1 ms | 63.1 ms | **6.9x** |
172
+ | Cocoa-Application (166 KB) | 8.0 ms | 22.2 ms | **2.8x** |
173
+ | AFNetworking (99 KB) | 4.2 ms | 9.3 ms | **2.2x** |
174
+ | watch (48 KB) | 2.1 ms | 2.7 ms | **1.3x** |
175
+ | project (19 KB) | 0.8 ms | 1.0 ms | **1.2x** |
124
176
 
125
177
  ### parseAndBuild (zero marshalling)
126
178
 
127
- | Fixture | Rust | TypeScript | Speedup |
128
- |---------|------|-----------|---------|
129
- | swift-protobuf (257 KB) | 4.6 ms | 62.9 ms | **13.6x** |
130
- | Cocoa-Application (166 KB) | 3.8 ms | 22.0 ms | **5.8x** |
131
- | AFNetworking (99 KB) | 1.9 ms | 9.1 ms | **4.9x** |
132
- | watch (48 KB) | 0.9 ms | 2.6 ms | **2.8x** |
133
- | project (19 KB) | 0.4 ms | 1.0 ms | **2.7x** |
179
+ | Fixture | Rust | TypeScript | Speedup |
180
+ | -------------------------- | ------ | ---------- | --------- |
181
+ | swift-protobuf (257 KB) | 4.6 ms | 62.9 ms | **13.6x** |
182
+ | Cocoa-Application (166 KB) | 3.8 ms | 22.0 ms | **5.8x** |
183
+ | AFNetworking (99 KB) | 1.9 ms | 9.1 ms | **4.9x** |
184
+ | watch (48 KB) | 0.9 ms | 2.6 ms | **2.8x** |
185
+ | project (19 KB) | 0.4 ms | 1.0 ms | **2.7x** |
134
186
 
135
187
  ### Package Size
136
188
 
137
- | | @bacons/xcode | @xcodekit/xcode |
138
- |-|--------------|-------------------|
139
- | Unpacked | 1.1 MB | 559 KB |
140
- | Gzipped | ~400 KB | ~270 KB |
189
+ | | @bacons/xcode | @xcodekit/xcode |
190
+ | -------- | ------------- | --------------- |
191
+ | Unpacked | 1.1 MB | 559 KB |
192
+ | Gzipped | ~400 KB | ~270 KB |
141
193
 
142
194
  Run benchmarks yourself:
143
195
 
@@ -149,13 +201,13 @@ make bench-js # Rust vs TypeScript head-to-head
149
201
 
150
202
  ## Choosing the Right Function
151
203
 
152
- | Use case | Function | Notes |
153
- |----------|----------|-------|
154
- | Parse only | `parse(text)` | 2-12x faster than TS |
155
- | Build from JS object | `build(obj)` | Fastest on large files (>100 KB) |
156
- | Build from JSON string | `buildFromJSON(json)` | Faster than `build()` on all sizes |
157
- | Full round-trip | `parseAndBuild(text)` | Fastest path, zero JS/Rust overhead |
158
- | Project manipulation | `XcodeProject.open()` | Stays in Rust, use `.toBuild()` to serialize |
204
+ | Use case | Function | Notes |
205
+ | ---------------------- | --------------------- | -------------------------------------------- |
206
+ | Parse only | `parse(text)` | 2-12x faster than TS |
207
+ | Build from JS object | `build(obj)` | Fastest on large files (>100 KB) |
208
+ | Build from JSON string | `buildFromJSON(json)` | Faster than `build()` on all sizes |
209
+ | Full round-trip | `parseAndBuild(text)` | Fastest path, zero JS/Rust overhead |
210
+ | Project manipulation | `XcodeProject.open()` | Stays in Rust, use `.toBuild()` to serialize |
159
211
 
160
212
  ## Compatibility
161
213
 
@@ -168,11 +220,11 @@ make bench-js # Rust vs TypeScript head-to-head
168
220
 
169
221
  ## Supported Platforms
170
222
 
171
- | Platform | Architecture |
172
- |----------|-------------|
173
- | macOS | arm64 (Apple Silicon), x64 (Intel) |
174
- | Linux | x64 (glibc), arm64 (glibc) |
175
- | Windows | x64 (MSVC) |
223
+ | Platform | Architecture |
224
+ | -------- | ---------------------------------- |
225
+ | macOS | arm64 (Apple Silicon), x64 (Intel) |
226
+ | Linux | x64 (glibc), arm64 (glibc) |
227
+ | Windows | x64 (MSVC) |
176
228
 
177
229
  ## Development
178
230
 
package/index.d.ts CHANGED
@@ -22,6 +22,8 @@ export declare function parseAndBuild(text: string): string
22
22
  export declare class XcodeProject {
23
23
  /** Open and parse a .pbxproj file from disk. */
24
24
  static open(filePath: string): XcodeProject
25
+ /** Parse a .pbxproj string into an XcodeProject (no file on disk needed). */
26
+ static fromString(content: string): XcodeProject
25
27
  /** Convert the project to a JSON-compatible object. */
26
28
  toJSON(): any
27
29
  /** Serialize the project back to .pbxproj format. */
@@ -90,4 +92,25 @@ export declare class XcodeProject {
90
92
  * Returns the UUID of the PBXTargetDependency.
91
93
  */
92
94
  addDependency(targetUuid: string, dependsOnUuid: string): string | null
95
+ /**
96
+ * Embed an extension target into a host app target.
97
+ * Creates PBXCopyFilesBuildPhase with correct dstSubfolderSpec.
98
+ * Returns the UUID of the copy files build phase.
99
+ */
100
+ embedExtension(hostTargetUuid: string, extensionTargetUuid: string): string | null
101
+ /**
102
+ * Add a PBXFileSystemSynchronizedRootGroup to a target (Xcode 16+).
103
+ * Returns the UUID of the sync group.
104
+ */
105
+ addFileSystemSyncGroup(targetUuid: string, path: string): string | null
106
+ /** Get a string property from any object. */
107
+ getObjectProperty(uuid: string, key: string): string | null
108
+ /** Set a string property on any object. */
109
+ setObjectProperty(uuid: string, key: string, value: string): boolean
110
+ /** Find all object UUIDs matching a given ISA type. */
111
+ findObjectsByIsa(isa: string): Array<string>
112
+ /** Get the name of a target. */
113
+ getTargetName(targetUuid: string): string | null
114
+ /** Set the name and productName of a target. */
115
+ setTargetName(targetUuid: string, name: string): boolean
93
116
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcodekit/xcode",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Parse, manipulate, and serialize Xcode .pbxproj files — native Rust rewrite",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -16,11 +16,11 @@
16
16
  },
17
17
  "license": "MIT",
18
18
  "optionalDependencies": {
19
- "@xcodekit/xcode-darwin-arm64": "0.2.0",
20
- "@xcodekit/xcode-darwin-x64": "0.2.0",
21
- "@xcodekit/xcode-linux-arm64-gnu": "0.2.0",
22
- "@xcodekit/xcode-linux-x64-gnu": "0.2.0",
23
- "@xcodekit/xcode-win32-x64-msvc": "0.2.0"
19
+ "@xcodekit/xcode-darwin-arm64": "0.3.0",
20
+ "@xcodekit/xcode-darwin-x64": "0.3.0",
21
+ "@xcodekit/xcode-linux-arm64-gnu": "0.3.0",
22
+ "@xcodekit/xcode-linux-x64-gnu": "0.3.0",
23
+ "@xcodekit/xcode-win32-x64-msvc": "0.3.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@napi-rs/cli": "^2.18.0",