rork-xcode 0.9.0 → 0.10.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
@@ -206,6 +206,17 @@ if (widget) project.removeTarget(widget);
206
206
  project.referrersOf(app.id); // every object referencing an id, for custom teardowns
207
207
  ```
208
208
 
209
+ ### Renaming
210
+
211
+ `renameTarget` renames a target and every place the document knows it by name. That covers the target's `name` and `productName`, its product file reference, the `remoteInfo` of container proxies pointing at it, `TEST_TARGET_NAME` values naming it, and the path segments of `TEST_HOST` and `BUNDLE_LOADER` that name the target or its product. Names match whole, so renaming `DemoApp` leaves `DemoAppTests` alone, and a `PRODUCT_NAME` of `$(TARGET_NAME)` follows by itself. On-disk renames (source folders, entitlements files) and the group paths pointing at those folders stay with the caller.
212
+
213
+ ```ts
214
+ const app = project.findMainAppTarget("ios");
215
+ if (app) project.renameTarget(app, "RenamedApp");
216
+ ```
217
+
218
+ Scheme files live next to the project rather than inside it, so the scheme model carries the counterpart (see [Schemes](#schemes)).
219
+
209
220
  ### Escape hatch
210
221
 
211
222
  Every view exposes its raw dictionary, so anything the typed surface does not cover stays one property away, and `project.objects()` iterates every object with its typed view:
@@ -235,25 +246,21 @@ for (const [id, object] of project.objects()) {
235
246
 
236
247
  `.xcscheme` files describe how Xcode builds, runs, tests, and archives a target. They are not property lists but a small XML dialect of their own, and the scheme module covers it with the same contract as the pbxproj functions. An Xcode-written scheme rebuilds byte for byte, any other input reaches Xcode's canonical layout in one build, and malformed input fails with a typed error carrying line and column.
237
248
 
238
- `Xcscheme` is the model. Buildable references are the elements editing flows touch, and they come back as typed views:
249
+ `Xcscheme` is the model. Renames are one call per move. `renameTarget` is the scheme-file side of the project model's `renameTarget` and touches only the named target's references, and `renameContainer` follows a rename of the `.xcodeproj` directory itself. Both return whether anything changed, so callers can skip rewriting untouched files:
239
250
 
240
251
  ```ts
241
252
  import { Xcscheme } from "rork-xcode";
242
253
 
243
254
  const scheme = Xcscheme.parse(xcschemeText);
244
255
 
245
- // Rename the app while keeping each product's own shape, so a testable
246
- // like DemoAppTests.xctest stays a test bundle.
247
- for (const reference of scheme.buildableReferences()) {
248
- const { blueprintName, buildableName } = reference;
249
- if (blueprintName) reference.blueprintName = blueprintName.replace("DemoApp", "RenamedApp");
250
- if (buildableName) reference.buildableName = buildableName.replace("DemoApp", "RenamedApp");
251
- reference.referencedContainer = "container:RenamedApp.xcodeproj";
252
- }
256
+ scheme.renameTarget("DemoApp", "RenamedApp"); // DemoAppTests stays untouched
257
+ scheme.renameContainer("DemoApp", "RenamedApp"); // container:RenamedApp.xcodeproj
253
258
 
254
259
  const text = scheme.build();
255
260
  ```
256
261
 
262
+ For anything else, buildable references come back as typed views through `scheme.buildableReferences()`, with property access to the blueprint name, buildable name, container, and identifier.
263
+
257
264
  `Xcscheme.create` produces the scheme Xcode's own "New Scheme" action writes for an application target, wired to the target's object id from the project document:
258
265
 
259
266
  ```ts
@@ -268,7 +275,7 @@ Underneath, the document is a plain tree of elements with ordered attributes and
268
275
 
269
276
  ## Xcconfig files
270
277
 
271
- Build settings do not only live in the pbxproj. Projects push them into `.xcconfig` files referenced through `baseConfigurationReference`, and the xcconfig module reads and writes that format with the fidelity the rest of the library promises. The format is hand-authored with no canonical layout, so the contract here is losslessness: parsing and building an untouched file reproduces it byte for byte comments, blank lines, column alignment, and line endings included. Malformed lines fail loudly with a typed error carrying line and column rather than being dropped, so a file the parser accepts is a file it fully understood.
278
+ Build settings do not only live in the pbxproj. Projects push them into `.xcconfig` files referenced through `baseConfigurationReference`, and the xcconfig module reads and writes that format with the fidelity the rest of the library promises. The format is hand-authored with no canonical layout, so the contract here is losslessness. Parsing and building an untouched file reproduces it byte for byte, including comments, blank lines, column alignment, and line endings. Malformed lines fail loudly with a typed error carrying line and column rather than being dropped, so a file the parser accepts is a file it fully understood.
272
279
 
273
280
  `Xcconfig` is the model. Reads follow the file top to bottom the way Xcode does, and writes edit single lines while leaving every other byte alone:
274
281
 
@@ -300,7 +307,7 @@ const settings = config.settings({
300
307
  });
301
308
  ```
302
309
 
303
- Registering a file on the project makes `getBuildSetting` resolve through it in Xcode's order target settings, the target's xcconfig, project settings, the project's xcconfig:
310
+ Registering a file on the project makes `getBuildSetting` resolve through it in Xcode's order of target settings, then the target's xcconfig, then project settings, then the project's xcconfig:
304
311
 
305
312
  ```ts
306
313
  project.registerXcconfig(reference, Xcconfig.parse(text));
@@ -336,7 +343,7 @@ Measured on an Apple M5 Max, Node.js 24, single thread, with `@bacons/xcode` 1.0
336
343
  ## Verification
337
344
 
338
345
  - The committed fixture corpus spans project generations from Xcode 3 to Xcode 16, captured from real projects with identifiers neutralized: synchronized folders with both exception-set kinds, classic groups, variant groups, aggregate and legacy targets, reference proxies, build rules, Swift packages, and a ~100 KiB multiplatform framework project.
339
- - Documents already in current Xcode's layout must round-trip byte for byte; documents from other tool generations must normalize to a byte-stable fixed point with unchanged values.
346
+ - Documents already in current Xcode's layout must round-trip byte for byte, and documents from other tool generations must normalize to a byte-stable fixed point with unchanged values.
340
347
  - On macOS, the suite cross-validates every fixture and its rebuilt form with `plutil`, Apple's own property list parser and the empirical ground truth for what Apple tooling accepts.
341
348
  - A corpus sweep (`pnpm corpus`) walks every Xcode project, scheme, and xcconfig on the machine, verifies each one parses and reaches a byte-stable fixed point (byte-exact losslessness for xcconfig, which has no canonical layout), exercises the object model against every project, and cross-validates a sample against plutil's own reading.
342
349
  - CI runs the full gate on Linux and macOS, and executes the built artifact on the oldest supported Node to enforce the `engines` floor.
package/dist/index.d.ts CHANGED
@@ -10,33 +10,34 @@
10
10
  * - `( item, item, ... )` parses to a {@link PbxprojArray}.
11
11
  * - `<48656c6c6f>` data runs parse to `Uint8Array`.
12
12
  * - Unquoted integers and decimals (`46`, `3.14`, `-12`) parse to `number`
13
- * under one print-back rule: the literal converts exactly when the number
14
- * formats back to the identical text.
13
+ * under one print-back rule, where the literal converts exactly when the
14
+ * number formats back to the identical text.
15
15
  * - Everything else (quoted text, identifiers, uuids, paths) parses to
16
16
  * `string`.
17
17
  *
18
- * The print-back rule is what keeps round-trips faithful: any literal the
18
+ * The print-back rule is what keeps round-trips faithful. Any literal the
19
19
  * conversion would reshape stays a string, so serializing never changes a
20
20
  * scalar's bytes. Leading-zero runs like `0755` would corrupt file modes,
21
- * trailing-zero decimals like `5.0` would drop the zero build settings are
22
- * written with, bare-dot decimals like `.5` would grow a leading zero, and
21
+ * trailing-zero decimals like `5.0` would drop the zero that build
22
+ * settings are written with, bare-dot decimals like `.5` would grow a
23
+ * leading zero, and
23
24
  * digit runs beyond `Number.MAX_SAFE_INTEGER` would lose precision (a
24
- * 24-character identifier can be all digits); all of these therefore parse
25
- * as strings.
25
+ * 24-character identifier can be all digits), so all of these parse as
26
+ * strings.
26
27
  *
27
28
  * @module
28
29
  */
29
30
  /**
30
31
  * A value representable in a `project.pbxproj` document.
31
32
  *
32
- * Notably absent are booleans and null: the format has no notation for
33
+ * Notably absent are booleans and null. The format has no notation for
33
34
  * either, and Xcode models flags as the strings `"YES"` and `"NO"`.
34
35
  */
35
36
  type PbxprojValue = string | number | Uint8Array | PbxprojArray | PbxprojObject;
36
37
  /**
37
38
  * A `( ... )` list: an ordered array of values.
38
39
  *
39
- * This is a plain JavaScript array; the interface exists only to give the
40
+ * This is a plain JavaScript array. The interface exists only to give the
40
41
  * recursive {@link PbxprojValue} type a name.
41
42
  */
42
43
  interface PbxprojArray extends Array<PbxprojValue> {}
@@ -55,18 +56,18 @@ interface PbxprojObject {
55
56
  /**
56
57
  * Serializes a project document to `project.pbxproj` text.
57
58
  *
58
- * The input is the same shape {@link parsePbxproj} produces; see the module
59
- * documentation of `types.ts` for the value model. Output is stable: two
59
+ * The input is the same shape {@link parsePbxproj} produces. See the module
60
+ * documentation of `types.ts` for the value model. Output is stable, so two
60
61
  * calls with semantically equal documents produce identical text, and the
61
62
  * layout matches what Xcode itself writes so diffs stay minimal.
62
63
  *
63
64
  * @param root The document root. Real project documents carry `objects`,
64
65
  * `rootObject`, and the version fields, but any dictionary serializes.
65
66
  * @returns The document text, terminated by a newline.
66
- * @throws PbxprojBuildError when a value has no pbxproj representation:
67
- * `null`, `undefined`, booleans, bigints, functions, symbols, class
68
- * instances, or non-finite numbers. The error names the path of the
69
- * offending value.
67
+ * @throws PbxprojBuildError when a value has no pbxproj representation,
68
+ * meaning `null`, `undefined`, booleans, bigints, functions, symbols,
69
+ * class instances, or non-finite numbers. The error names the path of
70
+ * the offending value.
70
71
  */
71
72
  declare function buildPbxproj(root: PbxprojObject): string;
72
73
  //#endregion
@@ -107,7 +108,7 @@ declare class PbxprojParseError extends Error {
107
108
  /** Where in the source text parsing failed. */
108
109
  readonly position: PbxprojErrorPosition;
109
110
  /**
110
- * @param message Failure description without location; the location is
111
+ * @param message Failure description without location. The location is
111
112
  * appended automatically.
112
113
  * @param source Full source text, used to compute the position.
113
114
  * @param offset Character offset of the failure inside `source`.
@@ -126,7 +127,7 @@ declare class XcschemeParseError extends Error {
126
127
  /** Where in the source text parsing failed. */
127
128
  readonly position: PbxprojErrorPosition;
128
129
  /**
129
- * @param message Failure description without location; the location is
130
+ * @param message Failure description without location. The location is
130
131
  * appended automatically.
131
132
  * @param source Full source text, used to compute the position.
132
133
  * @param offset Character offset of the failure inside `source`.
@@ -145,7 +146,7 @@ declare class XcconfigParseError extends Error {
145
146
  /** Where in the source text parsing failed. */
146
147
  readonly position: PbxprojErrorPosition;
147
148
  /**
148
- * @param message Failure description without location; the location is
149
+ * @param message Failure description without location. The location is
149
150
  * appended automatically.
150
151
  * @param source Full source text, used to compute the position.
151
152
  * @param offset Character offset of the failure inside `source`.
@@ -163,14 +164,14 @@ declare class XcschemeBuildError extends Error {
163
164
  /** Path to the offending element from the root, e.g. `Scheme.BuildAction[0]`. */
164
165
  readonly path: string;
165
166
  /**
166
- * @param message Failure description without location; the element path
167
+ * @param message Failure description without location. The element path
167
168
  * is appended automatically.
168
169
  * @param path Path to the offending element from the root.
169
170
  */
170
171
  constructor(message: string, path: string);
171
172
  }
172
173
  /**
173
- * Thrown by the object model when an operation cannot proceed: the
174
+ * Thrown by the object model when an operation cannot proceed, because the
174
175
  * document lacks the structure the operation needs (no objects dictionary,
175
176
  * no root project object), a view's object was removed from the document,
176
177
  * or a creation request names a product type the model cannot scaffold.
@@ -199,7 +200,7 @@ declare class PbxprojBuildError extends Error {
199
200
  /** Path to the offending value from the root, e.g. `$.objects.13B07F86.name`. */
200
201
  readonly path: string;
201
202
  /**
202
- * @param message Failure description without location; the value path is
203
+ * @param message Failure description without location. The value path is
203
204
  * appended automatically.
204
205
  * @param path Path to the offending value from the root, `$`.
205
206
  */
@@ -210,15 +211,15 @@ declare class PbxprojBuildError extends Error {
210
211
  /**
211
212
  * Names and well-known values of the pbxproj object vocabulary.
212
213
  *
213
- * Everything here mirrors strings Xcode itself writes; nothing is invented.
214
- * Centralizing them keeps the object model free of string literals and
215
- * gives call sites one place to import from.
214
+ * Everything here mirrors strings Xcode itself writes, and nothing is
215
+ * invented. Centralizing them keeps the object model free of string
216
+ * literals and gives call sites one place to import from.
216
217
  *
217
218
  * @module
218
219
  */
219
220
  /**
220
221
  * The `isa` names the object model works with. The parser and serializer
221
- * accept any isa; this list only covers the kinds the model creates or
222
+ * accept any isa, and this list only covers the kinds the model creates or
222
223
  * gives typed access to.
223
224
  */
224
225
  declare const Isa: {
@@ -629,11 +630,11 @@ declare class Target<Properties extends TargetProperties = TargetProperties> ext
629
630
  *
630
631
  * Configurations based on `.xcconfig` files take part once the files
631
632
  * are registered through {@link XcodeProject.registerXcconfig}, in
632
- * Xcode's order: target settings, the target's xcconfig, project
633
- * settings, the project's xcconfig.
633
+ * Xcode's order of target settings, then the target's xcconfig, then
634
+ * project settings, then the project's xcconfig.
634
635
  *
635
- * Only string values are returned; a list- or number-valued setting reads
636
- * as `undefined`.
636
+ * Only string values are returned, so a list- or number-valued setting
637
+ * reads as `undefined`.
637
638
  */
638
639
  getBuildSetting(key: string): string | undefined;
639
640
  /**
@@ -879,7 +880,7 @@ declare class Group<Properties extends GroupProperties = GroupProperties> extend
879
880
  * it to the group's children.
880
881
  *
881
882
  * The reference's `lastKnownFileType` derives from the file extension
882
- * when it is a known kind; otherwise the reference carries no type and
883
+ * when it is a known kind. Otherwise the reference carries no type and
883
884
  * Xcode re-derives one on open.
884
885
  *
885
886
  * @param path File path relative to the group, for example
@@ -905,8 +906,8 @@ declare class VariantGroup extends Group {
905
906
  declare class BuildPhase<Properties extends BuildPhaseProperties = BuildPhaseProperties> extends XcodeObject<Properties> {
906
907
  /**
907
908
  * The phase's display name, when it carries an explicit one. Xcode names
908
- * copy-files and shell-script phases; the standard phases derive their
909
- * names from their isa.
909
+ * copy-files and shell-script phases, while the standard phases derive
910
+ * their names from their isa.
910
911
  */
911
912
  get name(): string | undefined;
912
913
  /**
@@ -934,7 +935,7 @@ declare class BuildPhase<Properties extends BuildPhaseProperties = BuildPhasePro
934
935
  * @param reference The file reference or package product the build file
935
936
  * should point at.
936
937
  * @param options.referenceKey Which build-file field carries the
937
- * reference: `fileRef` for file references (the default) or
938
+ * reference, either `fileRef` for file references (the default) or
938
939
  * `productRef` for Swift package products.
939
940
  * @param options.settings Optional per-file settings, for example
940
941
  * `{ ATTRIBUTES: ["RemoveHeadersOnCopy"] }`.
@@ -1029,7 +1030,7 @@ declare class BuildFile extends XcodeObject<BuildFileProperties> {
1029
1030
  /**
1030
1031
  * The view of the file reference the build file points at, when it
1031
1032
  * points at one. Build files for Swift package products carry a
1032
- * `productRef` instead; see {@link productDependency}.
1033
+ * `productRef` instead, resolved through {@link productDependency}.
1033
1034
  */
1034
1035
  fileReference(): XcodeObject | undefined;
1035
1036
  /**
@@ -1055,12 +1056,12 @@ declare class SyncRootGroup extends XcodeObject<SyncRootGroupProperties> {
1055
1056
  *
1056
1057
  * Xcode keeps one exception set per target and folder, so when this
1057
1058
  * group already carries a set for the target, the file names merge into
1058
- * it instead of creating a second set; names already excluded are not
1059
- * duplicated.
1059
+ * it instead of creating a second set, and names already excluded are
1060
+ * not duplicated.
1060
1061
  *
1061
1062
  * The standard use is keeping a scaffolded `Info.plist` from being
1062
- * double-copied: the build already processes it through the target's
1063
- * `INFOPLIST_FILE` setting.
1063
+ * double-copied, since the build already processes it through the
1064
+ * target's `INFOPLIST_FILE` setting.
1064
1065
  *
1065
1066
  * @param target The target whose membership the exceptions restrict.
1066
1067
  * @param membershipExceptions File names inside the folder to exclude.
@@ -1540,7 +1541,7 @@ declare class RootProject extends XcodeObject<RootProjectProperties> {
1540
1541
  mainGroup(): Group | undefined;
1541
1542
  /**
1542
1543
  * The settings dictionary of the project-level default configuration.
1543
- * Targets inherit from these settings; see
1544
+ * Targets inherit from these settings, as described on
1544
1545
  * {@link NativeTarget.getBuildSetting}.
1545
1546
  */
1546
1547
  defaultConfigurationSettings(): PbxprojObject | undefined;
@@ -1619,7 +1620,7 @@ declare class XcodeProject {
1619
1620
  static parse(text: string): XcodeProject;
1620
1621
  /**
1621
1622
  * Wraps an already parsed document in a model. The document is used in
1622
- * place, not copied; model mutations write into it.
1623
+ * place rather than copied, so model mutations write into it.
1623
1624
  */
1624
1625
  static fromDocument(document: PbxprojObject): XcodeProject;
1625
1626
  /**
@@ -1630,7 +1631,7 @@ declare class XcodeProject {
1630
1631
  /**
1631
1632
  * The raw properties dictionary of an object.
1632
1633
  *
1633
- * @throws XcodeModelError when no object with the id exists; views use
1634
+ * @throws XcodeModelError when no object with the id exists. Views use
1634
1635
  * this accessor, so a view of a deleted object fails loudly instead of
1635
1636
  * resurrecting the entry.
1636
1637
  */
@@ -1653,8 +1654,8 @@ declare class XcodeProject {
1653
1654
  objects(): IterableIterator<[string, XcodeObject]>;
1654
1655
  /**
1655
1656
  * Generates a deterministic 24-character id from a seed, avoiding every
1656
- * id the document currently contains. The id is not reserved; adding an
1657
- * object with it (see {@link add}) is what claims it.
1657
+ * id the document currently contains. The id is not reserved, and only
1658
+ * adding an object with it (see {@link add}) claims it.
1658
1659
  */
1659
1660
  generateId(seed: string): string;
1660
1661
  /**
@@ -1723,7 +1724,8 @@ declare class XcodeProject {
1723
1724
  * and Resources build phases, and registers it on the project.
1724
1725
  *
1725
1726
  * @throws XcodeModelError for product types the model cannot create a
1726
- * product reference for; see {@link AddNativeTargetOptions.productType}.
1727
+ * product reference for, listed on
1728
+ * {@link AddNativeTargetOptions.productType}.
1727
1729
  */
1728
1730
  addNativeTarget(options: AddNativeTargetOptions): NativeTarget;
1729
1731
  /**
@@ -1733,7 +1735,7 @@ declare class XcodeProject {
1733
1735
  /**
1734
1736
  * Adds a remote Swift package reference and registers it on the project.
1735
1737
  * When the project already references the repository, the existing
1736
- * reference is returned unchanged, requirement included; adjust an
1738
+ * reference is returned unchanged, requirement included. Adjust an
1737
1739
  * existing requirement through the reference's properties. Link products
1738
1740
  * to targets with {@link NativeTarget.addSwiftPackageProduct}.
1739
1741
  *
@@ -1786,7 +1788,7 @@ declare class XcodeProject {
1786
1788
  * list containing it, or a nested dictionary carrying it as a key or
1787
1789
  * string value.
1788
1790
  *
1789
- * The scan is linear over the document; removal flows call it once per
1791
+ * The scan is linear over the document. Removal flows call it once per
1790
1792
  * removed object, which keeps teardown proportional to what is actually
1791
1793
  * removed.
1792
1794
  */
@@ -1798,7 +1800,7 @@ declare class XcodeProject {
1798
1800
  * (such as `TargetAttributes`) drop its entry.
1799
1801
  *
1800
1802
  * Removing an id the document does not contain is a no-op. This is the
1801
- * low-level removal; {@link removeTarget} composes it into a full
1803
+ * low-level removal, and {@link removeTarget} composes it into a full
1802
1804
  * teardown.
1803
1805
  */
1804
1806
  removeObject(id: string): void;
@@ -1811,7 +1813,7 @@ declare class XcodeProject {
1811
1813
  * dependencies on others), its membership exception sets, and
1812
1814
  * synchronized folders no remaining target links.
1813
1815
  *
1814
- * On-disk sources are untouched; the removal is document-only, like
1816
+ * On-disk sources are untouched. The removal is document-only, like
1815
1817
  * deleting a target in Xcode and keeping its folder.
1816
1818
  *
1817
1819
  * @throws XcodeModelError when the target belongs to another project,
@@ -1819,13 +1821,33 @@ declare class XcodeProject {
1819
1821
  * objects that happen to share them.
1820
1822
  */
1821
1823
  removeTarget(target: Target): void;
1824
+ /**
1825
+ * Renames a target and every place the document knows it by name. That
1826
+ * covers the target's `name` and `productName`, its product file
1827
+ * reference (`OldName.app` becomes `NewName.app`, whatever the
1828
+ * extension), the `remoteInfo` of container item proxies pointing at
1829
+ * the target, `TEST_TARGET_NAME` settings naming it, and the path
1830
+ * segments of `TEST_HOST` and `BUNDLE_LOADER` settings that name the
1831
+ * target or its product. A `PRODUCT_NAME` of the target's own
1832
+ * configurations is rewritten only when it spells the old name
1833
+ * literally. The usual `$(TARGET_NAME)` follows by itself.
1834
+ *
1835
+ * Scheme files live outside the pbxproj, so buildable references are
1836
+ * renamed separately through `Xcscheme.renameTarget`. On-disk renames
1837
+ * (source folders, entitlements files) and the group paths pointing at
1838
+ * those folders stay with the caller. Sibling targets such as
1839
+ * `OldNameTests` are renamed with their own calls.
1840
+ *
1841
+ * @throws XcodeModelError when the target belongs to another project.
1842
+ */
1843
+ renameTarget(target: Target, newName: string): void;
1822
1844
  /**
1823
1845
  * Finds a file reference by its project-relative path, resolving each
1824
1846
  * reference's location through the group tree from the main group
1825
1847
  * (nested group `path` components join with `/`).
1826
1848
  *
1827
1849
  * Members of file-system-synchronized folders are not listed in the
1828
- * document and therefore cannot be found; check
1850
+ * document and therefore cannot be found. Check
1829
1851
  * {@link NativeTarget.syncGroupPaths} for folder-level containment
1830
1852
  * instead.
1831
1853
  */
@@ -1987,8 +2009,8 @@ declare class XcodeObject<Properties extends PbxprojObject = PbxprojObject> {
1987
2009
  * nested dictionaries keyed by object id (such as the root project's
1988
2010
  * `TargetAttributes`) drop its entry.
1989
2011
  *
1990
- * This is the low-level removal; it does not cascade to objects that only
1991
- * made sense alongside this one. Higher-level operations like
2012
+ * This is the low-level removal, so it does not cascade to objects that
2013
+ * only made sense alongside this one. Higher-level operations like
1992
2014
  * {@link XcodeProject.removeTarget} compose it into full teardowns.
1993
2015
  */
1994
2016
  removeFromProject(): void;
@@ -2006,7 +2028,7 @@ declare class XcodeObject<Properties extends PbxprojObject = PbxprojObject> {
2006
2028
  * @param text Source text of the document.
2007
2029
  * @returns The document's root value. For real project files this is the
2008
2030
  * root dictionary with `objects`, `rootObject`, and version fields.
2009
- * @throws PbxprojParseError when the document is malformed; the error
2031
+ * @throws PbxprojParseError when the document is malformed. The error
2010
2032
  * carries the line and column of the failure.
2011
2033
  */
2012
2034
  declare function parsePbxproj(text: string): PbxprojValue;
@@ -2178,6 +2200,22 @@ declare class Xcscheme {
2178
2200
  * of these, so rename flows iterate this list.
2179
2201
  */
2180
2202
  buildableReferences(): BuildableReference[];
2203
+ /**
2204
+ * Renames every buildable reference pointing at a target. The blueprint
2205
+ * name is matched whole, and the buildable name is matched by its stem,
2206
+ * so `OldApp.app` becomes `NewApp.app` while `OldAppTests.xctest`, a
2207
+ * different target's product, stays untouched. This is the scheme-file
2208
+ * side of `XcodeProject.renameTarget`. Returns whether anything
2209
+ * changed, so callers can skip rewriting untouched files.
2210
+ */
2211
+ renameTarget(oldName: string, newName: string): boolean;
2212
+ /**
2213
+ * Rewrites every buildable reference's container after the
2214
+ * `.xcodeproj` directory itself is renamed, so `container:Old.xcodeproj`
2215
+ * becomes `container:New.xcodeproj`. The project names are matched
2216
+ * exactly. Returns whether anything changed.
2217
+ */
2218
+ renameContainer(oldProjectName: string, newProjectName: string): boolean;
2181
2219
  }
2182
2220
  /**
2183
2221
  * Options for {@link createXcscheme}.
@@ -2220,7 +2258,7 @@ declare function parseXcscheme(text: string): XcschemeDocument;
2220
2258
  * Deterministic object identifiers for generated pbxproj objects.
2221
2259
  *
2222
2260
  * Xcode identifies every object with 24 hexadecimal characters. Generated
2223
- * ids here are deterministic: the same seed always produces the same id, so
2261
+ * ids here are deterministic. The same seed always produces the same id, so
2224
2262
  * programmatic edits are reproducible and diffs stay minimal across runs.
2225
2263
  * The format is `XX` + the first 20 characters of `md5(seed)` + `XX`, which
2226
2264
  * is a valid identifier that remains recognizable as generated.
package/dist/index.js CHANGED
@@ -52,8 +52,8 @@ function repoNameFromUrl(repoUrl) {
52
52
  * Builds the uuid-to-comment map for a parsed project document.
53
53
  *
54
54
  * Objects that should render without a comment (unnamed groups) map to the
55
- * empty string; uuids absent from the map are not references at all.
56
- * Derivation is linear over the object graph: reverse indexes are built in
55
+ * empty string, and uuids absent from the map are not references at all.
56
+ * Derivation is linear over the object graph. Reverse indexes are built in
57
57
  * one pass, and every object's comment is computed once and cached.
58
58
  */
59
59
  function createReferenceComments(root) {
@@ -100,9 +100,9 @@ function createReferenceComments(root) {
100
100
  };
101
101
  /**
102
102
  * Comment for a `PBXFileSystemSynchronizedBuildFileExceptionSet`,
103
- * matching current Xcode: `Exceptions for "clip" folder in "clip"
104
- * target`. Falls back to the isa when the folder or target cannot be
105
- * resolved (older documents and hand-edited graphs).
103
+ * matching current Xcode, for example `Exceptions for "clip" folder in
104
+ * "clip" target`. Falls back to the isa when the folder or target cannot
105
+ * be resolved (older documents and hand-edited graphs).
106
106
  */
107
107
  const buildFileExceptionSetComment = (id, set) => {
108
108
  const folder = exceptionSetFolderName(id);
@@ -114,9 +114,9 @@ function createReferenceComments(root) {
114
114
  };
115
115
  /**
116
116
  * Comment for a `PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet`,
117
- * matching current Xcode: `Exceptions for "Tophat" folder in "CopyFiles"
118
- * phase from "Tophat" target`. Falls back to the isa when any of the
119
- * three names cannot be resolved.
117
+ * matching current Xcode, for example `Exceptions for "Tophat" folder in
118
+ * "CopyFiles" phase from "Tophat" target`. Falls back to the isa when
119
+ * any of the three names cannot be resolved.
120
120
  */
121
121
  const membershipExceptionSetComment = (id, set) => {
122
122
  const folder = exceptionSetFolderName(id);
@@ -246,7 +246,7 @@ var PbxprojParseError = class extends Error {
246
246
  /** Where in the source text parsing failed. */
247
247
  position;
248
248
  /**
249
- * @param message Failure description without location; the location is
249
+ * @param message Failure description without location. The location is
250
250
  * appended automatically.
251
251
  * @param source Full source text, used to compute the position.
252
252
  * @param offset Character offset of the failure inside `source`.
@@ -270,7 +270,7 @@ var XcschemeParseError = class extends Error {
270
270
  /** Where in the source text parsing failed. */
271
271
  position;
272
272
  /**
273
- * @param message Failure description without location; the location is
273
+ * @param message Failure description without location. The location is
274
274
  * appended automatically.
275
275
  * @param source Full source text, used to compute the position.
276
276
  * @param offset Character offset of the failure inside `source`.
@@ -294,7 +294,7 @@ var XcconfigParseError = class extends Error {
294
294
  /** Where in the source text parsing failed. */
295
295
  position;
296
296
  /**
297
- * @param message Failure description without location; the location is
297
+ * @param message Failure description without location. The location is
298
298
  * appended automatically.
299
299
  * @param source Full source text, used to compute the position.
300
300
  * @param offset Character offset of the failure inside `source`.
@@ -317,7 +317,7 @@ var XcschemeBuildError = class extends Error {
317
317
  /** Path to the offending element from the root, e.g. `Scheme.BuildAction[0]`. */
318
318
  path;
319
319
  /**
320
- * @param message Failure description without location; the element path
320
+ * @param message Failure description without location. The element path
321
321
  * is appended automatically.
322
322
  * @param path Path to the offending element from the root.
323
323
  */
@@ -328,7 +328,7 @@ var XcschemeBuildError = class extends Error {
328
328
  }
329
329
  };
330
330
  /**
331
- * Thrown by the object model when an operation cannot proceed: the
331
+ * Thrown by the object model when an operation cannot proceed, because the
332
332
  * document lacks the structure the operation needs (no objects dictionary,
333
333
  * no root project object), a view's object was removed from the document,
334
334
  * or a creation request names a product type the model cannot scaffold.
@@ -360,7 +360,7 @@ var PbxprojBuildError = class extends Error {
360
360
  /** Path to the offending value from the root, e.g. `$.objects.13B07F86.name`. */
361
361
  path;
362
362
  /**
363
- * @param message Failure description without location; the value path is
363
+ * @param message Failure description without location. The value path is
364
364
  * appended automatically.
365
365
  * @param path Path to the offending value from the root, `$`.
366
366
  */
@@ -387,8 +387,8 @@ var PbxprojBuildError = class extends Error {
387
387
  */
388
388
  const UNQUOTED_SAFE_PATTERN = /^[A-Za-z0-9_$/:.]+$/u;
389
389
  /**
390
- * Whether the value can render without quotes: every character is in the
391
- * unquoted-safe alphabet and the string is not empty.
390
+ * Whether the value can render without quotes, meaning every character is
391
+ * in the unquoted-safe alphabet and the string is not empty.
392
392
  *
393
393
  * Quoting decisions scan every string a document carries, and many of those
394
394
  * strings are substring slices of the source text. The regex engine flattens
@@ -400,7 +400,7 @@ function isSafeUnquoted(value) {
400
400
  }
401
401
  /**
402
402
  * Whether the string contains characters that require escape sequences
403
- * inside a quoted string: control characters, `"`, `\`, or DEL.
403
+ * inside a quoted string, meaning control characters, `"`, `\`, or DEL.
404
404
  */
405
405
  function needsEscaping(value) {
406
406
  for (let i = 0; i < value.length; i++) {
@@ -410,8 +410,8 @@ function needsEscaping(value) {
410
410
  return false;
411
411
  }
412
412
  /**
413
- * Escapes special characters for a quoted string: the named C-style escapes
414
- * plus `\Uxxxx` for remaining control characters.
413
+ * Escapes special characters for a quoted string, using the named C-style
414
+ * escapes plus `\Uxxxx` for remaining control characters.
415
415
  */
416
416
  function escapeString(value) {
417
417
  let result = "";
@@ -463,7 +463,7 @@ function ensureQuotes(value) {
463
463
  return `"${escapeString(value)}"`;
464
464
  }
465
465
  /**
466
- * Renders binary data as an uppercase hex run: `<DEADBEEF>`.
466
+ * Renders binary data as an uppercase hex run, for example `<DEADBEEF>`.
467
467
  */
468
468
  function formatData(data) {
469
469
  let hex = "";
@@ -485,7 +485,7 @@ function formatData(data) {
485
485
  * - reference comments (`13B07F86… /* AppDelegate.swift in Sources *​/`)
486
486
  * derived from the object graph.
487
487
  *
488
- * Numbers render exactly as JavaScript formats them; the version-like
488
+ * Numbers render exactly as JavaScript formats them. The version-like
489
489
  * settings Xcode writes with a trailing zero (`SWIFT_VERSION = 5.0`) arrive
490
490
  * from the parser as strings and round-trip verbatim, so no reformatting
491
491
  * heuristic is needed or applied.
@@ -541,25 +541,25 @@ function indentString(depth) {
541
541
  /**
542
542
  * Serialization state for one {@link buildPbxproj} call.
543
543
  *
544
- * The `write*` methods append directly to the output string; the `render*`
545
- * methods return fragments for the caller to place. Output accumulates by
546
- * appending, because engines represent growing strings as ropes: appends
547
- * stay cheap where template interpolation would allocate an intermediate
548
- * string per line.
544
+ * The `write*` methods append directly to the output string, and the
545
+ * `render*` methods return fragments for the caller to place. Output
546
+ * accumulates by appending, because engines represent growing strings as
547
+ * ropes. Appends stay cheap where template interpolation would allocate
548
+ * an intermediate string per line.
549
549
  */
550
550
  var Writer = class {
551
551
  /** The document text accumulated so far. */
552
552
  out = "";
553
- /** Current nesting depth; one tab per level. */
553
+ /** Current nesting depth, one tab per level. */
554
554
  indent = 0;
555
555
  /** Display comment per referenced uuid, derived once from the object graph. */
556
556
  comments;
557
557
  /**
558
- * Rendered string values by input string: `id /* comment *​/` for
559
- * referenced uuids, quoted text for everything else. Referenced objects
560
- * render at least twice (their section entry plus each referencing site)
561
- * and build settings repeat across configurations, so most renders are
562
- * cache hits. The map lives for one build call only.
558
+ * Rendered string values keyed by input string. Referenced uuids render
559
+ * as `id /* comment *​/` and everything else as quoted text. Referenced
560
+ * objects render at least twice (their section entry plus each
561
+ * referencing site) and build settings repeat across configurations, so
562
+ * most renders are cache hits. The map lives for one build call only.
563
563
  */
564
564
  renderedReferences = /* @__PURE__ */ new Map();
565
565
  /**
@@ -568,7 +568,7 @@ var Writer = class {
568
568
  */
569
569
  quotedKeys = /* @__PURE__ */ new Map();
570
570
  /**
571
- * Serializes the whole document eagerly; read it back with
571
+ * Serializes the whole document eagerly. Read it back with
572
572
  * {@link toString}.
573
573
  *
574
574
  * @param root The document root dictionary.
@@ -606,7 +606,7 @@ var Writer = class {
606
606
  *
607
607
  * Most calls hit the cache, and the writers call this for every key and
608
608
  * reference, so the method body stays small enough for the engine to
609
- * inline; the miss path lives in {@link renderReferenceUncached}.
609
+ * inline. The miss path lives in {@link renderReferenceUncached}.
610
610
  */
611
611
  renderReference(id) {
612
612
  const cached = this.renderedReferences.get(id);
@@ -647,8 +647,8 @@ var Writer = class {
647
647
  * Renders a string value in its key's context.
648
648
  *
649
649
  * `remoteGlobalIDString` and `TestTargetID` hold uuids of objects in
650
- * another container; annotating them with this container's comments would
651
- * be wrong, so they render bare.
650
+ * another container. Annotating them with this container's comments
651
+ * would be wrong, so they render bare.
652
652
  */
653
653
  renderStringValue(key, value) {
654
654
  if (key === "remoteGlobalIDString" || key === "TestTargetID") return ensureQuotes(value);
@@ -658,7 +658,7 @@ var Writer = class {
658
658
  * Appends the entries of a dictionary, one `key = value;` line each.
659
659
  *
660
660
  * Value paths (`$.objects.AA….name`) exist for error messages and are
661
- * only constructed in the branches that recurse or throw; the flat string
661
+ * only constructed in the branches that recurse or throw. The flat string
662
662
  * and number lines that dominate documents skip the concatenation.
663
663
  *
664
664
  * @param object The dictionary whose entries to write.
@@ -797,18 +797,18 @@ var Writer = class {
797
797
  /**
798
798
  * Serializes a project document to `project.pbxproj` text.
799
799
  *
800
- * The input is the same shape {@link parsePbxproj} produces; see the module
801
- * documentation of `types.ts` for the value model. Output is stable: two
800
+ * The input is the same shape {@link parsePbxproj} produces. See the module
801
+ * documentation of `types.ts` for the value model. Output is stable, so two
802
802
  * calls with semantically equal documents produce identical text, and the
803
803
  * layout matches what Xcode itself writes so diffs stay minimal.
804
804
  *
805
805
  * @param root The document root. Real project documents carry `objects`,
806
806
  * `rootObject`, and the version fields, but any dictionary serializes.
807
807
  * @returns The document text, terminated by a newline.
808
- * @throws PbxprojBuildError when a value has no pbxproj representation:
809
- * `null`, `undefined`, booleans, bigints, functions, symbols, class
810
- * instances, or non-finite numbers. The error names the path of the
811
- * offending value.
808
+ * @throws PbxprojBuildError when a value has no pbxproj representation,
809
+ * meaning `null`, `undefined`, booleans, bigints, functions, symbols,
810
+ * class instances, or non-finite numbers. The error names the path of
811
+ * the offending value.
812
812
  */
813
813
  function buildPbxproj(root) {
814
814
  if (!isDictionary(root)) throw new PbxprojBuildError("The document root must be a dictionary", "$");
@@ -819,15 +819,15 @@ function buildPbxproj(root) {
819
819
  /**
820
820
  * Names and well-known values of the pbxproj object vocabulary.
821
821
  *
822
- * Everything here mirrors strings Xcode itself writes; nothing is invented.
823
- * Centralizing them keeps the object model free of string literals and
824
- * gives call sites one place to import from.
822
+ * Everything here mirrors strings Xcode itself writes, and nothing is
823
+ * invented. Centralizing them keeps the object model free of string
824
+ * literals and gives call sites one place to import from.
825
825
  *
826
826
  * @module
827
827
  */
828
828
  /**
829
829
  * The `isa` names the object model works with. The parser and serializer
830
- * accept any isa; this list only covers the kinds the model creates or
830
+ * accept any isa, and this list only covers the kinds the model creates or
831
831
  * gives typed access to.
832
832
  */
833
833
  const Isa = {
@@ -959,7 +959,7 @@ const CopyFilesDestination = {
959
959
  };
960
960
  /**
961
961
  * Resolves the embed phase destination for an extension-like product type.
962
- * Watch applications are embedded by product type here; callers that only
962
+ * Watch applications are embedded by product type here. Callers that only
963
963
  * know build settings should check the deployment-target key instead.
964
964
  */
965
965
  function embedDestinationFor(productType) {
@@ -1047,7 +1047,7 @@ function ensureArray(object, key) {
1047
1047
  * Collects the string items of a possibly absent, possibly mixed array.
1048
1048
  *
1049
1049
  * Reference lists in well-formed documents contain only id strings, but a
1050
- * malformed document can mix in anything; non-strings are skipped rather
1050
+ * malformed document can mix in anything. Non-strings are skipped rather
1051
1051
  * than thrown on, matching the library's soft-failure stance on reads.
1052
1052
  */
1053
1053
  function stringItems(value) {
@@ -1179,8 +1179,8 @@ var XcodeObject = class {
1179
1179
  * nested dictionaries keyed by object id (such as the root project's
1180
1180
  * `TargetAttributes`) drop its entry.
1181
1181
  *
1182
- * This is the low-level removal; it does not cascade to objects that only
1183
- * made sense alongside this one. Higher-level operations like
1182
+ * This is the low-level removal, so it does not cascade to objects that
1183
+ * only made sense alongside this one. Higher-level operations like
1184
1184
  * {@link XcodeProject.removeTarget} compose it into full teardowns.
1185
1185
  */
1186
1186
  removeFromProject() {
@@ -1193,9 +1193,9 @@ var XcodeObject = class {
1193
1193
  * Typed views over the document object kinds that are not targets, from
1194
1194
  * groups and build phases to version groups and reference proxies.
1195
1195
  *
1196
- * Each class adds the accessors and mutations its kind supports; everything
1197
- * ultimately reads and writes the raw dictionaries through the base class,
1198
- * so mixing model calls with direct property access stays safe.
1196
+ * Each class adds the accessors and mutations its kind supports.
1197
+ * Everything ultimately reads and writes the raw dictionaries through the
1198
+ * base class, so mixing model calls with direct property access stays safe.
1199
1199
  *
1200
1200
  * @module
1201
1201
  */
@@ -1257,7 +1257,7 @@ var Group = class Group extends XcodeObject {
1257
1257
  * it to the group's children.
1258
1258
  *
1259
1259
  * The reference's `lastKnownFileType` derives from the file extension
1260
- * when it is a known kind; otherwise the reference carries no type and
1260
+ * when it is a known kind. Otherwise the reference carries no type and
1261
1261
  * Xcode re-derives one on open.
1262
1262
  *
1263
1263
  * @param path File path relative to the group, for example
@@ -1293,8 +1293,8 @@ var VariantGroup = class extends Group {
1293
1293
  var BuildPhase = class extends XcodeObject {
1294
1294
  /**
1295
1295
  * The phase's display name, when it carries an explicit one. Xcode names
1296
- * copy-files and shell-script phases; the standard phases derive their
1297
- * names from their isa.
1296
+ * copy-files and shell-script phases, while the standard phases derive
1297
+ * their names from their isa.
1298
1298
  */
1299
1299
  get name() {
1300
1300
  return this.getString("name");
@@ -1334,7 +1334,7 @@ var BuildPhase = class extends XcodeObject {
1334
1334
  * @param reference The file reference or package product the build file
1335
1335
  * should point at.
1336
1336
  * @param options.referenceKey Which build-file field carries the
1337
- * reference: `fileRef` for file references (the default) or
1337
+ * reference, either `fileRef` for file references (the default) or
1338
1338
  * `productRef` for Swift package products.
1339
1339
  * @param options.settings Optional per-file settings, for example
1340
1340
  * `{ ATTRIBUTES: ["RemoveHeadersOnCopy"] }`.
@@ -1447,7 +1447,7 @@ var BuildFile = class extends XcodeObject {
1447
1447
  /**
1448
1448
  * The view of the file reference the build file points at, when it
1449
1449
  * points at one. Build files for Swift package products carry a
1450
- * `productRef` instead; see {@link productDependency}.
1450
+ * `productRef` instead, resolved through {@link productDependency}.
1451
1451
  */
1452
1452
  fileReference() {
1453
1453
  return this.project.get(this.getString("fileRef"));
@@ -1480,12 +1480,12 @@ var SyncRootGroup = class extends XcodeObject {
1480
1480
  *
1481
1481
  * Xcode keeps one exception set per target and folder, so when this
1482
1482
  * group already carries a set for the target, the file names merge into
1483
- * it instead of creating a second set; names already excluded are not
1484
- * duplicated.
1483
+ * it instead of creating a second set, and names already excluded are
1484
+ * not duplicated.
1485
1485
  *
1486
1486
  * The standard use is keeping a scaffolded `Info.plist` from being
1487
- * double-copied: the build already processes it through the target's
1488
- * `INFOPLIST_FILE` setting.
1487
+ * double-copied, since the build already processes it through the
1488
+ * target's `INFOPLIST_FILE` setting.
1489
1489
  *
1490
1490
  * @param target The target whose membership the exceptions restrict.
1491
1491
  * @param membershipExceptions File names inside the folder to exclude.
@@ -1816,9 +1816,10 @@ var ReferenceProxy = class extends XcodeObject {
1816
1816
  * NeXTSTEP character set for byte values 0x80-0xFF, indexed by `byte - 0x80`.
1817
1817
  *
1818
1818
  * Octal escapes are how pre-Unicode NeXTSTEP text encoded non-ASCII
1819
- * characters; mapping them through this table is what makes `\341` decode to
1820
- * `Æ` instead of the Latin-1 `á`. Values are Unicode code points, per the
1821
- * published NEXTSTEP.TXT vendor mapping in the Unicode Character Database.
1819
+ * characters, and mapping them through this table is what makes `\341`
1820
+ * decode to `Æ` instead of the Latin-1 `á`. Values are Unicode code
1821
+ * points, per the published NEXTSTEP.TXT vendor mapping in the Unicode
1822
+ * Character Database.
1822
1823
  */
1823
1824
  const NEXT_STEP_MAPPINGS = [
1824
1825
  160,
@@ -1953,8 +1954,8 @@ const NEXT_STEP_MAPPINGS = [
1953
1954
  /**
1954
1955
  * Maps an octal escape value to its Unicode code point.
1955
1956
  *
1956
- * Values below 0x80 are ASCII and pass through; values in 0x80-0xFF select
1957
- * from the NeXTSTEP character set.
1957
+ * Values below 0x80 are ASCII and pass through, while values in 0x80-0xFF
1958
+ * select from the NeXTSTEP character set.
1958
1959
  */
1959
1960
  function nextStepToUnicode(code) {
1960
1961
  if (code < 128 || code > 255) return code;
@@ -2063,7 +2064,8 @@ function unescapeString(input) {
2063
2064
  * @module
2064
2065
  */
2065
2066
  /**
2066
- * Characters allowed in unquoted string literals: `[A-Za-z0-9_$/:.-]`.
2067
+ * The characters allowed in unquoted string literals are
2068
+ * `[A-Za-z0-9_$/:.-]`.
2067
2069
  *
2068
2070
  * A 256-entry lookup table keyed by code unit keeps classification to a
2069
2071
  * single array read in the hot loop. Non-ASCII units index past the table
@@ -2124,7 +2126,7 @@ function isDigit(code) {
2124
2126
  * Scanner state and grammar productions for one parse call.
2125
2127
  *
2126
2128
  * The parser holds a single cursor into the source string and advances it
2127
- * through the `read*` and `parse*` methods; there is no separate tokenizer
2129
+ * through the `read*` and `parse*` methods. There is no separate tokenizer
2128
2130
  * stage and no token objects.
2129
2131
  */
2130
2132
  var Parser$1 = class {
@@ -2135,7 +2137,7 @@ var Parser$1 = class {
2135
2137
  /**
2136
2138
  * Offset of an unterminated block comment the trivia scanner consumed, or
2137
2139
  * -1. Recording it instead of throwing keeps the trivia scanner free of
2138
- * failure branches; see {@link fail}.
2140
+ * failure branches, and {@link fail} reports it.
2139
2141
  */
2140
2142
  unterminatedCommentAt = -1;
2141
2143
  /**
@@ -2149,13 +2151,13 @@ var Parser$1 = class {
2149
2151
  * failure.
2150
2152
  *
2151
2153
  * An unterminated block comment swallows the rest of the input, so any
2152
- * failure raised after one (always some end-of-input error) is a symptom;
2153
- * the comment itself is reported instead. Content after the root value is
2154
- * never scanned, so a trailing unterminated comment still parses, as
2155
- * Apple's parser accepts it too.
2154
+ * failure raised after one (always some end-of-input error) is a symptom
2155
+ * and the comment itself is reported instead. Content after the root
2156
+ * value is never scanned, so a trailing unterminated comment still
2157
+ * parses, as Apple's parser accepts it too.
2156
2158
  *
2157
2159
  * @param message Failure description without location.
2158
- * @param offset Offset of the failure; defaults to the current cursor.
2160
+ * @param offset Offset of the failure, defaulting to the current cursor.
2159
2161
  */
2160
2162
  fail(message, offset = this.pos) {
2161
2163
  if (this.unterminatedCommentAt !== -1) throw new PbxprojParseError("Unterminated block comment", this.input, this.unterminatedCommentAt);
@@ -2251,11 +2253,11 @@ var Parser$1 = class {
2251
2253
  return input.slice(start, pos);
2252
2254
  }
2253
2255
  /**
2254
- * Reads a quoted string; the opening quote is at the current position.
2256
+ * Reads a quoted string whose opening quote is at the current position.
2255
2257
  *
2256
- * The scan tracks whether any escape sequence occurred: unescaped strings
2257
- * (the overwhelming majority) return as a direct slice, and only escaped
2258
- * ones pay for {@link unescapeString}.
2258
+ * The scan tracks whether any escape sequence occurred, so unescaped
2259
+ * strings (the overwhelming majority) return as a direct slice, and only
2260
+ * escaped ones pay for {@link unescapeString}.
2259
2261
  */
2260
2262
  readQuotedString() {
2261
2263
  const input = this.input;
@@ -2304,7 +2306,7 @@ var Parser$1 = class {
2304
2306
  return bytes;
2305
2307
  }
2306
2308
  /**
2307
- * Parses the document root: a dictionary or an array.
2309
+ * Parses the document root, which is a dictionary or an array.
2308
2310
  */
2309
2311
  parseDocument() {
2310
2312
  const code = this.peek();
@@ -2314,7 +2316,7 @@ var Parser$1 = class {
2314
2316
  this.fail(`Expected '{' or '(' at the start of the document but found '${this.input[this.pos]}'`);
2315
2317
  }
2316
2318
  /**
2317
- * Parses a `{ key = value; ... }` dictionary; the `{` is at the current
2319
+ * Parses a `{ key = value; ... }` dictionary whose `{` is at the current
2318
2320
  * position. Keys may be quoted or unquoted, and every entry requires the
2319
2321
  * `=` and terminating `;`.
2320
2322
  */
@@ -2348,9 +2350,9 @@ var Parser$1 = class {
2348
2350
  }
2349
2351
  }
2350
2352
  /**
2351
- * Parses a `( item, item, ... )` array; the `(` is at the current
2352
- * position. A trailing comma before `)` is allowed; Xcode writes one
2353
- * after every item.
2353
+ * Parses a `( item, item, ... )` array whose `(` is at the current
2354
+ * position. A trailing comma before `)` is allowed, because Xcode writes
2355
+ * one after every item.
2354
2356
  */
2355
2357
  parseArray() {
2356
2358
  const input = this.input;
@@ -2397,12 +2399,12 @@ var Parser$1 = class {
2397
2399
  /**
2398
2400
  * Decides whether an unquoted literal is a number or a string.
2399
2401
  *
2400
- * One loop with an early exit: the first character outside `[0-9.]` (after
2401
- * an optional leading `-`) settles the token as a string, so the
2402
- * 24-character identifiers that dominate project documents are classified
2403
- * within their first few characters.
2402
+ * The decision is one loop with an early exit. The first character outside
2403
+ * `[0-9.]` (after an optional leading `-`) settles the token as a string,
2404
+ * so the 24-character identifiers that dominate project documents are
2405
+ * classified within their first few characters.
2404
2406
  *
2405
- * Numeric-looking candidates convert under a single print-back rule: the
2407
+ * Numeric-looking candidates convert under a single print-back rule. The
2406
2408
  * literal becomes a number exactly when the number formats back to the
2407
2409
  * identical text. Any literal the conversion would reshape stays a string,
2408
2410
  * so a parse and build cycle cannot change a single byte of any scalar.
@@ -2440,13 +2442,34 @@ function interpretLiteral(literal) {
2440
2442
  * @param text Source text of the document.
2441
2443
  * @returns The document's root value. For real project files this is the
2442
2444
  * root dictionary with `objects`, `rootObject`, and version fields.
2443
- * @throws PbxprojParseError when the document is malformed; the error
2445
+ * @throws PbxprojParseError when the document is malformed. The error
2444
2446
  * carries the line and column of the failure.
2445
2447
  */
2446
2448
  function parsePbxproj(text) {
2447
2449
  return new Parser$1(text).parseDocument();
2448
2450
  }
2449
2451
  //#endregion
2452
+ //#region src/rename.ts
2453
+ /**
2454
+ * The stem-matching rule shared by the rename flows. The project model
2455
+ * renames product file references and host paths with it, and the scheme
2456
+ * model renames buildable names with it, so both sides agree on what
2457
+ * counts as the renamed target's file.
2458
+ *
2459
+ * @module
2460
+ */
2461
+ /**
2462
+ * Renames a file name whose stem is the target name, keeping the
2463
+ * extension. `SampleApp` and `SampleApp.app` rename, and so does a
2464
+ * multi-part extension like `SampleApp.app.dSYM`. A name whose stem
2465
+ * merely starts with the old name, like `SampleAppTests.xctest`, is a
2466
+ * different target's product and returns `undefined`.
2467
+ */
2468
+ function renameFileNameStem(fileName, oldName, newName) {
2469
+ if (fileName === oldName) return newName;
2470
+ if (fileName.startsWith(`${oldName}.`)) return newName + fileName.slice(oldName.length);
2471
+ }
2472
+ //#endregion
2450
2473
  //#region src/md5.ts
2451
2474
  /**
2452
2475
  * Embedded MD5, implemented from RFC 1321.
@@ -2454,8 +2477,8 @@ function parsePbxproj(text) {
2454
2477
  * Deterministic object ids hash their seed text (see `uuid.ts`), and the
2455
2478
  * library runs in every JavaScript runtime without depending on a crypto
2456
2479
  * module, so the digest is implemented here. MD5 is used strictly as a
2457
- * stable text-to-bits mapping for identifier generation; nothing security
2458
- * relevant derives from it.
2480
+ * stable text-to-bits mapping for identifier generation, and nothing
2481
+ * security relevant derives from it.
2459
2482
  *
2460
2483
  * @module
2461
2484
  */
@@ -2530,7 +2553,7 @@ const SHIFTS = [
2530
2553
  21
2531
2554
  ];
2532
2555
  /**
2533
- * Step constants for the 64 steps: the integer parts of
2556
+ * Step constants for the 64 steps, which are the integer parts of
2534
2557
  * `abs(sin(i + 1)) * 2^32`, as RFC 1321 section 3.4 tabulates them. The
2535
2558
  * values are fixed by the specification rather than derived through
2536
2559
  * `Math.sin` at load, because the digest feeds deterministic identifiers
@@ -2604,9 +2627,9 @@ const SINES = new Uint32Array([
2604
2627
  3951481745
2605
2628
  ]);
2606
2629
  /**
2607
- * Encodes text as UTF-8 bytes with RFC 1321 padding applied: a `0x80`
2608
- * terminator, zero fill to 56 bytes mod 64, then the bit length as a
2609
- * little-endian 64-bit integer.
2630
+ * Encodes text as UTF-8 bytes with RFC 1321 padding applied, meaning a
2631
+ * `0x80` terminator, zero fill to 56 bytes mod 64, then the bit length as
2632
+ * a little-endian 64-bit integer.
2610
2633
  */
2611
2634
  function paddedUtf8(text) {
2612
2635
  const bytes = new TextEncoder().encode(text);
@@ -2624,8 +2647,8 @@ function paddedUtf8(text) {
2624
2647
  /**
2625
2648
  * Adds two 32-bit values with wraparound, keeping intermediates inside the
2626
2649
  * 32-bit range JavaScript bitwise operators preserve. `Math.trunc` is not
2627
- * a substitute here: the bitwise coercion is what performs the modular
2628
- * wrap the algorithm requires.
2650
+ * a substitute here, because the bitwise coercion is what performs the
2651
+ * modular wrap the algorithm requires.
2629
2652
  */
2630
2653
  function add32(a, b) {
2631
2654
  return a + b | 0;
@@ -2697,7 +2720,7 @@ function md5Hex(text) {
2697
2720
  * Deterministic object identifiers for generated pbxproj objects.
2698
2721
  *
2699
2722
  * Xcode identifies every object with 24 hexadecimal characters. Generated
2700
- * ids here are deterministic: the same seed always produces the same id, so
2723
+ * ids here are deterministic. The same seed always produces the same id, so
2701
2724
  * programmatic edits are reproducible and diffs stay minimal across runs.
2702
2725
  * The format is `XX` + the first 20 characters of `md5(seed)` + `XX`, which
2703
2726
  * is a valid identifier that remains recognizable as generated.
@@ -2940,7 +2963,7 @@ function defaultConfigurationSettingsOf(project, configurationListId) {
2940
2963
  * extends it with products, embedding, synchronized folders, Swift
2941
2964
  * packages, and system frameworks.
2942
2965
  *
2943
- * Reads are deliberately soft: user-generated projects can be malformed,
2966
+ * Reads are deliberately soft. User-generated projects can be malformed,
2944
2967
  * so lookups return `undefined` instead of throwing wherever a document
2945
2968
  * could legally or illegally omit something. Mutations create any missing
2946
2969
  * structure they need.
@@ -2993,11 +3016,11 @@ var Target = class extends XcodeObject {
2993
3016
  *
2994
3017
  * Configurations based on `.xcconfig` files take part once the files
2995
3018
  * are registered through {@link XcodeProject.registerXcconfig}, in
2996
- * Xcode's order: target settings, the target's xcconfig, project
2997
- * settings, the project's xcconfig.
3019
+ * Xcode's order of target settings, then the target's xcconfig, then
3020
+ * project settings, then the project's xcconfig.
2998
3021
  *
2999
- * Only string values are returned; a list- or number-valued setting reads
3000
- * as `undefined`.
3022
+ * Only string values are returned, so a list- or number-valued setting
3023
+ * reads as `undefined`.
3001
3024
  */
3002
3025
  getBuildSetting(key) {
3003
3026
  const targetConfiguration = defaultConfigurationOf(this.project, this.getString("buildConfigurationList"));
@@ -3372,7 +3395,7 @@ var LegacyTarget = class extends Target {
3372
3395
  * `project.pbxproj`.
3373
3396
  *
3374
3397
  * The model is a set of lightweight views over the plain parsed document.
3375
- * All state lives in the document itself; views hold only an id and a
3398
+ * All state lives in the document itself. Views hold only an id and a
3376
3399
  * project reference, so model mutations and direct dictionary writes
3377
3400
  * compose freely and {@link XcodeProject.build} always serializes the
3378
3401
  * current state. New objects receive deterministic identifiers (see
@@ -3402,7 +3425,7 @@ var RootProject = class extends XcodeObject {
3402
3425
  }
3403
3426
  /**
3404
3427
  * The settings dictionary of the project-level default configuration.
3405
- * Targets inherit from these settings; see
3428
+ * Targets inherit from these settings, as described on
3406
3429
  * {@link NativeTarget.getBuildSetting}.
3407
3430
  */
3408
3431
  defaultConfigurationSettings() {
@@ -3477,7 +3500,7 @@ var XcodeProject = class XcodeProject {
3477
3500
  }
3478
3501
  /**
3479
3502
  * Wraps an already parsed document in a model. The document is used in
3480
- * place, not copied; model mutations write into it.
3503
+ * place rather than copied, so model mutations write into it.
3481
3504
  */
3482
3505
  static fromDocument(document) {
3483
3506
  return new XcodeProject(document);
@@ -3492,7 +3515,7 @@ var XcodeProject = class XcodeProject {
3492
3515
  /**
3493
3516
  * The raw properties dictionary of an object.
3494
3517
  *
3495
- * @throws XcodeModelError when no object with the id exists; views use
3518
+ * @throws XcodeModelError when no object with the id exists. Views use
3496
3519
  * this accessor, so a view of a deleted object fails loudly instead of
3497
3520
  * resurrecting the entry.
3498
3521
  */
@@ -3535,8 +3558,8 @@ var XcodeProject = class XcodeProject {
3535
3558
  }
3536
3559
  /**
3537
3560
  * Generates a deterministic 24-character id from a seed, avoiding every
3538
- * id the document currently contains. The id is not reserved; adding an
3539
- * object with it (see {@link add}) is what claims it.
3561
+ * id the document currently contains. The id is not reserved, and only
3562
+ * adding an object with it (see {@link add}) claims it.
3540
3563
  */
3541
3564
  generateId(seed) {
3542
3565
  return generateObjectId(seed, new Set(Object.keys(this.objectsDictionary)));
@@ -3643,7 +3666,8 @@ var XcodeProject = class XcodeProject {
3643
3666
  * and Resources build phases, and registers it on the project.
3644
3667
  *
3645
3668
  * @throws XcodeModelError for product types the model cannot create a
3646
- * product reference for; see {@link AddNativeTargetOptions.productType}.
3669
+ * product reference for, listed on
3670
+ * {@link AddNativeTargetOptions.productType}.
3647
3671
  */
3648
3672
  addNativeTarget(options) {
3649
3673
  const productInfo = PRODUCT_FILE_INFO[options.productType];
@@ -3689,7 +3713,7 @@ var XcodeProject = class XcodeProject {
3689
3713
  /**
3690
3714
  * Adds a remote Swift package reference and registers it on the project.
3691
3715
  * When the project already references the repository, the existing
3692
- * reference is returned unchanged, requirement included; adjust an
3716
+ * reference is returned unchanged, requirement included. Adjust an
3693
3717
  * existing requirement through the reference's properties. Link products
3694
3718
  * to targets with {@link NativeTarget.addSwiftPackageProduct}.
3695
3719
  *
@@ -3764,7 +3788,7 @@ var XcodeProject = class XcodeProject {
3764
3788
  * list containing it, or a nested dictionary carrying it as a key or
3765
3789
  * string value.
3766
3790
  *
3767
- * The scan is linear over the document; removal flows call it once per
3791
+ * The scan is linear over the document. Removal flows call it once per
3768
3792
  * removed object, which keeps teardown proportional to what is actually
3769
3793
  * removed.
3770
3794
  */
@@ -3780,7 +3804,7 @@ var XcodeProject = class XcodeProject {
3780
3804
  * (such as `TargetAttributes`) drop its entry.
3781
3805
  *
3782
3806
  * Removing an id the document does not contain is a no-op. This is the
3783
- * low-level removal; {@link removeTarget} composes it into a full
3807
+ * low-level removal, and {@link removeTarget} composes it into a full
3784
3808
  * teardown.
3785
3809
  */
3786
3810
  removeObject(id) {
@@ -3798,7 +3822,7 @@ var XcodeProject = class XcodeProject {
3798
3822
  * dependencies on others), its membership exception sets, and
3799
3823
  * synchronized folders no remaining target links.
3800
3824
  *
3801
- * On-disk sources are untouched; the removal is document-only, like
3825
+ * On-disk sources are untouched. The removal is document-only, like
3802
3826
  * deleting a target in Xcode and keeping its folder.
3803
3827
  *
3804
3828
  * @throws XcodeModelError when the target belongs to another project,
@@ -3841,12 +3865,58 @@ var XcodeProject = class XcodeProject {
3841
3865
  for (const groupId of syncGroupIds) if (!this.nativeTargets().some((remaining) => stringItems(remaining.properties["fileSystemSynchronizedGroups"]).includes(groupId))) this.removeObject(groupId);
3842
3866
  }
3843
3867
  /**
3868
+ * Renames a target and every place the document knows it by name. That
3869
+ * covers the target's `name` and `productName`, its product file
3870
+ * reference (`OldName.app` becomes `NewName.app`, whatever the
3871
+ * extension), the `remoteInfo` of container item proxies pointing at
3872
+ * the target, `TEST_TARGET_NAME` settings naming it, and the path
3873
+ * segments of `TEST_HOST` and `BUNDLE_LOADER` settings that name the
3874
+ * target or its product. A `PRODUCT_NAME` of the target's own
3875
+ * configurations is rewritten only when it spells the old name
3876
+ * literally. The usual `$(TARGET_NAME)` follows by itself.
3877
+ *
3878
+ * Scheme files live outside the pbxproj, so buildable references are
3879
+ * renamed separately through `Xcscheme.renameTarget`. On-disk renames
3880
+ * (source folders, entitlements files) and the group paths pointing at
3881
+ * those folders stay with the caller. Sibling targets such as
3882
+ * `OldNameTests` are renamed with their own calls.
3883
+ *
3884
+ * @throws XcodeModelError when the target belongs to another project.
3885
+ */
3886
+ renameTarget(target, newName) {
3887
+ if (target.project !== this) throw new XcodeModelError("Cannot rename a target that belongs to another project");
3888
+ const oldName = target.name;
3889
+ if (oldName === newName) return;
3890
+ target.set("name", newName);
3891
+ if (oldName == null) return;
3892
+ if (target.getString("productName") === oldName) target.set("productName", newName);
3893
+ const product = this.get(target.getString("productReference"));
3894
+ if (product != null) for (const key of ["path", "name"]) {
3895
+ const fileName = product.getString(key);
3896
+ const renamed = fileName == null ? void 0 : renameFileNameStem(fileName, oldName, newName);
3897
+ if (renamed != null) product.set(key, renamed);
3898
+ }
3899
+ for (const configuration of target.buildConfigurations()) {
3900
+ const settings = configuration.buildSettings;
3901
+ if (settings?.PRODUCT_NAME === oldName) settings.PRODUCT_NAME = newName;
3902
+ }
3903
+ for (const [, view] of this.objects()) {
3904
+ if (ContainerItemProxy.is(view) && view.getString("remoteGlobalIDString") === target.id && view.remoteInfo === oldName) view.set("remoteInfo", newName);
3905
+ if (BuildConfiguration.is(view)) {
3906
+ const settings = view.buildSettings;
3907
+ if (settings == null) continue;
3908
+ if (settings.TEST_TARGET_NAME === oldName) settings.TEST_TARGET_NAME = newName;
3909
+ renameHostPathSettings(settings, oldName, newName);
3910
+ }
3911
+ }
3912
+ }
3913
+ /**
3844
3914
  * Finds a file reference by its project-relative path, resolving each
3845
3915
  * reference's location through the group tree from the main group
3846
3916
  * (nested group `path` components join with `/`).
3847
3917
  *
3848
3918
  * Members of file-system-synchronized folders are not listed in the
3849
- * document and therefore cannot be found; check
3919
+ * document and therefore cannot be found. Check
3850
3920
  * {@link NativeTarget.syncGroupPaths} for folder-level containment
3851
3921
  * instead.
3852
3922
  */
@@ -3933,6 +4003,31 @@ function joinPath(prefix, segment) {
3933
4003
  return prefix === "" ? segment : `${prefix}/${segment}`;
3934
4004
  }
3935
4005
  /**
4006
+ * Renames the segments of a path-valued build setting that name the
4007
+ * target or one of its products. Settings like `TEST_HOST` embed the
4008
+ * product path as
4009
+ * `$(BUILT_PRODUCTS_DIR)/SampleApp.app/.../SampleApp`, so each segment
4010
+ * is matched whole against the target name. Substring occurrences inside
4011
+ * unrelated segments stay untouched.
4012
+ */
4013
+ function renamePathSegments(value, oldName, newName) {
4014
+ return value.split("/").map((segment) => renameFileNameStem(segment, oldName, newName) ?? segment).join("/");
4015
+ }
4016
+ /**
4017
+ * Renames the target name inside the host-path settings of one
4018
+ * configuration. `TEST_HOST` and `BUNDLE_LOADER` carry the hosting
4019
+ * product's path, so their segments rename the way the product file
4020
+ * reference does.
4021
+ */
4022
+ function renameHostPathSettings(settings, oldName, newName) {
4023
+ for (const key of ["TEST_HOST", "BUNDLE_LOADER"]) {
4024
+ const value = asString(settings[key]);
4025
+ if (value == null) continue;
4026
+ const rewritten = renamePathSegments(value, oldName, newName);
4027
+ if (rewritten !== value) settings[key] = rewritten;
4028
+ }
4029
+ }
4030
+ /**
3936
4031
  * Whether a value references the id anywhere in its structure. A
3937
4032
  * reference is a string equal to it, an array containing it at any depth,
3938
4033
  * or a dictionary carrying it as a key or somewhere in its values.
@@ -3978,7 +4073,7 @@ function stripValue(value, id) {
3978
4073
  return value;
3979
4074
  }
3980
4075
  /**
3981
- * Strips every reference to the id from an object's properties; see
4076
+ * Strips every reference to the id from an object's properties. See
3982
4077
  * {@link stripValue} for the shapes handled. String properties naming the
3983
4078
  * id are deleted rather than left empty.
3984
4079
  */
@@ -4530,6 +4625,46 @@ var Xcscheme = class Xcscheme {
4530
4625
  buildableReferences() {
4531
4626
  return this.elements("BuildableReference").map((element) => new BuildableReference(element));
4532
4627
  }
4628
+ /**
4629
+ * Renames every buildable reference pointing at a target. The blueprint
4630
+ * name is matched whole, and the buildable name is matched by its stem,
4631
+ * so `OldApp.app` becomes `NewApp.app` while `OldAppTests.xctest`, a
4632
+ * different target's product, stays untouched. This is the scheme-file
4633
+ * side of `XcodeProject.renameTarget`. Returns whether anything
4634
+ * changed, so callers can skip rewriting untouched files.
4635
+ */
4636
+ renameTarget(oldName, newName) {
4637
+ if (oldName === newName) return false;
4638
+ let changed = false;
4639
+ for (const reference of this.buildableReferences()) {
4640
+ if (reference.blueprintName === oldName) {
4641
+ reference.blueprintName = newName;
4642
+ changed = true;
4643
+ }
4644
+ const buildableName = reference.buildableName;
4645
+ const renamed = buildableName == null ? void 0 : renameFileNameStem(buildableName, oldName, newName);
4646
+ if (renamed != null) {
4647
+ reference.buildableName = renamed;
4648
+ changed = true;
4649
+ }
4650
+ }
4651
+ return changed;
4652
+ }
4653
+ /**
4654
+ * Rewrites every buildable reference's container after the
4655
+ * `.xcodeproj` directory itself is renamed, so `container:Old.xcodeproj`
4656
+ * becomes `container:New.xcodeproj`. The project names are matched
4657
+ * exactly. Returns whether anything changed.
4658
+ */
4659
+ renameContainer(oldProjectName, newProjectName) {
4660
+ if (oldProjectName === newProjectName) return false;
4661
+ let changed = false;
4662
+ for (const reference of this.buildableReferences()) if (reference.referencedContainer === `container:${oldProjectName}.xcodeproj`) {
4663
+ reference.referencedContainer = `container:${newProjectName}.xcodeproj`;
4664
+ changed = true;
4665
+ }
4666
+ return changed;
4667
+ }
4533
4668
  };
4534
4669
  /**
4535
4670
  * Creates the scheme Xcode writes for an application target. The
@@ -4664,8 +4799,8 @@ function buildXcconfig(document) {
4664
4799
  */
4665
4800
  const INCLUDE_PATTERN = /^#include(\?)?\s*"([^"]*)"\s*(?:\/\/.*)?$/u;
4666
4801
  /**
4667
- * Matches the head of an assignment: leading whitespace, the setting
4668
- * name, and the raw conditions block up to the equals sign.
4802
+ * Matches the head of an assignment, which is the leading whitespace, the
4803
+ * setting name, and the raw conditions block up to the equals sign.
4669
4804
  */
4670
4805
  const ASSIGNMENT_HEAD_PATTERN = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*((?:\[[^\]]*\])*)\s*=/u;
4671
4806
  /**
@@ -4777,7 +4912,7 @@ function parseXcconfig(source) {
4777
4912
  * Object model for Xcode build configuration files (`.xcconfig`).
4778
4913
  *
4779
4914
  * {@link Xcconfig} wraps a parsed document the way `XcodeProject` wraps a
4780
- * pbxproj: the document stays the single source of truth, reads and
4915
+ * pbxproj. The document stays the single source of truth, reads and
4781
4916
  * writes go through it, and {@link Xcconfig.build} emits it back with
4782
4917
  * untouched lines preserved byte for byte.
4783
4918
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rork-xcode",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Zero-dependency Xcode project (pbxproj) and scheme (xcscheme) parser, builder, and object model that runs identically in every JavaScript runtime",
5
5
  "keywords": [
6
6
  "apple",