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/README.md +45 -13
- package/dist/index.d.ts +185 -54
- package/dist/index.js +456 -122
- package/package.json +1 -1
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
|
|
56
|
-
* Derivation is linear over the object graph
|
|
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
|
|
104
|
-
* target`. Falls back to the isa when the folder or target cannot
|
|
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
|
|
118
|
-
* phase from "Tophat" target`. Falls back to the isa when
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
545
|
-
* methods return fragments for the caller to place. Output
|
|
546
|
-
* appending, because engines represent growing strings as
|
|
547
|
-
* stay cheap where template interpolation would allocate
|
|
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
|
|
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
|
|
559
|
-
*
|
|
560
|
-
* render at least twice (their section entry plus each
|
|
561
|
-
* and build settings repeat across configurations, so
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
801
|
-
* documentation of `types.ts` for the value model. Output is stable
|
|
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,
|
|
810
|
-
* instances, or non-finite numbers. The error names the path of
|
|
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
|
|
823
|
-
* Centralizing them keeps the object model free of string
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1197
|
-
* ultimately reads and writes the raw dictionaries through the
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1820
|
-
* `Æ` instead of the Latin-1 `á`. Values are Unicode code
|
|
1821
|
-
* published NEXTSTEP.TXT vendor mapping in the Unicode
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
2154
|
-
* never scanned, so a trailing unterminated comment still
|
|
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
|
|
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
|
|
2404
|
+
* Reads a quoted string whose opening quote is at the current position.
|
|
2255
2405
|
*
|
|
2256
|
-
* The scan tracks whether any escape sequence occurred
|
|
2257
|
-
* (the overwhelming majority) return as a direct slice, and only
|
|
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
|
|
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
|
|
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
|
|
2352
|
-
* position. A trailing comma before `)` is allowed
|
|
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
|
-
*
|
|
2401
|
-
* an optional leading `-`) settles the token as a string,
|
|
2402
|
-
* 24-character identifiers that dominate project documents are
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2608
|
-
* terminator, zero fill to 56 bytes mod 64, then the bit length as
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3539
|
-
* object with it (see {@link add})
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 };
|