rork-xcode 0.8.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/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`.
@@ -283,6 +283,30 @@ var XcschemeParseError = class extends Error {
283
283
  }
284
284
  };
285
285
  /**
286
+ * Thrown when the source text is not a well-formed build configuration
287
+ * file (the line-based format of `.xcconfig` files).
288
+ *
289
+ * The message always embeds the line and column of the failure, and the
290
+ * same information is available in structured form on {@link position}
291
+ * for programmatic use.
292
+ */
293
+ var XcconfigParseError = class extends Error {
294
+ /** Where in the source text parsing failed. */
295
+ position;
296
+ /**
297
+ * @param message Failure description without location. The location is
298
+ * appended automatically.
299
+ * @param source Full source text, used to compute the position.
300
+ * @param offset Character offset of the failure inside `source`.
301
+ */
302
+ constructor(message, source, offset) {
303
+ const position = positionAt(source, offset);
304
+ super(`${message} (line ${position.line}, column ${position.column})`);
305
+ this.name = "XcconfigParseError";
306
+ this.position = position;
307
+ }
308
+ };
309
+ /**
286
310
  * Thrown when a scheme element cannot be written as XML.
287
311
  *
288
312
  * Raised for element and attribute names that are not valid XML names and
@@ -293,7 +317,7 @@ var XcschemeBuildError = class extends Error {
293
317
  /** Path to the offending element from the root, e.g. `Scheme.BuildAction[0]`. */
294
318
  path;
295
319
  /**
296
- * @param message Failure description without location; the element path
320
+ * @param message Failure description without location. The element path
297
321
  * is appended automatically.
298
322
  * @param path Path to the offending element from the root.
299
323
  */
@@ -304,7 +328,7 @@ var XcschemeBuildError = class extends Error {
304
328
  }
305
329
  };
306
330
  /**
307
- * Thrown by the object model when an operation cannot proceed: the
331
+ * Thrown by the object model when an operation cannot proceed, because the
308
332
  * document lacks the structure the operation needs (no objects dictionary,
309
333
  * no root project object), a view's object was removed from the document,
310
334
  * or a creation request names a product type the model cannot scaffold.
@@ -336,7 +360,7 @@ var PbxprojBuildError = class extends Error {
336
360
  /** Path to the offending value from the root, e.g. `$.objects.13B07F86.name`. */
337
361
  path;
338
362
  /**
339
- * @param message Failure description without location; the value path is
363
+ * @param message Failure description without location. The value path is
340
364
  * appended automatically.
341
365
  * @param path Path to the offending value from the root, `$`.
342
366
  */
@@ -363,8 +387,8 @@ var PbxprojBuildError = class extends Error {
363
387
  */
364
388
  const UNQUOTED_SAFE_PATTERN = /^[A-Za-z0-9_$/:.]+$/u;
365
389
  /**
366
- * Whether the value can render without quotes: every character is in the
367
- * 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.
368
392
  *
369
393
  * Quoting decisions scan every string a document carries, and many of those
370
394
  * strings are substring slices of the source text. The regex engine flattens
@@ -376,7 +400,7 @@ function isSafeUnquoted(value) {
376
400
  }
377
401
  /**
378
402
  * Whether the string contains characters that require escape sequences
379
- * inside a quoted string: control characters, `"`, `\`, or DEL.
403
+ * inside a quoted string, meaning control characters, `"`, `\`, or DEL.
380
404
  */
381
405
  function needsEscaping(value) {
382
406
  for (let i = 0; i < value.length; i++) {
@@ -386,8 +410,8 @@ function needsEscaping(value) {
386
410
  return false;
387
411
  }
388
412
  /**
389
- * Escapes special characters for a quoted string: the named C-style escapes
390
- * 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.
391
415
  */
392
416
  function escapeString(value) {
393
417
  let result = "";
@@ -439,7 +463,7 @@ function ensureQuotes(value) {
439
463
  return `"${escapeString(value)}"`;
440
464
  }
441
465
  /**
442
- * Renders binary data as an uppercase hex run: `<DEADBEEF>`.
466
+ * Renders binary data as an uppercase hex run, for example `<DEADBEEF>`.
443
467
  */
444
468
  function formatData(data) {
445
469
  let hex = "";
@@ -461,7 +485,7 @@ function formatData(data) {
461
485
  * - reference comments (`13B07F86… /* AppDelegate.swift in Sources *​/`)
462
486
  * derived from the object graph.
463
487
  *
464
- * Numbers render exactly as JavaScript formats them; the version-like
488
+ * Numbers render exactly as JavaScript formats them. The version-like
465
489
  * settings Xcode writes with a trailing zero (`SWIFT_VERSION = 5.0`) arrive
466
490
  * from the parser as strings and round-trip verbatim, so no reformatting
467
491
  * heuristic is needed or applied.
@@ -517,25 +541,25 @@ function indentString(depth) {
517
541
  /**
518
542
  * Serialization state for one {@link buildPbxproj} call.
519
543
  *
520
- * The `write*` methods append directly to the output string; the `render*`
521
- * methods return fragments for the caller to place. Output accumulates by
522
- * appending, because engines represent growing strings as ropes: appends
523
- * stay cheap where template interpolation would allocate an intermediate
524
- * 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.
525
549
  */
526
550
  var Writer = class {
527
551
  /** The document text accumulated so far. */
528
552
  out = "";
529
- /** Current nesting depth; one tab per level. */
553
+ /** Current nesting depth, one tab per level. */
530
554
  indent = 0;
531
555
  /** Display comment per referenced uuid, derived once from the object graph. */
532
556
  comments;
533
557
  /**
534
- * Rendered string values by input string: `id /* comment *​/` for
535
- * referenced uuids, quoted text for everything else. Referenced objects
536
- * render at least twice (their section entry plus each referencing site)
537
- * and build settings repeat across configurations, so most renders are
538
- * 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.
539
563
  */
540
564
  renderedReferences = /* @__PURE__ */ new Map();
541
565
  /**
@@ -544,7 +568,7 @@ var Writer = class {
544
568
  */
545
569
  quotedKeys = /* @__PURE__ */ new Map();
546
570
  /**
547
- * Serializes the whole document eagerly; read it back with
571
+ * Serializes the whole document eagerly. Read it back with
548
572
  * {@link toString}.
549
573
  *
550
574
  * @param root The document root dictionary.
@@ -582,7 +606,7 @@ var Writer = class {
582
606
  *
583
607
  * Most calls hit the cache, and the writers call this for every key and
584
608
  * reference, so the method body stays small enough for the engine to
585
- * inline; the miss path lives in {@link renderReferenceUncached}.
609
+ * inline. The miss path lives in {@link renderReferenceUncached}.
586
610
  */
587
611
  renderReference(id) {
588
612
  const cached = this.renderedReferences.get(id);
@@ -623,8 +647,8 @@ var Writer = class {
623
647
  * Renders a string value in its key's context.
624
648
  *
625
649
  * `remoteGlobalIDString` and `TestTargetID` hold uuids of objects in
626
- * another container; annotating them with this container's comments would
627
- * be wrong, so they render bare.
650
+ * another container. Annotating them with this container's comments
651
+ * would be wrong, so they render bare.
628
652
  */
629
653
  renderStringValue(key, value) {
630
654
  if (key === "remoteGlobalIDString" || key === "TestTargetID") return ensureQuotes(value);
@@ -634,7 +658,7 @@ var Writer = class {
634
658
  * Appends the entries of a dictionary, one `key = value;` line each.
635
659
  *
636
660
  * Value paths (`$.objects.AA….name`) exist for error messages and are
637
- * 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
638
662
  * and number lines that dominate documents skip the concatenation.
639
663
  *
640
664
  * @param object The dictionary whose entries to write.
@@ -773,18 +797,18 @@ var Writer = class {
773
797
  /**
774
798
  * Serializes a project document to `project.pbxproj` text.
775
799
  *
776
- * The input is the same shape {@link parsePbxproj} produces; see the module
777
- * 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
778
802
  * calls with semantically equal documents produce identical text, and the
779
803
  * layout matches what Xcode itself writes so diffs stay minimal.
780
804
  *
781
805
  * @param root The document root. Real project documents carry `objects`,
782
806
  * `rootObject`, and the version fields, but any dictionary serializes.
783
807
  * @returns The document text, terminated by a newline.
784
- * @throws PbxprojBuildError when a value has no pbxproj representation:
785
- * `null`, `undefined`, booleans, bigints, functions, symbols, class
786
- * instances, or non-finite numbers. The error names the path of the
787
- * 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.
788
812
  */
789
813
  function buildPbxproj(root) {
790
814
  if (!isDictionary(root)) throw new PbxprojBuildError("The document root must be a dictionary", "$");
@@ -795,15 +819,15 @@ function buildPbxproj(root) {
795
819
  /**
796
820
  * Names and well-known values of the pbxproj object vocabulary.
797
821
  *
798
- * Everything here mirrors strings Xcode itself writes; nothing is invented.
799
- * Centralizing them keeps the object model free of string literals and
800
- * 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.
801
825
  *
802
826
  * @module
803
827
  */
804
828
  /**
805
829
  * The `isa` names the object model works with. The parser and serializer
806
- * 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
807
831
  * gives typed access to.
808
832
  */
809
833
  const Isa = {
@@ -935,7 +959,7 @@ const CopyFilesDestination = {
935
959
  };
936
960
  /**
937
961
  * Resolves the embed phase destination for an extension-like product type.
938
- * Watch applications are embedded by product type here; callers that only
962
+ * Watch applications are embedded by product type here. Callers that only
939
963
  * know build settings should check the deployment-target key instead.
940
964
  */
941
965
  function embedDestinationFor(productType) {
@@ -1023,7 +1047,7 @@ function ensureArray(object, key) {
1023
1047
  * Collects the string items of a possibly absent, possibly mixed array.
1024
1048
  *
1025
1049
  * Reference lists in well-formed documents contain only id strings, but a
1026
- * malformed document can mix in anything; non-strings are skipped rather
1050
+ * malformed document can mix in anything. Non-strings are skipped rather
1027
1051
  * than thrown on, matching the library's soft-failure stance on reads.
1028
1052
  */
1029
1053
  function stringItems(value) {
@@ -1155,8 +1179,8 @@ var XcodeObject = class {
1155
1179
  * nested dictionaries keyed by object id (such as the root project's
1156
1180
  * `TargetAttributes`) drop its entry.
1157
1181
  *
1158
- * This is the low-level removal; it does not cascade to objects that only
1159
- * 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
1160
1184
  * {@link XcodeProject.removeTarget} compose it into full teardowns.
1161
1185
  */
1162
1186
  removeFromProject() {
@@ -1169,9 +1193,9 @@ var XcodeObject = class {
1169
1193
  * Typed views over the document object kinds that are not targets, from
1170
1194
  * groups and build phases to version groups and reference proxies.
1171
1195
  *
1172
- * Each class adds the accessors and mutations its kind supports; everything
1173
- * ultimately reads and writes the raw dictionaries through the base class,
1174
- * 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.
1175
1199
  *
1176
1200
  * @module
1177
1201
  */
@@ -1233,7 +1257,7 @@ var Group = class Group extends XcodeObject {
1233
1257
  * it to the group's children.
1234
1258
  *
1235
1259
  * The reference's `lastKnownFileType` derives from the file extension
1236
- * 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
1237
1261
  * Xcode re-derives one on open.
1238
1262
  *
1239
1263
  * @param path File path relative to the group, for example
@@ -1269,8 +1293,8 @@ var VariantGroup = class extends Group {
1269
1293
  var BuildPhase = class extends XcodeObject {
1270
1294
  /**
1271
1295
  * The phase's display name, when it carries an explicit one. Xcode names
1272
- * copy-files and shell-script phases; the standard phases derive their
1273
- * names from their isa.
1296
+ * copy-files and shell-script phases, while the standard phases derive
1297
+ * their names from their isa.
1274
1298
  */
1275
1299
  get name() {
1276
1300
  return this.getString("name");
@@ -1310,7 +1334,7 @@ var BuildPhase = class extends XcodeObject {
1310
1334
  * @param reference The file reference or package product the build file
1311
1335
  * should point at.
1312
1336
  * @param options.referenceKey Which build-file field carries the
1313
- * reference: `fileRef` for file references (the default) or
1337
+ * reference, either `fileRef` for file references (the default) or
1314
1338
  * `productRef` for Swift package products.
1315
1339
  * @param options.settings Optional per-file settings, for example
1316
1340
  * `{ ATTRIBUTES: ["RemoveHeadersOnCopy"] }`.
@@ -1423,7 +1447,7 @@ var BuildFile = class extends XcodeObject {
1423
1447
  /**
1424
1448
  * The view of the file reference the build file points at, when it
1425
1449
  * points at one. Build files for Swift package products carry a
1426
- * `productRef` instead; see {@link productDependency}.
1450
+ * `productRef` instead, resolved through {@link productDependency}.
1427
1451
  */
1428
1452
  fileReference() {
1429
1453
  return this.project.get(this.getString("fileRef"));
@@ -1456,12 +1480,12 @@ var SyncRootGroup = class extends XcodeObject {
1456
1480
  *
1457
1481
  * Xcode keeps one exception set per target and folder, so when this
1458
1482
  * group already carries a set for the target, the file names merge into
1459
- * it instead of creating a second set; names already excluded are not
1460
- * duplicated.
1483
+ * it instead of creating a second set, and names already excluded are
1484
+ * not duplicated.
1461
1485
  *
1462
1486
  * The standard use is keeping a scaffolded `Info.plist` from being
1463
- * double-copied: the build already processes it through the target's
1464
- * `INFOPLIST_FILE` setting.
1487
+ * double-copied, since the build already processes it through the
1488
+ * target's `INFOPLIST_FILE` setting.
1465
1489
  *
1466
1490
  * @param target The target whose membership the exceptions restrict.
1467
1491
  * @param membershipExceptions File names inside the folder to exclude.
@@ -1587,6 +1611,16 @@ var BuildConfiguration = class extends XcodeObject {
1587
1611
  get buildSettings() {
1588
1612
  return asDictionary(this.properties["buildSettings"]);
1589
1613
  }
1614
+ /**
1615
+ * The view of the `.xcconfig` file reference this configuration is
1616
+ * based on, when the configuration has one and it resolves. The file's
1617
+ * settings sit below the configuration's own `buildSettings` in
1618
+ * Xcode's resolution order.
1619
+ */
1620
+ baseConfigurationReference() {
1621
+ const view = this.project.get(this.getString("baseConfigurationReference"));
1622
+ return FileReference.is(view) ? view : void 0;
1623
+ }
1590
1624
  };
1591
1625
  /**
1592
1626
  * A `PBXBuildStyle` is the pre-Xcode-2 predecessor of
@@ -1782,9 +1816,10 @@ var ReferenceProxy = class extends XcodeObject {
1782
1816
  * NeXTSTEP character set for byte values 0x80-0xFF, indexed by `byte - 0x80`.
1783
1817
  *
1784
1818
  * Octal escapes are how pre-Unicode NeXTSTEP text encoded non-ASCII
1785
- * characters; mapping them through this table is what makes `\341` decode to
1786
- * `Æ` instead of the Latin-1 `á`. Values are Unicode code points, per the
1787
- * 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.
1788
1823
  */
1789
1824
  const NEXT_STEP_MAPPINGS = [
1790
1825
  160,
@@ -1919,8 +1954,8 @@ const NEXT_STEP_MAPPINGS = [
1919
1954
  /**
1920
1955
  * Maps an octal escape value to its Unicode code point.
1921
1956
  *
1922
- * Values below 0x80 are ASCII and pass through; values in 0x80-0xFF select
1923
- * 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.
1924
1959
  */
1925
1960
  function nextStepToUnicode(code) {
1926
1961
  if (code < 128 || code > 255) return code;
@@ -2029,7 +2064,8 @@ function unescapeString(input) {
2029
2064
  * @module
2030
2065
  */
2031
2066
  /**
2032
- * Characters allowed in unquoted string literals: `[A-Za-z0-9_$/:.-]`.
2067
+ * The characters allowed in unquoted string literals are
2068
+ * `[A-Za-z0-9_$/:.-]`.
2033
2069
  *
2034
2070
  * A 256-entry lookup table keyed by code unit keeps classification to a
2035
2071
  * single array read in the hot loop. Non-ASCII units index past the table
@@ -2090,7 +2126,7 @@ function isDigit(code) {
2090
2126
  * Scanner state and grammar productions for one parse call.
2091
2127
  *
2092
2128
  * The parser holds a single cursor into the source string and advances it
2093
- * through the `read*` and `parse*` methods; there is no separate tokenizer
2129
+ * through the `read*` and `parse*` methods. There is no separate tokenizer
2094
2130
  * stage and no token objects.
2095
2131
  */
2096
2132
  var Parser$1 = class {
@@ -2101,7 +2137,7 @@ var Parser$1 = class {
2101
2137
  /**
2102
2138
  * Offset of an unterminated block comment the trivia scanner consumed, or
2103
2139
  * -1. Recording it instead of throwing keeps the trivia scanner free of
2104
- * failure branches; see {@link fail}.
2140
+ * failure branches, and {@link fail} reports it.
2105
2141
  */
2106
2142
  unterminatedCommentAt = -1;
2107
2143
  /**
@@ -2115,13 +2151,13 @@ var Parser$1 = class {
2115
2151
  * failure.
2116
2152
  *
2117
2153
  * An unterminated block comment swallows the rest of the input, so any
2118
- * failure raised after one (always some end-of-input error) is a symptom;
2119
- * the comment itself is reported instead. Content after the root value is
2120
- * never scanned, so a trailing unterminated comment still parses, as
2121
- * 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.
2122
2158
  *
2123
2159
  * @param message Failure description without location.
2124
- * @param offset Offset of the failure; defaults to the current cursor.
2160
+ * @param offset Offset of the failure, defaulting to the current cursor.
2125
2161
  */
2126
2162
  fail(message, offset = this.pos) {
2127
2163
  if (this.unterminatedCommentAt !== -1) throw new PbxprojParseError("Unterminated block comment", this.input, this.unterminatedCommentAt);
@@ -2217,11 +2253,11 @@ var Parser$1 = class {
2217
2253
  return input.slice(start, pos);
2218
2254
  }
2219
2255
  /**
2220
- * 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.
2221
2257
  *
2222
- * The scan tracks whether any escape sequence occurred: unescaped strings
2223
- * (the overwhelming majority) return as a direct slice, and only escaped
2224
- * 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}.
2225
2261
  */
2226
2262
  readQuotedString() {
2227
2263
  const input = this.input;
@@ -2270,7 +2306,7 @@ var Parser$1 = class {
2270
2306
  return bytes;
2271
2307
  }
2272
2308
  /**
2273
- * Parses the document root: a dictionary or an array.
2309
+ * Parses the document root, which is a dictionary or an array.
2274
2310
  */
2275
2311
  parseDocument() {
2276
2312
  const code = this.peek();
@@ -2280,7 +2316,7 @@ var Parser$1 = class {
2280
2316
  this.fail(`Expected '{' or '(' at the start of the document but found '${this.input[this.pos]}'`);
2281
2317
  }
2282
2318
  /**
2283
- * Parses a `{ key = value; ... }` dictionary; the `{` is at the current
2319
+ * Parses a `{ key = value; ... }` dictionary whose `{` is at the current
2284
2320
  * position. Keys may be quoted or unquoted, and every entry requires the
2285
2321
  * `=` and terminating `;`.
2286
2322
  */
@@ -2314,9 +2350,9 @@ var Parser$1 = class {
2314
2350
  }
2315
2351
  }
2316
2352
  /**
2317
- * Parses a `( item, item, ... )` array; the `(` is at the current
2318
- * position. A trailing comma before `)` is allowed; Xcode writes one
2319
- * 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.
2320
2356
  */
2321
2357
  parseArray() {
2322
2358
  const input = this.input;
@@ -2363,12 +2399,12 @@ var Parser$1 = class {
2363
2399
  /**
2364
2400
  * Decides whether an unquoted literal is a number or a string.
2365
2401
  *
2366
- * One loop with an early exit: the first character outside `[0-9.]` (after
2367
- * an optional leading `-`) settles the token as a string, so the
2368
- * 24-character identifiers that dominate project documents are classified
2369
- * 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.
2370
2406
  *
2371
- * Numeric-looking candidates convert under a single print-back rule: the
2407
+ * Numeric-looking candidates convert under a single print-back rule. The
2372
2408
  * literal becomes a number exactly when the number formats back to the
2373
2409
  * identical text. Any literal the conversion would reshape stays a string,
2374
2410
  * so a parse and build cycle cannot change a single byte of any scalar.
@@ -2406,13 +2442,34 @@ function interpretLiteral(literal) {
2406
2442
  * @param text Source text of the document.
2407
2443
  * @returns The document's root value. For real project files this is the
2408
2444
  * root dictionary with `objects`, `rootObject`, and version fields.
2409
- * @throws PbxprojParseError when the document is malformed; the error
2445
+ * @throws PbxprojParseError when the document is malformed. The error
2410
2446
  * carries the line and column of the failure.
2411
2447
  */
2412
2448
  function parsePbxproj(text) {
2413
2449
  return new Parser$1(text).parseDocument();
2414
2450
  }
2415
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
2416
2473
  //#region src/md5.ts
2417
2474
  /**
2418
2475
  * Embedded MD5, implemented from RFC 1321.
@@ -2420,8 +2477,8 @@ function parsePbxproj(text) {
2420
2477
  * Deterministic object ids hash their seed text (see `uuid.ts`), and the
2421
2478
  * library runs in every JavaScript runtime without depending on a crypto
2422
2479
  * module, so the digest is implemented here. MD5 is used strictly as a
2423
- * stable text-to-bits mapping for identifier generation; nothing security
2424
- * relevant derives from it.
2480
+ * stable text-to-bits mapping for identifier generation, and nothing
2481
+ * security relevant derives from it.
2425
2482
  *
2426
2483
  * @module
2427
2484
  */
@@ -2496,7 +2553,7 @@ const SHIFTS = [
2496
2553
  21
2497
2554
  ];
2498
2555
  /**
2499
- * Step constants for the 64 steps: the integer parts of
2556
+ * Step constants for the 64 steps, which are the integer parts of
2500
2557
  * `abs(sin(i + 1)) * 2^32`, as RFC 1321 section 3.4 tabulates them. The
2501
2558
  * values are fixed by the specification rather than derived through
2502
2559
  * `Math.sin` at load, because the digest feeds deterministic identifiers
@@ -2570,9 +2627,9 @@ const SINES = new Uint32Array([
2570
2627
  3951481745
2571
2628
  ]);
2572
2629
  /**
2573
- * Encodes text as UTF-8 bytes with RFC 1321 padding applied: a `0x80`
2574
- * terminator, zero fill to 56 bytes mod 64, then the bit length as a
2575
- * 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.
2576
2633
  */
2577
2634
  function paddedUtf8(text) {
2578
2635
  const bytes = new TextEncoder().encode(text);
@@ -2590,8 +2647,8 @@ function paddedUtf8(text) {
2590
2647
  /**
2591
2648
  * Adds two 32-bit values with wraparound, keeping intermediates inside the
2592
2649
  * 32-bit range JavaScript bitwise operators preserve. `Math.trunc` is not
2593
- * a substitute here: the bitwise coercion is what performs the modular
2594
- * wrap the algorithm requires.
2650
+ * a substitute here, because the bitwise coercion is what performs the
2651
+ * modular wrap the algorithm requires.
2595
2652
  */
2596
2653
  function add32(a, b) {
2597
2654
  return a + b | 0;
@@ -2663,7 +2720,7 @@ function md5Hex(text) {
2663
2720
  * Deterministic object identifiers for generated pbxproj objects.
2664
2721
  *
2665
2722
  * Xcode identifies every object with 24 hexadecimal characters. Generated
2666
- * 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
2667
2724
  * programmatic edits are reproducible and diffs stay minimal across runs.
2668
2725
  * The format is `XX` + the first 20 characters of `md5(seed)` + `XX`, which
2669
2726
  * is a valid identifier that remains recognizable as generated.
@@ -2880,16 +2937,23 @@ function configurationsOf(project, configurationListId) {
2880
2937
  return configurations;
2881
2938
  }
2882
2939
  /**
2883
- * The settings dictionary of a configuration list's default configuration:
2884
- * the one named by `defaultConfigurationName`, falling back to the first
2885
- * configuration. Returns `undefined` when the list has no configurations
2886
- * or the default carries no settings dictionary.
2940
+ * The view of a configuration list's default configuration, which is the
2941
+ * one named by `defaultConfigurationName`, falling back to the first
2942
+ * configuration. Returns `undefined` when the list has no configurations.
2887
2943
  */
2888
- function defaultConfigurationSettingsOf(project, configurationListId) {
2944
+ function defaultConfigurationOf(project, configurationListId) {
2889
2945
  const list = asDictionary(project.propertiesOfOptional(configurationListId));
2890
2946
  const configurations = configurationsOf(project, configurationListId);
2891
2947
  const defaultName = asString(list?.["defaultConfigurationName"]);
2892
- return asDictionary((configurations.find((configuration) => configuration.getString("name") === defaultName) ?? configurations[0])?.properties["buildSettings"]);
2948
+ return configurations.find((configuration) => configuration.getString("name") === defaultName) ?? configurations[0];
2949
+ }
2950
+ /**
2951
+ * The settings dictionary of a configuration list's default configuration
2952
+ * (see {@link defaultConfigurationOf}). Returns `undefined` when the list
2953
+ * has no configurations or the default carries no settings dictionary.
2954
+ */
2955
+ function defaultConfigurationSettingsOf(project, configurationListId) {
2956
+ return asDictionary(defaultConfigurationOf(project, configurationListId)?.properties["buildSettings"]);
2893
2957
  }
2894
2958
  //#endregion
2895
2959
  //#region src/model/target.ts
@@ -2899,7 +2963,7 @@ function defaultConfigurationSettingsOf(project, configurationListId) {
2899
2963
  * extends it with products, embedding, synchronized folders, Swift
2900
2964
  * packages, and system frameworks.
2901
2965
  *
2902
- * Reads are deliberately soft: user-generated projects can be malformed,
2966
+ * Reads are deliberately soft. User-generated projects can be malformed,
2903
2967
  * so lookups return `undefined` instead of throwing wherever a document
2904
2968
  * could legally or illegally omit something. Mutations create any missing
2905
2969
  * structure they need.
@@ -2950,13 +3014,24 @@ var Target = class extends XcodeObject {
2950
3014
  * generated app templates set values like `SDKROOT` only at the project
2951
3015
  * level.
2952
3016
  *
2953
- * Only string values are returned; a list- or number-valued setting reads
2954
- * as `undefined`.
3017
+ * Configurations based on `.xcconfig` files take part once the files
3018
+ * are registered through {@link XcodeProject.registerXcconfig}, in
3019
+ * Xcode's order of target settings, then the target's xcconfig, then
3020
+ * project settings, then the project's xcconfig.
3021
+ *
3022
+ * Only string values are returned, so a list- or number-valued setting
3023
+ * reads as `undefined`.
2955
3024
  */
2956
3025
  getBuildSetting(key) {
2957
- const targetSettings = this.defaultConfigurationSettings();
3026
+ const targetConfiguration = defaultConfigurationOf(this.project, this.getString("buildConfigurationList"));
3027
+ const targetSettings = asDictionary(targetConfiguration?.properties["buildSettings"]);
2958
3028
  if (targetSettings != null && key in targetSettings) return asString(targetSettings[key]);
2959
- return asString(this.project.rootProject.defaultConfigurationSettings()?.[key]);
3029
+ const targetXcconfig = targetConfiguration == null ? void 0 : this.project.xcconfigSettingsOf(targetConfiguration);
3030
+ if (targetXcconfig != null && key in targetXcconfig) return targetXcconfig[key];
3031
+ const projectConfiguration = defaultConfigurationOf(this.project, this.project.rootProject.getString("buildConfigurationList"));
3032
+ const projectSettings = asDictionary(projectConfiguration?.properties["buildSettings"]);
3033
+ if (projectSettings != null && key in projectSettings) return asString(projectSettings[key]);
3034
+ return (projectConfiguration == null ? void 0 : this.project.xcconfigSettingsOf(projectConfiguration))?.[key];
2960
3035
  }
2961
3036
  /**
2962
3037
  * Writes a build setting on every configuration of the target, so Debug
@@ -3320,7 +3395,7 @@ var LegacyTarget = class extends Target {
3320
3395
  * `project.pbxproj`.
3321
3396
  *
3322
3397
  * The model is a set of lightweight views over the plain parsed document.
3323
- * 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
3324
3399
  * project reference, so model mutations and direct dictionary writes
3325
3400
  * compose freely and {@link XcodeProject.build} always serializes the
3326
3401
  * current state. New objects receive deterministic identifiers (see
@@ -3350,7 +3425,7 @@ var RootProject = class extends XcodeObject {
3350
3425
  }
3351
3426
  /**
3352
3427
  * The settings dictionary of the project-level default configuration.
3353
- * Targets inherit from these settings; see
3428
+ * Targets inherit from these settings, as described on
3354
3429
  * {@link NativeTarget.getBuildSetting}.
3355
3430
  */
3356
3431
  defaultConfigurationSettings() {
@@ -3400,6 +3475,12 @@ var XcodeProject = class XcodeProject {
3400
3475
  * access, so views of the same object compare with `===`.
3401
3476
  */
3402
3477
  views = /* @__PURE__ */ new Map();
3478
+ /**
3479
+ * Flattened settings of registered `.xcconfig` files, keyed by the id
3480
+ * of the file reference configurations name in
3481
+ * `baseConfigurationReference`.
3482
+ */
3483
+ xcconfigSettings = /* @__PURE__ */ new Map();
3403
3484
  constructor(document) {
3404
3485
  const objects = asDictionary(document["objects"]);
3405
3486
  if (objects == null) throw new XcodeModelError("The document has no objects dictionary");
@@ -3419,7 +3500,7 @@ var XcodeProject = class XcodeProject {
3419
3500
  }
3420
3501
  /**
3421
3502
  * Wraps an already parsed document in a model. The document is used in
3422
- * place, not copied; model mutations write into it.
3503
+ * place rather than copied, so model mutations write into it.
3423
3504
  */
3424
3505
  static fromDocument(document) {
3425
3506
  return new XcodeProject(document);
@@ -3434,7 +3515,7 @@ var XcodeProject = class XcodeProject {
3434
3515
  /**
3435
3516
  * The raw properties dictionary of an object.
3436
3517
  *
3437
- * @throws XcodeModelError when no object with the id exists; views use
3518
+ * @throws XcodeModelError when no object with the id exists. Views use
3438
3519
  * this accessor, so a view of a deleted object fails loudly instead of
3439
3520
  * resurrecting the entry.
3440
3521
  */
@@ -3477,8 +3558,8 @@ var XcodeProject = class XcodeProject {
3477
3558
  }
3478
3559
  /**
3479
3560
  * Generates a deterministic 24-character id from a seed, avoiding every
3480
- * id the document currently contains. The id is not reserved; adding an
3481
- * 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.
3482
3563
  */
3483
3564
  generateId(seed) {
3484
3565
  return generateObjectId(seed, new Set(Object.keys(this.objectsDictionary)));
@@ -3508,6 +3589,27 @@ var XcodeProject = class XcodeProject {
3508
3589
  return view;
3509
3590
  }
3510
3591
  /**
3592
+ * Registers the contents of a `.xcconfig` file so build-setting reads
3593
+ * can layer it below the configurations that are based on it. The
3594
+ * library never touches the filesystem, so the caller loads the file
3595
+ * and hands it over together with the file reference that
3596
+ * configurations name in `baseConfigurationReference`. Included files
3597
+ * take part through {@link XcconfigSettingsOptions.resolveInclude};
3598
+ * the settings are flattened once, at registration.
3599
+ */
3600
+ registerXcconfig(reference, config, options = {}) {
3601
+ this.xcconfigSettings.set(reference.id, config.settings(options));
3602
+ }
3603
+ /**
3604
+ * The flattened settings registered for the `.xcconfig` file a
3605
+ * configuration is based on, or `undefined` when the configuration
3606
+ * names none or the file was not registered.
3607
+ */
3608
+ xcconfigSettingsOf(configuration) {
3609
+ const referenceId = configuration.getString("baseConfigurationReference");
3610
+ return referenceId == null ? void 0 : this.xcconfigSettings.get(referenceId);
3611
+ }
3612
+ /**
3511
3613
  * The view of the document's root `PBXProject` object.
3512
3614
  *
3513
3615
  * @throws XcodeModelError when `rootObject` is missing or dangling,
@@ -3564,7 +3666,8 @@ var XcodeProject = class XcodeProject {
3564
3666
  * and Resources build phases, and registers it on the project.
3565
3667
  *
3566
3668
  * @throws XcodeModelError for product types the model cannot create a
3567
- * product reference for; see {@link AddNativeTargetOptions.productType}.
3669
+ * product reference for, listed on
3670
+ * {@link AddNativeTargetOptions.productType}.
3568
3671
  */
3569
3672
  addNativeTarget(options) {
3570
3673
  const productInfo = PRODUCT_FILE_INFO[options.productType];
@@ -3610,7 +3713,7 @@ var XcodeProject = class XcodeProject {
3610
3713
  /**
3611
3714
  * Adds a remote Swift package reference and registers it on the project.
3612
3715
  * When the project already references the repository, the existing
3613
- * reference is returned unchanged, requirement included; adjust an
3716
+ * reference is returned unchanged, requirement included. Adjust an
3614
3717
  * existing requirement through the reference's properties. Link products
3615
3718
  * to targets with {@link NativeTarget.addSwiftPackageProduct}.
3616
3719
  *
@@ -3685,7 +3788,7 @@ var XcodeProject = class XcodeProject {
3685
3788
  * list containing it, or a nested dictionary carrying it as a key or
3686
3789
  * string value.
3687
3790
  *
3688
- * 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
3689
3792
  * removed object, which keeps teardown proportional to what is actually
3690
3793
  * removed.
3691
3794
  */
@@ -3701,7 +3804,7 @@ var XcodeProject = class XcodeProject {
3701
3804
  * (such as `TargetAttributes`) drop its entry.
3702
3805
  *
3703
3806
  * Removing an id the document does not contain is a no-op. This is the
3704
- * low-level removal; {@link removeTarget} composes it into a full
3807
+ * low-level removal, and {@link removeTarget} composes it into a full
3705
3808
  * teardown.
3706
3809
  */
3707
3810
  removeObject(id) {
@@ -3719,7 +3822,7 @@ var XcodeProject = class XcodeProject {
3719
3822
  * dependencies on others), its membership exception sets, and
3720
3823
  * synchronized folders no remaining target links.
3721
3824
  *
3722
- * On-disk sources are untouched; the removal is document-only, like
3825
+ * On-disk sources are untouched. The removal is document-only, like
3723
3826
  * deleting a target in Xcode and keeping its folder.
3724
3827
  *
3725
3828
  * @throws XcodeModelError when the target belongs to another project,
@@ -3762,12 +3865,58 @@ var XcodeProject = class XcodeProject {
3762
3865
  for (const groupId of syncGroupIds) if (!this.nativeTargets().some((remaining) => stringItems(remaining.properties["fileSystemSynchronizedGroups"]).includes(groupId))) this.removeObject(groupId);
3763
3866
  }
3764
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
+ /**
3765
3914
  * Finds a file reference by its project-relative path, resolving each
3766
3915
  * reference's location through the group tree from the main group
3767
3916
  * (nested group `path` components join with `/`).
3768
3917
  *
3769
3918
  * Members of file-system-synchronized folders are not listed in the
3770
- * document and therefore cannot be found; check
3919
+ * document and therefore cannot be found. Check
3771
3920
  * {@link NativeTarget.syncGroupPaths} for folder-level containment
3772
3921
  * instead.
3773
3922
  */
@@ -3854,6 +4003,31 @@ function joinPath(prefix, segment) {
3854
4003
  return prefix === "" ? segment : `${prefix}/${segment}`;
3855
4004
  }
3856
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
+ /**
3857
4031
  * Whether a value references the id anywhere in its structure. A
3858
4032
  * reference is a string equal to it, an array containing it at any depth,
3859
4033
  * or a dictionary carrying it as a key or somewhere in its values.
@@ -3899,7 +4073,7 @@ function stripValue(value, id) {
3899
4073
  return value;
3900
4074
  }
3901
4075
  /**
3902
- * 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
3903
4077
  * {@link stripValue} for the shapes handled. String properties naming the
3904
4078
  * id are deleted rather than left empty.
3905
4079
  */
@@ -4451,6 +4625,46 @@ var Xcscheme = class Xcscheme {
4451
4625
  buildableReferences() {
4452
4626
  return this.elements("BuildableReference").map((element) => new BuildableReference(element));
4453
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
+ }
4454
4668
  };
4455
4669
  /**
4456
4670
  * Creates the scheme Xcode writes for an application target. The
@@ -4557,4 +4771,328 @@ function createXcscheme(options) {
4557
4771
  };
4558
4772
  }
4559
4773
  //#endregion
4560
- 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, XcodeModelError, XcodeObject, XcodeProject, Xcscheme, XcschemeBuildError, XcschemeParseError, buildPbxproj, buildXcscheme, createXcscheme, generateObjectId, isXcschemeElement, parseApplePlatform, parsePbxproj, parseXcscheme, xcschemeElements };
4774
+ //#region src/xcconfig/build.ts
4775
+ /**
4776
+ * Serializes a document back to `.xcconfig` text.
4777
+ */
4778
+ function buildXcconfig(document) {
4779
+ let text = "";
4780
+ for (const statement of document.statements) text += statement.raw + statement.eol;
4781
+ return text;
4782
+ }
4783
+ //#endregion
4784
+ //#region src/xcconfig/parse.ts
4785
+ /**
4786
+ * Parser for Xcode build configuration files (`.xcconfig`).
4787
+ *
4788
+ * The grammar is line based. A line is blank, a `//` comment, an
4789
+ * `#include "path"` directive, or a `KEY[conditions] = value` assignment.
4790
+ * `//` starts a comment anywhere on a line, including inside values, which
4791
+ * matches Xcode's reading of the format. Every parsed statement keeps its
4792
+ * exact source text, so an untouched document rebuilds byte for byte.
4793
+ *
4794
+ * @module
4795
+ */
4796
+ /**
4797
+ * Matches `#include "path"` and `#include? "path"`, with an optional
4798
+ * trailing comment.
4799
+ */
4800
+ const INCLUDE_PATTERN = /^#include(\?)?\s*"([^"]*)"\s*(?:\/\/.*)?$/u;
4801
+ /**
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.
4804
+ */
4805
+ const ASSIGNMENT_HEAD_PATTERN = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*((?:\[[^\]]*\])*)\s*=/u;
4806
+ /**
4807
+ * Matches one `[name=value]` condition group inside the conditions block.
4808
+ */
4809
+ const CONDITION_PATTERN = /\[([^=\]]+)=([^\]]*)\]/gu;
4810
+ /**
4811
+ * Strips the trailing comment, surrounding whitespace, and one trailing
4812
+ * semicolon from an assignment's right-hand side. Xcode tolerates the
4813
+ * semicolon as a leftover from property-list habits and ignores it.
4814
+ */
4815
+ function cleanValue(rightHandSide) {
4816
+ const commentStart = rightHandSide.indexOf("//");
4817
+ const trimmed = (commentStart === -1 ? rightHandSide : rightHandSide.slice(0, commentStart)).trim();
4818
+ return trimmed.endsWith(";") ? trimmed.slice(0, -1).trimEnd() : trimmed;
4819
+ }
4820
+ /**
4821
+ * Parses the conditions block of an assignment, for example
4822
+ * `[sdk=iphoneos*][arch=arm64]`.
4823
+ *
4824
+ * @throws XcconfigParseError when the block has content that is not a
4825
+ * well-formed `[name=value]` sequence.
4826
+ */
4827
+ function parseConditions(block, source, blockOffset) {
4828
+ const conditions = [];
4829
+ let consumed = 0;
4830
+ for (const match of block.matchAll(CONDITION_PATTERN)) {
4831
+ if (match.index !== consumed) throw new XcconfigParseError("Malformed setting condition", source, blockOffset + consumed);
4832
+ conditions.push({
4833
+ name: match[1].trim(),
4834
+ value: match[2].trim()
4835
+ });
4836
+ consumed = match.index + match[0].length;
4837
+ }
4838
+ if (consumed !== block.length) throw new XcconfigParseError("Malformed setting condition", source, blockOffset + consumed);
4839
+ return conditions;
4840
+ }
4841
+ /**
4842
+ * Parses one line into a statement.
4843
+ *
4844
+ * @param content The line without its terminator.
4845
+ * @param source The full source text, for error positions.
4846
+ * @param lineOffset Offset of the line inside `source`.
4847
+ */
4848
+ function parseLine(content, eol, source, lineOffset) {
4849
+ const trimmed = content.trim();
4850
+ if (trimmed === "") return {
4851
+ kind: "blank",
4852
+ raw: content,
4853
+ eol
4854
+ };
4855
+ if (trimmed.startsWith("//")) return {
4856
+ kind: "comment",
4857
+ text: trimmed.slice(2),
4858
+ raw: content,
4859
+ eol
4860
+ };
4861
+ if (trimmed.startsWith("#")) {
4862
+ const include = INCLUDE_PATTERN.exec(trimmed);
4863
+ if (include == null) throw new XcconfigParseError("Malformed #include directive", source, lineOffset + content.indexOf("#"));
4864
+ return {
4865
+ kind: "include",
4866
+ path: include[2],
4867
+ optional: include[1] === "?",
4868
+ raw: content,
4869
+ eol
4870
+ };
4871
+ }
4872
+ const head = ASSIGNMENT_HEAD_PATTERN.exec(content);
4873
+ if (head == null) throw new XcconfigParseError("Expected a setting assignment, an #include directive, or a // comment", source, lineOffset + (content.length - content.trimStart().length));
4874
+ const conditionsBlock = head[2];
4875
+ const conditionsOffset = lineOffset + head[0].indexOf(conditionsBlock, head[1].length);
4876
+ const conditions = conditionsBlock === "" ? [] : parseConditions(conditionsBlock, source, conditionsOffset);
4877
+ return {
4878
+ kind: "assignment",
4879
+ key: head[1],
4880
+ conditions,
4881
+ value: cleanValue(content.slice(head[0].length)),
4882
+ raw: content,
4883
+ eol
4884
+ };
4885
+ }
4886
+ /**
4887
+ * Parses `.xcconfig` text into its document form.
4888
+ *
4889
+ * @throws XcconfigParseError when a line is not a blank, a comment, an
4890
+ * include, or a well-formed assignment. The error carries the line and
4891
+ * column of the failure.
4892
+ */
4893
+ function parseXcconfig(source) {
4894
+ if (source === "") return { statements: [] };
4895
+ const statements = [];
4896
+ let offset = 0;
4897
+ while (offset < source.length) {
4898
+ const lineFeed = source.indexOf("\n", offset);
4899
+ const end = lineFeed === -1 ? source.length : lineFeed;
4900
+ const hasCarriageReturn = end > offset && source.charCodeAt(end - 1) === 13;
4901
+ const content = source.slice(offset, hasCarriageReturn ? end - 1 : end);
4902
+ const eol = lineFeed === -1 ? "" : hasCarriageReturn ? "\r\n" : "\n";
4903
+ statements.push(parseLine(content, eol, source, offset));
4904
+ if (lineFeed === -1) break;
4905
+ offset = lineFeed + 1;
4906
+ }
4907
+ return { statements };
4908
+ }
4909
+ //#endregion
4910
+ //#region src/xcconfig/model.ts
4911
+ /**
4912
+ * Object model for Xcode build configuration files (`.xcconfig`).
4913
+ *
4914
+ * {@link Xcconfig} wraps a parsed document the way `XcodeProject` wraps a
4915
+ * pbxproj. The document stays the single source of truth, reads and
4916
+ * writes go through it, and {@link Xcconfig.build} emits it back with
4917
+ * untouched lines preserved byte for byte.
4918
+ *
4919
+ * @module
4920
+ */
4921
+ /**
4922
+ * Whether a condition value matches a context value. A bare `*` matches
4923
+ * anything, a trailing `*` matches by prefix, and anything else matches
4924
+ * exactly.
4925
+ */
4926
+ function matchesCondition(conditionValue, contextValue) {
4927
+ if (conditionValue === "*") return true;
4928
+ if (conditionValue.endsWith("*")) return contextValue.startsWith(conditionValue.slice(0, -1));
4929
+ return conditionValue === contextValue;
4930
+ }
4931
+ /**
4932
+ * Splices a prior value into `$(inherited)` and `${inherited}`
4933
+ * references. With no prior value the references stay literal, because
4934
+ * they then refer to layers below the file chain, which resolve later.
4935
+ */
4936
+ function spliceInherited(value, prior) {
4937
+ if (prior == null) return value;
4938
+ return value.replaceAll("$(inherited)", prior).replaceAll("${inherited}", prior);
4939
+ }
4940
+ /**
4941
+ * A build configuration file with typed, mutable access to its settings.
4942
+ *
4943
+ * ```ts
4944
+ * const config = Xcconfig.parse(text);
4945
+ * config.get("PRODUCT_BUNDLE_IDENTIFIER");
4946
+ * config.set("MARKETING_VERSION", "1.2.0");
4947
+ * const updated = config.build();
4948
+ * ```
4949
+ */
4950
+ var Xcconfig = class Xcconfig {
4951
+ /** The parsed document this model wraps. */
4952
+ document;
4953
+ constructor(document) {
4954
+ this.document = document;
4955
+ }
4956
+ /**
4957
+ * Parses `.xcconfig` text and wraps it in a model.
4958
+ *
4959
+ * @throws XcconfigParseError when the text is malformed.
4960
+ */
4961
+ static parse(text) {
4962
+ return new Xcconfig(parseXcconfig(text));
4963
+ }
4964
+ /**
4965
+ * Creates an empty configuration file.
4966
+ */
4967
+ static create() {
4968
+ return new Xcconfig({ statements: [] });
4969
+ }
4970
+ /**
4971
+ * Serializes the current document state to `.xcconfig` text.
4972
+ */
4973
+ build() {
4974
+ return buildXcconfig(this.document);
4975
+ }
4976
+ /**
4977
+ * The names of the settings this file assigns unconditionally, in
4978
+ * first-assignment order. Conditional assignments such as
4979
+ * `KEY[sdk=iphoneos*]` are reachable through {@link assignments}.
4980
+ */
4981
+ keys() {
4982
+ const keys = [];
4983
+ for (const statement of this.document.statements) if (statement.kind === "assignment" && statement.conditions.length === 0 && !keys.includes(statement.key)) keys.push(statement.key);
4984
+ return keys;
4985
+ }
4986
+ /**
4987
+ * Every assignment of this file, conditional ones included, in
4988
+ * document order. The items are the document's own nodes, not copies,
4989
+ * so treat them as read-only views. Writes belong on {@link set},
4990
+ * which keeps a statement's value and its source text in step.
4991
+ */
4992
+ assignments() {
4993
+ return this.document.statements.filter((statement) => statement.kind === "assignment");
4994
+ }
4995
+ /**
4996
+ * The `#include` directives of this file, in document order. The items
4997
+ * are the document's own nodes, not copies, so treat them as read-only
4998
+ * views.
4999
+ */
5000
+ includes() {
5001
+ return this.document.statements.filter((statement) => statement.kind === "include");
5002
+ }
5003
+ /**
5004
+ * Reads the value of a setting from this file alone. When the key is
5005
+ * assigned more than once the last assignment wins, matching how Xcode
5006
+ * reads the file top to bottom. Conditional assignments are ignored.
5007
+ */
5008
+ get(key) {
5009
+ let value;
5010
+ for (const statement of this.document.statements) if (statement.kind === "assignment" && statement.key === key && statement.conditions.length === 0) value = statement.value;
5011
+ return value;
5012
+ }
5013
+ /**
5014
+ * Writes a setting. The last unconditional assignment of the key is
5015
+ * rewritten in place as a canonical `KEY = value` line, replacing any
5016
+ * trailing comment the line carried. A key the file does not assign
5017
+ * yet is appended at the end, following the document's line-ending
5018
+ * convention.
5019
+ */
5020
+ set(key, value) {
5021
+ let target;
5022
+ for (const statement of this.document.statements) if (statement.kind === "assignment" && statement.key === key && statement.conditions.length === 0) target = statement;
5023
+ if (target != null) {
5024
+ target.value = value;
5025
+ target.raw = `${key} = ${value}`;
5026
+ return;
5027
+ }
5028
+ const eol = this.document.statements.find((statement) => statement.eol !== "")?.eol ?? "\n";
5029
+ const last = this.document.statements.at(-1);
5030
+ if (last != null && last.eol === "") last.eol = eol;
5031
+ this.document.statements.push({
5032
+ kind: "assignment",
5033
+ key,
5034
+ conditions: [],
5035
+ value,
5036
+ raw: `${key} = ${value}`,
5037
+ eol
5038
+ });
5039
+ }
5040
+ /**
5041
+ * Removes every unconditional assignment of a setting.
5042
+ *
5043
+ * @returns True when at least one assignment was removed.
5044
+ */
5045
+ remove(key) {
5046
+ const kept = this.document.statements.filter((statement) => statement.kind !== "assignment" || statement.key !== key || statement.conditions.length > 0);
5047
+ const removed = kept.length !== this.document.statements.length;
5048
+ this.document.statements = kept;
5049
+ return removed;
5050
+ }
5051
+ /**
5052
+ * Flattens the file into a settings dictionary the way Xcode reads it:
5053
+ * top to bottom with later assignments winning, and every `#include`
5054
+ * contributing its settings at the point of the directive, so lines
5055
+ * after an include override it.
5056
+ *
5057
+ * Conditional assignments apply when every condition matches
5058
+ * {@link XcconfigSettingsOptions.context}; without a context they are
5059
+ * skipped. `$(inherited)` references splice in the value accumulated
5060
+ * earlier in the chain, and stay literal when there is none, since
5061
+ * they then refer to layers below the file, which resolve later.
5062
+ *
5063
+ * Includes only take part when {@link XcconfigSettingsOptions.resolveInclude}
5064
+ * is provided, since the library never touches the filesystem itself.
5065
+ * A file included again later re-applies, exactly like pasting its text
5066
+ * a second time. Only re-entry while a file is still being expanded is
5067
+ * skipped, tracked by include path and by instance, so cyclic includes
5068
+ * terminate even when the resolver parses a fresh instance per call.
5069
+ */
5070
+ settings(options = {}) {
5071
+ const merged = {};
5072
+ const pathStack = /* @__PURE__ */ new Set();
5073
+ const instanceStack = /* @__PURE__ */ new Set();
5074
+ const context = options.context;
5075
+ const applies = (statement) => statement.conditions.every((condition) => {
5076
+ const contextValue = condition.name === "sdk" || condition.name === "arch" || condition.name === "config" ? context?.[condition.name] : void 0;
5077
+ return contextValue != null && matchesCondition(condition.value, contextValue);
5078
+ });
5079
+ const visit = (config) => {
5080
+ if (instanceStack.has(config)) return;
5081
+ instanceStack.add(config);
5082
+ for (const statement of config.document.statements) if (statement.kind === "include") {
5083
+ if (pathStack.has(statement.path)) continue;
5084
+ const included = options.resolveInclude?.(statement.path, statement.optional);
5085
+ if (included != null) {
5086
+ pathStack.add(statement.path);
5087
+ visit(included);
5088
+ pathStack.delete(statement.path);
5089
+ }
5090
+ } else if (statement.kind === "assignment" && applies(statement)) merged[statement.key] = spliceInherited(statement.value, merged[statement.key]);
5091
+ instanceStack.delete(config);
5092
+ };
5093
+ visit(this);
5094
+ return merged;
5095
+ }
5096
+ };
5097
+ //#endregion
5098
+ 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 };