rork-xcode 0.9.0 → 0.11.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/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,37 +797,185 @@ 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", "$");
815
815
  return new Writer(root).toString();
816
816
  }
817
817
  //#endregion
818
+ //#region src/expansion.ts
819
+ /**
820
+ * Expands every `$(NAME)` and `${NAME}` reference in a value through the
821
+ * lookup. Substituted text is expanded again, so references are followed
822
+ * through chains of values, and reference names may themselves contain
823
+ * references (`$(SETTING_$(VARIANT))`), which expand innermost first.
824
+ *
825
+ * A lookup answer of `undefined` leaves the reference in the output
826
+ * verbatim, so partial resolution loses no information. A reference
827
+ * whose expansion would recurse into itself also stays verbatim, which
828
+ * keeps cyclic definitions finite. Xcode's `:` operators are honored for
829
+ * the forms below, and a reference carrying any other operator stays
830
+ * verbatim rather than expanding to a wrongly transformed value.
831
+ *
832
+ * The supported operators are `lower` and `upper` for case mapping,
833
+ * `rfc1034identifier` and `c99extidentifier` for the identifier
834
+ * mappings bundle identifiers and product module names use, and
835
+ * `default=value` for substituting a fallback when the setting resolves
836
+ * empty or is not known to the lookup.
837
+ */
838
+ function expandBuildSettingReferences(value, lookup, options) {
839
+ return expand(value, lookup, options?.expandLookupValues !== false, /* @__PURE__ */ new Set());
840
+ }
841
+ /** The UTF-16 code unit of `$`, which starts every reference. */
842
+ const CODE_DOLLAR = 36;
843
+ /**
844
+ * One expansion pass over a value. Active names carry the references
845
+ * currently being expanded up the call stack, so a cycle is detected the
846
+ * moment a name would expand inside its own expansion.
847
+ */
848
+ function expand(value, lookup, expandLookupValues, active) {
849
+ if (!value.includes("$")) return value;
850
+ let out = "";
851
+ let index = 0;
852
+ while (index < value.length) {
853
+ const open = value.charCodeAt(index) === CODE_DOLLAR ? value[index + 1] : void 0;
854
+ if (open !== "(" && open !== "{") {
855
+ out += value[index];
856
+ index++;
857
+ continue;
858
+ }
859
+ const end = findClosingDelimiter(value, index + 2, open === "(" ? ")" : "}");
860
+ if (end === -1) {
861
+ out += value.slice(index);
862
+ break;
863
+ }
864
+ const reference = value.slice(index, end + 1);
865
+ const inner = expand(value.slice(index + 2, end), lookup, expandLookupValues, active);
866
+ out += expandReference(reference, inner, lookup, expandLookupValues, active);
867
+ index = end + 1;
868
+ }
869
+ return out;
870
+ }
871
+ /**
872
+ * Finds the index of the closing delimiter matching an opener at
873
+ * `start - 1`, honoring nested delimiters of the same kind, or -1 when
874
+ * the reference is unterminated.
875
+ */
876
+ function findClosingDelimiter(value, start, close) {
877
+ const open = close === ")" ? "(" : "{";
878
+ let depth = 0;
879
+ for (let i = start; i < value.length; i++) {
880
+ const ch = value[i];
881
+ if (ch === open) depth++;
882
+ else if (ch === close) {
883
+ if (depth === 0) return i;
884
+ depth--;
885
+ }
886
+ }
887
+ return -1;
888
+ }
889
+ /**
890
+ * Expands one reference whose name and operators have already had their
891
+ * own references resolved. Returns the original reference text whenever
892
+ * the expansion cannot honestly produce a value.
893
+ */
894
+ function expandReference(reference, inner, lookup, expandLookupValues, active) {
895
+ const colon = inner.indexOf(":");
896
+ const name = colon === -1 ? inner : inner.slice(0, colon);
897
+ if (active.has(name)) return reference;
898
+ const raw = lookup(name);
899
+ const nested = new Set(active);
900
+ nested.add(name);
901
+ let value = raw;
902
+ if (raw != null && expandLookupValues) value = expand(raw, lookup, expandLookupValues, nested);
903
+ if (colon !== -1) {
904
+ for (const operator of splitOperators(inner.slice(colon + 1))) if (operator.startsWith("default=")) {
905
+ if (value == null || value === "") value = operator.slice(8);
906
+ } else if (value != null) {
907
+ const applied = applyOperator(operator, value);
908
+ if (applied == null) return reference;
909
+ value = applied;
910
+ } else if (applyOperator(operator, "") == null) return reference;
911
+ }
912
+ return value ?? reference;
913
+ }
914
+ /**
915
+ * Splits an operator chain at colons, keeping a `default=` value intact
916
+ * even when it contains colons of its own, the way Xcode consumes the
917
+ * rest of the reference as the default.
918
+ */
919
+ function splitOperators(operators) {
920
+ const parts = [];
921
+ let rest = operators;
922
+ while (rest.length > 0) {
923
+ if (rest.startsWith("default=")) {
924
+ parts.push(rest);
925
+ break;
926
+ }
927
+ const colon = rest.indexOf(":");
928
+ if (colon === -1) {
929
+ parts.push(rest);
930
+ break;
931
+ }
932
+ parts.push(rest.slice(0, colon));
933
+ rest = rest.slice(colon + 1);
934
+ }
935
+ return parts;
936
+ }
937
+ /**
938
+ * The transforming operators the expander supports, keyed by the name
939
+ * they are written with. `default=` is not listed because it carries an
940
+ * argument and substitutes rather than transforms, so the reference
941
+ * expansion handles it structurally.
942
+ */
943
+ const OPERATOR_TRANSFORMS = {
944
+ c99extidentifier: (value) => value.replaceAll(/[^A-Za-z0-9_]/gu, "_"),
945
+ lower: (value) => value.toLowerCase(),
946
+ rfc1034identifier: (value) => value.replaceAll(/[^A-Za-z0-9-]/gu, "-"),
947
+ upper: (value) => value.toUpperCase()
948
+ };
949
+ /**
950
+ * Whether a parsed operator names one of the supported transforms. The
951
+ * check is an own-property test, so document text like `toString` cannot
952
+ * reach the table's prototype.
953
+ */
954
+ function isBuildSettingOperator(operator) {
955
+ return Object.hasOwn(OPERATOR_TRANSFORMS, operator);
956
+ }
957
+ /**
958
+ * Applies one operator to a resolved value, or returns `undefined` for
959
+ * operators outside {@link BuildSettingOperator}, which the caller turns
960
+ * into a verbatim reference.
961
+ */
962
+ function applyOperator(operator, value) {
963
+ return isBuildSettingOperator(operator) ? OPERATOR_TRANSFORMS[operator](value) : void 0;
964
+ }
965
+ //#endregion
818
966
  //#region src/model/isa.ts
819
967
  /**
820
968
  * Names and well-known values of the pbxproj object vocabulary.
821
969
  *
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.
970
+ * Everything here mirrors strings Xcode itself writes, and nothing is
971
+ * invented. Centralizing them keeps the object model free of string
972
+ * literals and gives call sites one place to import from.
825
973
  *
826
974
  * @module
827
975
  */
828
976
  /**
829
977
  * 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
978
+ * accept any isa, and this list only covers the kinds the model creates or
831
979
  * gives typed access to.
832
980
  */
833
981
  const Isa = {
@@ -959,7 +1107,7 @@ const CopyFilesDestination = {
959
1107
  };
960
1108
  /**
961
1109
  * Resolves the embed phase destination for an extension-like product type.
962
- * Watch applications are embedded by product type here; callers that only
1110
+ * Watch applications are embedded by product type here. Callers that only
963
1111
  * know build settings should check the deployment-target key instead.
964
1112
  */
965
1113
  function embedDestinationFor(productType) {
@@ -1047,7 +1195,7 @@ function ensureArray(object, key) {
1047
1195
  * Collects the string items of a possibly absent, possibly mixed array.
1048
1196
  *
1049
1197
  * Reference lists in well-formed documents contain only id strings, but a
1050
- * malformed document can mix in anything; non-strings are skipped rather
1198
+ * malformed document can mix in anything. Non-strings are skipped rather
1051
1199
  * than thrown on, matching the library's soft-failure stance on reads.
1052
1200
  */
1053
1201
  function stringItems(value) {
@@ -1179,8 +1327,8 @@ var XcodeObject = class {
1179
1327
  * nested dictionaries keyed by object id (such as the root project's
1180
1328
  * `TargetAttributes`) drop its entry.
1181
1329
  *
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
1330
+ * This is the low-level removal, so it does not cascade to objects that
1331
+ * only made sense alongside this one. Higher-level operations like
1184
1332
  * {@link XcodeProject.removeTarget} compose it into full teardowns.
1185
1333
  */
1186
1334
  removeFromProject() {
@@ -1193,9 +1341,9 @@ var XcodeObject = class {
1193
1341
  * Typed views over the document object kinds that are not targets, from
1194
1342
  * groups and build phases to version groups and reference proxies.
1195
1343
  *
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.
1344
+ * Each class adds the accessors and mutations its kind supports.
1345
+ * Everything ultimately reads and writes the raw dictionaries through the
1346
+ * base class, so mixing model calls with direct property access stays safe.
1199
1347
  *
1200
1348
  * @module
1201
1349
  */
@@ -1257,7 +1405,7 @@ var Group = class Group extends XcodeObject {
1257
1405
  * it to the group's children.
1258
1406
  *
1259
1407
  * The reference's `lastKnownFileType` derives from the file extension
1260
- * when it is a known kind; otherwise the reference carries no type and
1408
+ * when it is a known kind. Otherwise the reference carries no type and
1261
1409
  * Xcode re-derives one on open.
1262
1410
  *
1263
1411
  * @param path File path relative to the group, for example
@@ -1293,8 +1441,8 @@ var VariantGroup = class extends Group {
1293
1441
  var BuildPhase = class extends XcodeObject {
1294
1442
  /**
1295
1443
  * 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.
1444
+ * copy-files and shell-script phases, while the standard phases derive
1445
+ * their names from their isa.
1298
1446
  */
1299
1447
  get name() {
1300
1448
  return this.getString("name");
@@ -1334,7 +1482,7 @@ var BuildPhase = class extends XcodeObject {
1334
1482
  * @param reference The file reference or package product the build file
1335
1483
  * should point at.
1336
1484
  * @param options.referenceKey Which build-file field carries the
1337
- * reference: `fileRef` for file references (the default) or
1485
+ * reference, either `fileRef` for file references (the default) or
1338
1486
  * `productRef` for Swift package products.
1339
1487
  * @param options.settings Optional per-file settings, for example
1340
1488
  * `{ ATTRIBUTES: ["RemoveHeadersOnCopy"] }`.
@@ -1447,7 +1595,7 @@ var BuildFile = class extends XcodeObject {
1447
1595
  /**
1448
1596
  * The view of the file reference the build file points at, when it
1449
1597
  * points at one. Build files for Swift package products carry a
1450
- * `productRef` instead; see {@link productDependency}.
1598
+ * `productRef` instead, resolved through {@link productDependency}.
1451
1599
  */
1452
1600
  fileReference() {
1453
1601
  return this.project.get(this.getString("fileRef"));
@@ -1480,12 +1628,12 @@ var SyncRootGroup = class extends XcodeObject {
1480
1628
  *
1481
1629
  * Xcode keeps one exception set per target and folder, so when this
1482
1630
  * 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.
1631
+ * it instead of creating a second set, and names already excluded are
1632
+ * not duplicated.
1485
1633
  *
1486
1634
  * 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.
1635
+ * double-copied, since the build already processes it through the
1636
+ * target's `INFOPLIST_FILE` setting.
1489
1637
  *
1490
1638
  * @param target The target whose membership the exceptions restrict.
1491
1639
  * @param membershipExceptions File names inside the folder to exclude.
@@ -1816,9 +1964,10 @@ var ReferenceProxy = class extends XcodeObject {
1816
1964
  * NeXTSTEP character set for byte values 0x80-0xFF, indexed by `byte - 0x80`.
1817
1965
  *
1818
1966
  * 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.
1967
+ * characters, and mapping them through this table is what makes `\341`
1968
+ * decode to `Æ` instead of the Latin-1 `á`. Values are Unicode code
1969
+ * points, per the published NEXTSTEP.TXT vendor mapping in the Unicode
1970
+ * Character Database.
1822
1971
  */
1823
1972
  const NEXT_STEP_MAPPINGS = [
1824
1973
  160,
@@ -1953,8 +2102,8 @@ const NEXT_STEP_MAPPINGS = [
1953
2102
  /**
1954
2103
  * Maps an octal escape value to its Unicode code point.
1955
2104
  *
1956
- * Values below 0x80 are ASCII and pass through; values in 0x80-0xFF select
1957
- * from the NeXTSTEP character set.
2105
+ * Values below 0x80 are ASCII and pass through, while values in 0x80-0xFF
2106
+ * select from the NeXTSTEP character set.
1958
2107
  */
1959
2108
  function nextStepToUnicode(code) {
1960
2109
  if (code < 128 || code > 255) return code;
@@ -2063,7 +2212,8 @@ function unescapeString(input) {
2063
2212
  * @module
2064
2213
  */
2065
2214
  /**
2066
- * Characters allowed in unquoted string literals: `[A-Za-z0-9_$/:.-]`.
2215
+ * The characters allowed in unquoted string literals are
2216
+ * `[A-Za-z0-9_$/:.-]`.
2067
2217
  *
2068
2218
  * A 256-entry lookup table keyed by code unit keeps classification to a
2069
2219
  * single array read in the hot loop. Non-ASCII units index past the table
@@ -2124,7 +2274,7 @@ function isDigit(code) {
2124
2274
  * Scanner state and grammar productions for one parse call.
2125
2275
  *
2126
2276
  * 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
2277
+ * through the `read*` and `parse*` methods. There is no separate tokenizer
2128
2278
  * stage and no token objects.
2129
2279
  */
2130
2280
  var Parser$1 = class {
@@ -2135,7 +2285,7 @@ var Parser$1 = class {
2135
2285
  /**
2136
2286
  * Offset of an unterminated block comment the trivia scanner consumed, or
2137
2287
  * -1. Recording it instead of throwing keeps the trivia scanner free of
2138
- * failure branches; see {@link fail}.
2288
+ * failure branches, and {@link fail} reports it.
2139
2289
  */
2140
2290
  unterminatedCommentAt = -1;
2141
2291
  /**
@@ -2149,13 +2299,13 @@ var Parser$1 = class {
2149
2299
  * failure.
2150
2300
  *
2151
2301
  * 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.
2302
+ * failure raised after one (always some end-of-input error) is a symptom
2303
+ * and the comment itself is reported instead. Content after the root
2304
+ * value is never scanned, so a trailing unterminated comment still
2305
+ * parses, as Apple's parser accepts it too.
2156
2306
  *
2157
2307
  * @param message Failure description without location.
2158
- * @param offset Offset of the failure; defaults to the current cursor.
2308
+ * @param offset Offset of the failure, defaulting to the current cursor.
2159
2309
  */
2160
2310
  fail(message, offset = this.pos) {
2161
2311
  if (this.unterminatedCommentAt !== -1) throw new PbxprojParseError("Unterminated block comment", this.input, this.unterminatedCommentAt);
@@ -2251,11 +2401,11 @@ var Parser$1 = class {
2251
2401
  return input.slice(start, pos);
2252
2402
  }
2253
2403
  /**
2254
- * Reads a quoted string; the opening quote is at the current position.
2404
+ * Reads a quoted string whose opening quote is at the current position.
2255
2405
  *
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}.
2406
+ * The scan tracks whether any escape sequence occurred, so unescaped
2407
+ * strings (the overwhelming majority) return as a direct slice, and only
2408
+ * escaped ones pay for {@link unescapeString}.
2259
2409
  */
2260
2410
  readQuotedString() {
2261
2411
  const input = this.input;
@@ -2304,7 +2454,7 @@ var Parser$1 = class {
2304
2454
  return bytes;
2305
2455
  }
2306
2456
  /**
2307
- * Parses the document root: a dictionary or an array.
2457
+ * Parses the document root, which is a dictionary or an array.
2308
2458
  */
2309
2459
  parseDocument() {
2310
2460
  const code = this.peek();
@@ -2314,7 +2464,7 @@ var Parser$1 = class {
2314
2464
  this.fail(`Expected '{' or '(' at the start of the document but found '${this.input[this.pos]}'`);
2315
2465
  }
2316
2466
  /**
2317
- * Parses a `{ key = value; ... }` dictionary; the `{` is at the current
2467
+ * Parses a `{ key = value; ... }` dictionary whose `{` is at the current
2318
2468
  * position. Keys may be quoted or unquoted, and every entry requires the
2319
2469
  * `=` and terminating `;`.
2320
2470
  */
@@ -2348,9 +2498,9 @@ var Parser$1 = class {
2348
2498
  }
2349
2499
  }
2350
2500
  /**
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.
2501
+ * Parses a `( item, item, ... )` array whose `(` is at the current
2502
+ * position. A trailing comma before `)` is allowed, because Xcode writes
2503
+ * one after every item.
2354
2504
  */
2355
2505
  parseArray() {
2356
2506
  const input = this.input;
@@ -2397,12 +2547,12 @@ var Parser$1 = class {
2397
2547
  /**
2398
2548
  * Decides whether an unquoted literal is a number or a string.
2399
2549
  *
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.
2550
+ * The decision is one loop with an early exit. The first character outside
2551
+ * `[0-9.]` (after an optional leading `-`) settles the token as a string,
2552
+ * so the 24-character identifiers that dominate project documents are
2553
+ * classified within their first few characters.
2404
2554
  *
2405
- * Numeric-looking candidates convert under a single print-back rule: the
2555
+ * Numeric-looking candidates convert under a single print-back rule. The
2406
2556
  * literal becomes a number exactly when the number formats back to the
2407
2557
  * identical text. Any literal the conversion would reshape stays a string,
2408
2558
  * so a parse and build cycle cannot change a single byte of any scalar.
@@ -2440,13 +2590,34 @@ function interpretLiteral(literal) {
2440
2590
  * @param text Source text of the document.
2441
2591
  * @returns The document's root value. For real project files this is the
2442
2592
  * root dictionary with `objects`, `rootObject`, and version fields.
2443
- * @throws PbxprojParseError when the document is malformed; the error
2593
+ * @throws PbxprojParseError when the document is malformed. The error
2444
2594
  * carries the line and column of the failure.
2445
2595
  */
2446
2596
  function parsePbxproj(text) {
2447
2597
  return new Parser$1(text).parseDocument();
2448
2598
  }
2449
2599
  //#endregion
2600
+ //#region src/rename.ts
2601
+ /**
2602
+ * The stem-matching rule shared by the rename flows. The project model
2603
+ * renames product file references and host paths with it, and the scheme
2604
+ * model renames buildable names with it, so both sides agree on what
2605
+ * counts as the renamed target's file.
2606
+ *
2607
+ * @module
2608
+ */
2609
+ /**
2610
+ * Renames a file name whose stem is the target name, keeping the
2611
+ * extension. `SampleApp` and `SampleApp.app` rename, and so does a
2612
+ * multi-part extension like `SampleApp.app.dSYM`. A name whose stem
2613
+ * merely starts with the old name, like `SampleAppTests.xctest`, is a
2614
+ * different target's product and returns `undefined`.
2615
+ */
2616
+ function renameFileNameStem(fileName, oldName, newName) {
2617
+ if (fileName === oldName) return newName;
2618
+ if (fileName.startsWith(`${oldName}.`)) return newName + fileName.slice(oldName.length);
2619
+ }
2620
+ //#endregion
2450
2621
  //#region src/md5.ts
2451
2622
  /**
2452
2623
  * Embedded MD5, implemented from RFC 1321.
@@ -2454,8 +2625,8 @@ function parsePbxproj(text) {
2454
2625
  * Deterministic object ids hash their seed text (see `uuid.ts`), and the
2455
2626
  * library runs in every JavaScript runtime without depending on a crypto
2456
2627
  * 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.
2628
+ * stable text-to-bits mapping for identifier generation, and nothing
2629
+ * security relevant derives from it.
2459
2630
  *
2460
2631
  * @module
2461
2632
  */
@@ -2530,7 +2701,7 @@ const SHIFTS = [
2530
2701
  21
2531
2702
  ];
2532
2703
  /**
2533
- * Step constants for the 64 steps: the integer parts of
2704
+ * Step constants for the 64 steps, which are the integer parts of
2534
2705
  * `abs(sin(i + 1)) * 2^32`, as RFC 1321 section 3.4 tabulates them. The
2535
2706
  * values are fixed by the specification rather than derived through
2536
2707
  * `Math.sin` at load, because the digest feeds deterministic identifiers
@@ -2604,9 +2775,9 @@ const SINES = new Uint32Array([
2604
2775
  3951481745
2605
2776
  ]);
2606
2777
  /**
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.
2778
+ * Encodes text as UTF-8 bytes with RFC 1321 padding applied, meaning a
2779
+ * `0x80` terminator, zero fill to 56 bytes mod 64, then the bit length as
2780
+ * a little-endian 64-bit integer.
2610
2781
  */
2611
2782
  function paddedUtf8(text) {
2612
2783
  const bytes = new TextEncoder().encode(text);
@@ -2624,8 +2795,8 @@ function paddedUtf8(text) {
2624
2795
  /**
2625
2796
  * Adds two 32-bit values with wraparound, keeping intermediates inside the
2626
2797
  * 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.
2798
+ * a substitute here, because the bitwise coercion is what performs the
2799
+ * modular wrap the algorithm requires.
2629
2800
  */
2630
2801
  function add32(a, b) {
2631
2802
  return a + b | 0;
@@ -2697,7 +2868,7 @@ function md5Hex(text) {
2697
2868
  * Deterministic object identifiers for generated pbxproj objects.
2698
2869
  *
2699
2870
  * Xcode identifies every object with 24 hexadecimal characters. Generated
2700
- * ids here are deterministic: the same seed always produces the same id, so
2871
+ * ids here are deterministic. The same seed always produces the same id, so
2701
2872
  * programmatic edits are reproducible and diffs stay minimal across runs.
2702
2873
  * The format is `XX` + the first 20 characters of `md5(seed)` + `XX`, which
2703
2874
  * is a valid identifier that remains recognizable as generated.
@@ -2940,7 +3111,7 @@ function defaultConfigurationSettingsOf(project, configurationListId) {
2940
3111
  * extends it with products, embedding, synchronized folders, Swift
2941
3112
  * packages, and system frameworks.
2942
3113
  *
2943
- * Reads are deliberately soft: user-generated projects can be malformed,
3114
+ * Reads are deliberately soft. User-generated projects can be malformed,
2944
3115
  * so lookups return `undefined` instead of throwing wherever a document
2945
3116
  * could legally or illegally omit something. Mutations create any missing
2946
3117
  * structure they need.
@@ -2993,11 +3164,11 @@ var Target = class extends XcodeObject {
2993
3164
  *
2994
3165
  * Configurations based on `.xcconfig` files take part once the files
2995
3166
  * are registered through {@link XcodeProject.registerXcconfig}, in
2996
- * Xcode's order: target settings, the target's xcconfig, project
2997
- * settings, the project's xcconfig.
3167
+ * Xcode's order of target settings, then the target's xcconfig, then
3168
+ * project settings, then the project's xcconfig.
2998
3169
  *
2999
- * Only string values are returned; a list- or number-valued setting reads
3000
- * as `undefined`.
3170
+ * Only string values are returned, so a list- or number-valued setting
3171
+ * reads as `undefined`.
3001
3172
  */
3002
3173
  getBuildSetting(key) {
3003
3174
  const targetConfiguration = defaultConfigurationOf(this.project, this.getString("buildConfigurationList"));
@@ -3011,6 +3182,48 @@ var Target = class extends XcodeObject {
3011
3182
  return (projectConfiguration == null ? void 0 : this.project.xcconfigSettingsOf(projectConfiguration))?.[key];
3012
3183
  }
3013
3184
  /**
3185
+ * Reads a build setting like {@link getBuildSetting} and expands the
3186
+ * `$(NAME)` and `${NAME}` references in it. Referenced settings resolve
3187
+ * through the same layering, `$(inherited)` and a setting referencing
3188
+ * itself continue from the next layer down the way Xcode composes
3189
+ * values, and `$(TARGET_NAME)` falls back to the target's name, so the
3190
+ * common `PRODUCT_NAME = "$(TARGET_NAME)"` resolves without any caller
3191
+ * setup.
3192
+ *
3193
+ * References the model cannot resolve (build-system paths like
3194
+ * `$(BUILT_PRODUCTS_DIR)`, environment values) stay in the result
3195
+ * verbatim unless the caller's lookup answers them, so no information
3196
+ * is invented. See {@link expandBuildSettingReferences} for the
3197
+ * operator forms honored during expansion.
3198
+ */
3199
+ resolveBuildSetting(key, options) {
3200
+ const targetConfiguration = defaultConfigurationOf(this.project, this.getString("buildConfigurationList"));
3201
+ const projectConfiguration = defaultConfigurationOf(this.project, this.project.rootProject.getString("buildConfigurationList"));
3202
+ const layers = [
3203
+ asDictionary(targetConfiguration?.properties["buildSettings"]),
3204
+ targetConfiguration == null ? void 0 : this.project.xcconfigSettingsOf(targetConfiguration),
3205
+ asDictionary(projectConfiguration?.properties["buildSettings"]),
3206
+ projectConfiguration == null ? void 0 : this.project.xcconfigSettingsOf(projectConfiguration)
3207
+ ];
3208
+ const resolve = (name, fromLayer, active) => {
3209
+ const guard = `${fromLayer}:${name}`;
3210
+ if (active.has(guard)) return;
3211
+ const nested = new Set(active);
3212
+ nested.add(guard);
3213
+ for (let layer = fromLayer; layer < layers.length; layer++) {
3214
+ const settings = layers[layer];
3215
+ if (settings == null || !(name in settings)) continue;
3216
+ const value = asString(settings[name]);
3217
+ if (value == null) return;
3218
+ return expandBuildSettingReferences(value, (reference) => {
3219
+ if (reference === "inherited" || reference === name) return resolve(name, layer + 1, nested) ?? "";
3220
+ return resolve(reference, 0, nested) ?? options?.lookup?.(reference) ?? builtinSetting(this, reference);
3221
+ }, { expandLookupValues: false });
3222
+ }
3223
+ };
3224
+ return resolve(key, 0, /* @__PURE__ */ new Set());
3225
+ }
3226
+ /**
3014
3227
  * Writes a build setting on every configuration of the target, so Debug
3015
3228
  * and Release stay consistent.
3016
3229
  */
@@ -3124,6 +3337,15 @@ var Target = class extends XcodeObject {
3124
3337
  }
3125
3338
  };
3126
3339
  /**
3340
+ * The settings the build system defines from the target itself rather
3341
+ * than from the document. `TARGET_NAME` is the one programmatic reads
3342
+ * meet constantly, through the template's `PRODUCT_NAME` of
3343
+ * `$(TARGET_NAME)`.
3344
+ */
3345
+ function builtinSetting(target, name) {
3346
+ return name === "TARGET_NAME" ? target.name : void 0;
3347
+ }
3348
+ /**
3127
3349
  * A `PBXNativeTarget` is a product the project compiles and packages
3128
3350
  * itself, such as an application or an app extension.
3129
3351
  */
@@ -3372,7 +3594,7 @@ var LegacyTarget = class extends Target {
3372
3594
  * `project.pbxproj`.
3373
3595
  *
3374
3596
  * 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
3597
+ * All state lives in the document itself. Views hold only an id and a
3376
3598
  * project reference, so model mutations and direct dictionary writes
3377
3599
  * compose freely and {@link XcodeProject.build} always serializes the
3378
3600
  * current state. New objects receive deterministic identifiers (see
@@ -3402,7 +3624,7 @@ var RootProject = class extends XcodeObject {
3402
3624
  }
3403
3625
  /**
3404
3626
  * The settings dictionary of the project-level default configuration.
3405
- * Targets inherit from these settings; see
3627
+ * Targets inherit from these settings, as described on
3406
3628
  * {@link NativeTarget.getBuildSetting}.
3407
3629
  */
3408
3630
  defaultConfigurationSettings() {
@@ -3477,7 +3699,7 @@ var XcodeProject = class XcodeProject {
3477
3699
  }
3478
3700
  /**
3479
3701
  * Wraps an already parsed document in a model. The document is used in
3480
- * place, not copied; model mutations write into it.
3702
+ * place rather than copied, so model mutations write into it.
3481
3703
  */
3482
3704
  static fromDocument(document) {
3483
3705
  return new XcodeProject(document);
@@ -3492,7 +3714,7 @@ var XcodeProject = class XcodeProject {
3492
3714
  /**
3493
3715
  * The raw properties dictionary of an object.
3494
3716
  *
3495
- * @throws XcodeModelError when no object with the id exists; views use
3717
+ * @throws XcodeModelError when no object with the id exists. Views use
3496
3718
  * this accessor, so a view of a deleted object fails loudly instead of
3497
3719
  * resurrecting the entry.
3498
3720
  */
@@ -3535,8 +3757,8 @@ var XcodeProject = class XcodeProject {
3535
3757
  }
3536
3758
  /**
3537
3759
  * 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.
3760
+ * id the document currently contains. The id is not reserved, and only
3761
+ * adding an object with it (see {@link add}) claims it.
3540
3762
  */
3541
3763
  generateId(seed) {
3542
3764
  return generateObjectId(seed, new Set(Object.keys(this.objectsDictionary)));
@@ -3643,7 +3865,8 @@ var XcodeProject = class XcodeProject {
3643
3865
  * and Resources build phases, and registers it on the project.
3644
3866
  *
3645
3867
  * @throws XcodeModelError for product types the model cannot create a
3646
- * product reference for; see {@link AddNativeTargetOptions.productType}.
3868
+ * product reference for, listed on
3869
+ * {@link AddNativeTargetOptions.productType}.
3647
3870
  */
3648
3871
  addNativeTarget(options) {
3649
3872
  const productInfo = PRODUCT_FILE_INFO[options.productType];
@@ -3689,7 +3912,7 @@ var XcodeProject = class XcodeProject {
3689
3912
  /**
3690
3913
  * Adds a remote Swift package reference and registers it on the project.
3691
3914
  * When the project already references the repository, the existing
3692
- * reference is returned unchanged, requirement included; adjust an
3915
+ * reference is returned unchanged, requirement included. Adjust an
3693
3916
  * existing requirement through the reference's properties. Link products
3694
3917
  * to targets with {@link NativeTarget.addSwiftPackageProduct}.
3695
3918
  *
@@ -3764,7 +3987,7 @@ var XcodeProject = class XcodeProject {
3764
3987
  * list containing it, or a nested dictionary carrying it as a key or
3765
3988
  * string value.
3766
3989
  *
3767
- * The scan is linear over the document; removal flows call it once per
3990
+ * The scan is linear over the document. Removal flows call it once per
3768
3991
  * removed object, which keeps teardown proportional to what is actually
3769
3992
  * removed.
3770
3993
  */
@@ -3780,7 +4003,7 @@ var XcodeProject = class XcodeProject {
3780
4003
  * (such as `TargetAttributes`) drop its entry.
3781
4004
  *
3782
4005
  * 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
4006
+ * low-level removal, and {@link removeTarget} composes it into a full
3784
4007
  * teardown.
3785
4008
  */
3786
4009
  removeObject(id) {
@@ -3798,7 +4021,7 @@ var XcodeProject = class XcodeProject {
3798
4021
  * dependencies on others), its membership exception sets, and
3799
4022
  * synchronized folders no remaining target links.
3800
4023
  *
3801
- * On-disk sources are untouched; the removal is document-only, like
4024
+ * On-disk sources are untouched. The removal is document-only, like
3802
4025
  * deleting a target in Xcode and keeping its folder.
3803
4026
  *
3804
4027
  * @throws XcodeModelError when the target belongs to another project,
@@ -3841,12 +4064,58 @@ var XcodeProject = class XcodeProject {
3841
4064
  for (const groupId of syncGroupIds) if (!this.nativeTargets().some((remaining) => stringItems(remaining.properties["fileSystemSynchronizedGroups"]).includes(groupId))) this.removeObject(groupId);
3842
4065
  }
3843
4066
  /**
4067
+ * Renames a target and every place the document knows it by name. That
4068
+ * covers the target's `name` and `productName`, its product file
4069
+ * reference (`OldName.app` becomes `NewName.app`, whatever the
4070
+ * extension), the `remoteInfo` of container item proxies pointing at
4071
+ * the target, `TEST_TARGET_NAME` settings naming it, and the path
4072
+ * segments of `TEST_HOST` and `BUNDLE_LOADER` settings that name the
4073
+ * target or its product. A `PRODUCT_NAME` of the target's own
4074
+ * configurations is rewritten only when it spells the old name
4075
+ * literally. The usual `$(TARGET_NAME)` follows by itself.
4076
+ *
4077
+ * Scheme files live outside the pbxproj, so buildable references are
4078
+ * renamed separately through `Xcscheme.renameTarget`. On-disk renames
4079
+ * (source folders, entitlements files) and the group paths pointing at
4080
+ * those folders stay with the caller. Sibling targets such as
4081
+ * `OldNameTests` are renamed with their own calls.
4082
+ *
4083
+ * @throws XcodeModelError when the target belongs to another project.
4084
+ */
4085
+ renameTarget(target, newName) {
4086
+ if (target.project !== this) throw new XcodeModelError("Cannot rename a target that belongs to another project");
4087
+ const oldName = target.name;
4088
+ if (oldName === newName) return;
4089
+ target.set("name", newName);
4090
+ if (oldName == null) return;
4091
+ if (target.getString("productName") === oldName) target.set("productName", newName);
4092
+ const product = this.get(target.getString("productReference"));
4093
+ if (product != null) for (const key of ["path", "name"]) {
4094
+ const fileName = product.getString(key);
4095
+ const renamed = fileName == null ? void 0 : renameFileNameStem(fileName, oldName, newName);
4096
+ if (renamed != null) product.set(key, renamed);
4097
+ }
4098
+ for (const configuration of target.buildConfigurations()) {
4099
+ const settings = configuration.buildSettings;
4100
+ if (settings?.PRODUCT_NAME === oldName) settings.PRODUCT_NAME = newName;
4101
+ }
4102
+ for (const [, view] of this.objects()) {
4103
+ if (ContainerItemProxy.is(view) && view.getString("remoteGlobalIDString") === target.id && view.remoteInfo === oldName) view.set("remoteInfo", newName);
4104
+ if (BuildConfiguration.is(view)) {
4105
+ const settings = view.buildSettings;
4106
+ if (settings == null) continue;
4107
+ if (settings.TEST_TARGET_NAME === oldName) settings.TEST_TARGET_NAME = newName;
4108
+ renameHostPathSettings(settings, oldName, newName);
4109
+ }
4110
+ }
4111
+ }
4112
+ /**
3844
4113
  * Finds a file reference by its project-relative path, resolving each
3845
4114
  * reference's location through the group tree from the main group
3846
4115
  * (nested group `path` components join with `/`).
3847
4116
  *
3848
4117
  * Members of file-system-synchronized folders are not listed in the
3849
- * document and therefore cannot be found; check
4118
+ * document and therefore cannot be found. Check
3850
4119
  * {@link NativeTarget.syncGroupPaths} for folder-level containment
3851
4120
  * instead.
3852
4121
  */
@@ -3933,6 +4202,31 @@ function joinPath(prefix, segment) {
3933
4202
  return prefix === "" ? segment : `${prefix}/${segment}`;
3934
4203
  }
3935
4204
  /**
4205
+ * Renames the segments of a path-valued build setting that name the
4206
+ * target or one of its products. Settings like `TEST_HOST` embed the
4207
+ * product path as
4208
+ * `$(BUILT_PRODUCTS_DIR)/SampleApp.app/.../SampleApp`, so each segment
4209
+ * is matched whole against the target name. Substring occurrences inside
4210
+ * unrelated segments stay untouched.
4211
+ */
4212
+ function renamePathSegments(value, oldName, newName) {
4213
+ return value.split("/").map((segment) => renameFileNameStem(segment, oldName, newName) ?? segment).join("/");
4214
+ }
4215
+ /**
4216
+ * Renames the target name inside the host-path settings of one
4217
+ * configuration. `TEST_HOST` and `BUNDLE_LOADER` carry the hosting
4218
+ * product's path, so their segments rename the way the product file
4219
+ * reference does.
4220
+ */
4221
+ function renameHostPathSettings(settings, oldName, newName) {
4222
+ for (const key of ["TEST_HOST", "BUNDLE_LOADER"]) {
4223
+ const value = asString(settings[key]);
4224
+ if (value == null) continue;
4225
+ const rewritten = renamePathSegments(value, oldName, newName);
4226
+ if (rewritten !== value) settings[key] = rewritten;
4227
+ }
4228
+ }
4229
+ /**
3936
4230
  * Whether a value references the id anywhere in its structure. A
3937
4231
  * reference is a string equal to it, an array containing it at any depth,
3938
4232
  * or a dictionary carrying it as a key or somewhere in its values.
@@ -3978,7 +4272,7 @@ function stripValue(value, id) {
3978
4272
  return value;
3979
4273
  }
3980
4274
  /**
3981
- * Strips every reference to the id from an object's properties; see
4275
+ * Strips every reference to the id from an object's properties. See
3982
4276
  * {@link stripValue} for the shapes handled. String properties naming the
3983
4277
  * id are deleted rather than left empty.
3984
4278
  */
@@ -4530,6 +4824,46 @@ var Xcscheme = class Xcscheme {
4530
4824
  buildableReferences() {
4531
4825
  return this.elements("BuildableReference").map((element) => new BuildableReference(element));
4532
4826
  }
4827
+ /**
4828
+ * Renames every buildable reference pointing at a target. The blueprint
4829
+ * name is matched whole, and the buildable name is matched by its stem,
4830
+ * so `OldApp.app` becomes `NewApp.app` while `OldAppTests.xctest`, a
4831
+ * different target's product, stays untouched. This is the scheme-file
4832
+ * side of `XcodeProject.renameTarget`. Returns whether anything
4833
+ * changed, so callers can skip rewriting untouched files.
4834
+ */
4835
+ renameTarget(oldName, newName) {
4836
+ if (oldName === newName) return false;
4837
+ let changed = false;
4838
+ for (const reference of this.buildableReferences()) {
4839
+ if (reference.blueprintName === oldName) {
4840
+ reference.blueprintName = newName;
4841
+ changed = true;
4842
+ }
4843
+ const buildableName = reference.buildableName;
4844
+ const renamed = buildableName == null ? void 0 : renameFileNameStem(buildableName, oldName, newName);
4845
+ if (renamed != null) {
4846
+ reference.buildableName = renamed;
4847
+ changed = true;
4848
+ }
4849
+ }
4850
+ return changed;
4851
+ }
4852
+ /**
4853
+ * Rewrites every buildable reference's container after the
4854
+ * `.xcodeproj` directory itself is renamed, so `container:Old.xcodeproj`
4855
+ * becomes `container:New.xcodeproj`. The project names are matched
4856
+ * exactly. Returns whether anything changed.
4857
+ */
4858
+ renameContainer(oldProjectName, newProjectName) {
4859
+ if (oldProjectName === newProjectName) return false;
4860
+ let changed = false;
4861
+ for (const reference of this.buildableReferences()) if (reference.referencedContainer === `container:${oldProjectName}.xcodeproj`) {
4862
+ reference.referencedContainer = `container:${newProjectName}.xcodeproj`;
4863
+ changed = true;
4864
+ }
4865
+ return changed;
4866
+ }
4533
4867
  };
4534
4868
  /**
4535
4869
  * Creates the scheme Xcode writes for an application target. The
@@ -4664,8 +4998,8 @@ function buildXcconfig(document) {
4664
4998
  */
4665
4999
  const INCLUDE_PATTERN = /^#include(\?)?\s*"([^"]*)"\s*(?:\/\/.*)?$/u;
4666
5000
  /**
4667
- * Matches the head of an assignment: leading whitespace, the setting
4668
- * name, and the raw conditions block up to the equals sign.
5001
+ * Matches the head of an assignment, which is the leading whitespace, the
5002
+ * setting name, and the raw conditions block up to the equals sign.
4669
5003
  */
4670
5004
  const ASSIGNMENT_HEAD_PATTERN = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*((?:\[[^\]]*\])*)\s*=/u;
4671
5005
  /**
@@ -4777,7 +5111,7 @@ function parseXcconfig(source) {
4777
5111
  * Object model for Xcode build configuration files (`.xcconfig`).
4778
5112
  *
4779
5113
  * {@link Xcconfig} wraps a parsed document the way `XcodeProject` wraps a
4780
- * pbxproj: the document stays the single source of truth, reads and
5114
+ * pbxproj. The document stays the single source of truth, reads and
4781
5115
  * writes go through it, and {@link Xcconfig.build} emits it back with
4782
5116
  * untouched lines preserved byte for byte.
4783
5117
  *
@@ -4960,4 +5294,4 @@ var Xcconfig = class Xcconfig {
4960
5294
  }
4961
5295
  };
4962
5296
  //#endregion
4963
- export { AggregateTarget, AppleScriptBuildPhase, BuildConfiguration, BuildFile, BuildFileExceptionSet, BuildPhase, BuildPhaseMembershipExceptionSet, BuildRule, BuildStyle, BuildableReference, ConfigurationList, ContainerItemProxy, CopyFilesBuildPhase, CopyFilesDestination, ExceptionSet, FileReference, FrameworksBuildPhase, Group, HeadersBuildPhase, Isa, LegacyTarget, LocalSwiftPackageReference, NativeTarget, PbxprojBuildError, PbxprojParseError, ProductType, ReferenceProxy, RemoteSwiftPackageReference, ResourcesBuildPhase, RezBuildPhase, RootProject, ShellScriptBuildPhase, SourcesBuildPhase, SwiftPackageProductDependency, SwiftPackageReference, SyncRootGroup, Target, TargetDependency, VariantGroup, VersionGroup, Xcconfig, XcconfigParseError, XcodeModelError, XcodeObject, XcodeProject, Xcscheme, XcschemeBuildError, XcschemeParseError, buildPbxproj, buildXcconfig, buildXcscheme, createXcscheme, generateObjectId, isXcschemeElement, parseApplePlatform, parsePbxproj, parseXcconfig, parseXcscheme, xcschemeElements };
5297
+ export { AggregateTarget, AppleScriptBuildPhase, BuildConfiguration, BuildFile, BuildFileExceptionSet, BuildPhase, BuildPhaseMembershipExceptionSet, BuildRule, BuildStyle, BuildableReference, ConfigurationList, ContainerItemProxy, CopyFilesBuildPhase, CopyFilesDestination, ExceptionSet, FileReference, FrameworksBuildPhase, Group, HeadersBuildPhase, Isa, LegacyTarget, LocalSwiftPackageReference, NativeTarget, PbxprojBuildError, PbxprojParseError, ProductType, ReferenceProxy, RemoteSwiftPackageReference, ResourcesBuildPhase, RezBuildPhase, RootProject, ShellScriptBuildPhase, SourcesBuildPhase, SwiftPackageProductDependency, SwiftPackageReference, SyncRootGroup, Target, TargetDependency, VariantGroup, VersionGroup, Xcconfig, XcconfigParseError, XcodeModelError, XcodeObject, XcodeProject, Xcscheme, XcschemeBuildError, XcschemeParseError, buildPbxproj, buildXcconfig, buildXcscheme, createXcscheme, expandBuildSettingReferences, generateObjectId, isXcschemeElement, parseApplePlatform, parsePbxproj, parseXcconfig, parseXcscheme, xcschemeElements };