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