rork-xcode 0.3.0 → 0.4.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 +24 -21
- package/dist/index.d.ts +172 -22
- package/dist/index.js +408 -167
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,11 +25,11 @@ const text = buildPbxproj(project); // byte-stable, Xcode-canonical layout
|
|
|
25
25
|
|
|
26
26
|
`rork-xcode` is designed for exactly that situation:
|
|
27
27
|
|
|
28
|
-
- **Zero dependencies.** The pbxproj grammar is a small OpenStep-style property list dialect
|
|
28
|
+
- **Zero dependencies.** The pbxproj grammar is a small OpenStep-style property list dialect of dictionaries, arrays, strings, and hex data runs. A dedicated scanner covers it completely, with no general-purpose parser stack, no native addon, and no WASM blob.
|
|
29
29
|
- **One artifact, one code path.** A single ESM file with named exports. No environment-conditional entry points, no reliance on ambient globals like `Buffer`. What you test locally is what runs in production, whatever the bundler.
|
|
30
30
|
- **Xcode-canonical output.** The serializer reproduces the layout Xcode itself writes (tab indentation, per-isa object sections in sorted order, single-line build-file entries, and derived reference comments like `13B07F86… /* AppDelegate.swift in Sources */`), so diffs against Xcode-saved projects stay minimal and Xcode does not rewrite the file on next save.
|
|
31
31
|
- **Round-trip faithful.** Parse → build is byte-identical for Xcode-canonical documents and a fixed point for everything else. Lexical subtleties that plain number conversion would destroy (leading-zero values like `0755`, trailing-zero versions like `5.0`, digit runs longer than the double-precision safe range) are preserved as strings by design.
|
|
32
|
-
- **Loud failure modes.** Malformed documents fail with a typed error carrying line and column
|
|
32
|
+
- **Loud failure modes.** Malformed documents fail with a typed error carrying line and column, and unrepresentable values (`null`, booleans, non-finite numbers) fail with the exact path of the offending value. Nothing is silently dropped.
|
|
33
33
|
|
|
34
34
|
## Install
|
|
35
35
|
|
|
@@ -222,45 +222,48 @@ for (const [id, object] of project.objects()) {
|
|
|
222
222
|
|
|
223
223
|
### Semantics
|
|
224
224
|
|
|
225
|
-
- **Typed vocabulary, generic fallback.** The typed views cover targets of every kind, groups and variant groups, Xcode 16 synchronized folders and their exception sets, build phases and build rules, Core Data version groups, and cross-project reference proxies. Every other kind is a generic `XcodeObject` with the same read and write access, so nothing in a document is out of reach.
|
|
225
|
+
- **Typed vocabulary, generic fallback.** The typed views cover targets of every kind, groups and variant groups, Xcode 16 synchronized folders and their exception sets, build phases and build rules, build configurations, file references, container item proxies, Core Data version groups, and cross-project reference proxies. Every other kind is a generic `XcodeObject` with the same read and write access, so nothing in a document is out of reach.
|
|
226
|
+
- **`is()` narrowing.** Every view class carries a static type guard for discriminating mixed objects: `if (NativeTarget.is(object))` narrows the way `instanceof` does, subclasses included. Build configurations additionally expose their settings as a live typed dictionary through `configuration.buildSettings`.
|
|
226
227
|
- **Typed, open property shapes.** Known keys autocomplete (`target.properties.productType`) and the shape stays open, so keys like `INFOPLIST_KEY_*` settings remain first-class. The shapes describe well-formed documents. When reading untrusted input, use the narrowing accessors, which never trust them.
|
|
227
|
-
- **Two verb families.** `add*` wires something to its owner (a dependency, a package, a framework, a synchronized folder) and is idempotent
|
|
228
|
+
- **Two verb families.** `add*` wires something to its owner (a dependency, a package, a framework, a synchronized folder) and is idempotent, so re-adding returns the existing wiring. `ensure*` returns a structural container, creating it when missing (a build phase, a group chain, the Products group). Both families can therefore run unconditionally in scaffold and repair flows.
|
|
228
229
|
- **Deterministic identifiers.** New objects get ids derived from what they are (`XX` + 20 digest characters + `XX`, from an embedded hash), so programmatic edits are reproducible run to run and diffs stay minimal. Collisions within a document resolve deterministically, and identical edit sequences produce byte-identical documents.
|
|
229
230
|
- **Soft reads, loud writes.** Real-world projects can be malformed, so lookups return `undefined` where a document could omit something. Operations that cannot proceed without structure (no root project object, an unknown product type, a view whose object was deleted) throw `XcodeModelError`.
|
|
230
231
|
- **Identity-mapped views.** Two lookups of the same id return the same instance, so views compare with `===`.
|
|
231
232
|
|
|
232
233
|
## Schemes
|
|
233
234
|
|
|
234
|
-
`.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
|
|
235
|
+
`.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.
|
|
236
|
+
|
|
237
|
+
`Xcscheme` is the model. Buildable references are the elements editing flows touch, and they come back as typed views:
|
|
235
238
|
|
|
236
239
|
```ts
|
|
237
|
-
import {
|
|
240
|
+
import { Xcscheme } from "rork-xcode";
|
|
238
241
|
|
|
239
|
-
const scheme =
|
|
242
|
+
const scheme = Xcscheme.parse(xcschemeText);
|
|
240
243
|
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
reference.
|
|
244
|
+
// Rename the app while keeping each product's own shape, so a testable
|
|
245
|
+
// like DemoAppTests.xctest stays a test bundle.
|
|
246
|
+
for (const reference of scheme.buildableReferences()) {
|
|
247
|
+
const { blueprintName, buildableName } = reference;
|
|
248
|
+
if (blueprintName) reference.blueprintName = blueprintName.replace("DemoApp", "RenamedApp");
|
|
249
|
+
if (buildableName) reference.buildableName = buildableName.replace("DemoApp", "RenamedApp");
|
|
250
|
+
reference.referencedContainer = "container:RenamedApp.xcodeproj";
|
|
246
251
|
}
|
|
247
252
|
|
|
248
|
-
const text =
|
|
253
|
+
const text = scheme.build();
|
|
249
254
|
```
|
|
250
255
|
|
|
251
|
-
`
|
|
256
|
+
`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:
|
|
252
257
|
|
|
253
258
|
```ts
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const scheme = createXcscheme({
|
|
259
|
+
const scheme = Xcscheme.create({
|
|
257
260
|
appName: "DemoApp",
|
|
258
261
|
blueprintIdentifier: app.id,
|
|
259
262
|
});
|
|
260
|
-
const text =
|
|
263
|
+
const text = scheme.build();
|
|
261
264
|
```
|
|
262
265
|
|
|
263
|
-
|
|
266
|
+
Underneath, the document is a plain tree of elements with ordered attributes and children, reachable through `scheme.root` and `scheme.elements(name)`, so anything the typed surface does not cover stays one property away. `parseXcscheme` and `buildXcscheme` remain available for working with the tree directly. Attribute order is preserved and meaningful, which is how byte-identical round-trips fall out. Comments are kept, attribute values resolve the character references Xcode writes (`"`, `&`, ` ` and friends), and the writer re-escapes them identically.
|
|
264
267
|
|
|
265
268
|
## Performance
|
|
266
269
|
|
|
@@ -283,8 +286,8 @@ Measured on an Apple M5 Max, Node.js 24, single thread, with `@bacons/xcode` 1.0
|
|
|
283
286
|
|
|
284
287
|
### Key performance features
|
|
285
288
|
|
|
286
|
-
- **Single-pass scanner.** One cursor over the input string with table-driven character classification
|
|
287
|
-
- **Comments skip in bulk.** Reference comments are a sizable share of a canonical document's bytes
|
|
289
|
+
- **Single-pass scanner.** One cursor over the input string with table-driven character classification. There is no tokenizer stage and no intermediate token objects.
|
|
290
|
+
- **Comments skip in bulk.** Reference comments are a sizable share of a canonical document's bytes, so comment bodies are jumped with `indexOf` instead of being scanned per character.
|
|
288
291
|
- **Linear comment derivation.** Building the `/* … */` annotations uses reverse indexes over the object graph (build file → phase, configuration list → owner), so serialization stays linear on projects with thousands of objects.
|
|
289
292
|
- **Memoized rendering.** Quoting decisions for the repeated key vocabulary and rendered uuid references are cached per document, halving the quote scans on reference-heavy sections.
|
|
290
293
|
|
package/dist/index.d.ts
CHANGED
|
@@ -307,6 +307,8 @@ interface BuildSettings extends PbxprojObject {
|
|
|
307
307
|
SUPPORTED_PLATFORMS?: string;
|
|
308
308
|
SWIFT_VERSION?: string;
|
|
309
309
|
TARGETED_DEVICE_FAMILY?: number | string;
|
|
310
|
+
TEST_HOST?: string;
|
|
311
|
+
TEST_TARGET_NAME?: string;
|
|
310
312
|
TVOS_DEPLOYMENT_TARGET?: string;
|
|
311
313
|
WATCHOS_DEPLOYMENT_TARGET?: string;
|
|
312
314
|
XROS_DEPLOYMENT_TARGET?: string;
|
|
@@ -888,6 +890,49 @@ declare class VersionGroup extends Group<VersionGroupProperties> {
|
|
|
888
890
|
*/
|
|
889
891
|
setCurrentVersion(reference: XcodeObject): void;
|
|
890
892
|
}
|
|
893
|
+
/**
|
|
894
|
+
* An `XCBuildConfiguration` holds one named settings dictionary of a
|
|
895
|
+
* target or of the project, for example Debug or Release.
|
|
896
|
+
*/
|
|
897
|
+
declare class BuildConfiguration extends XcodeObject<BuildConfigurationProperties> {
|
|
898
|
+
/**
|
|
899
|
+
* The configuration's name, when present.
|
|
900
|
+
*/
|
|
901
|
+
get name(): string | undefined;
|
|
902
|
+
/**
|
|
903
|
+
* The configuration's settings dictionary, typed with the keys
|
|
904
|
+
* programmatic edits touch most, or `undefined` when the configuration
|
|
905
|
+
* carries none. The dictionary is live. Writes through it land in the
|
|
906
|
+
* document.
|
|
907
|
+
*/
|
|
908
|
+
get buildSettings(): BuildSettings | undefined;
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* A `PBXFileReference` names one file on disk, from source files to the
|
|
912
|
+
* built products themselves.
|
|
913
|
+
*/
|
|
914
|
+
declare class FileReference extends XcodeObject<FileReferenceProperties> {
|
|
915
|
+
/**
|
|
916
|
+
* The reference's path, relative to its `sourceTree`, when present.
|
|
917
|
+
*/
|
|
918
|
+
get path(): string | undefined;
|
|
919
|
+
/**
|
|
920
|
+
* The reference's display name, when it carries one distinct from the
|
|
921
|
+
* path.
|
|
922
|
+
*/
|
|
923
|
+
get name(): string | undefined;
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* A `PBXContainerItemProxy` is the indirection Xcode places between a
|
|
927
|
+
* target dependency and the target it points at.
|
|
928
|
+
*/
|
|
929
|
+
declare class ContainerItemProxy extends XcodeObject<ContainerItemProxyProperties> {
|
|
930
|
+
/**
|
|
931
|
+
* The display name of the object the proxy points at, when present.
|
|
932
|
+
* For target dependencies this is the target's name.
|
|
933
|
+
*/
|
|
934
|
+
get remoteInfo(): string | undefined;
|
|
935
|
+
}
|
|
891
936
|
/**
|
|
892
937
|
* A `PBXReferenceProxy` stands in for a product built by a target of
|
|
893
938
|
* another project referenced from this one.
|
|
@@ -1213,15 +1258,32 @@ declare class XcodeObject<Properties extends PbxprojObject = PbxprojObject> {
|
|
|
1213
1258
|
/** The object's 24-character identifier (its key in `objects`). */
|
|
1214
1259
|
readonly id: string;
|
|
1215
1260
|
/**
|
|
1216
|
-
* Views are created by the project's identity map
|
|
1261
|
+
* Views are created by the project's identity map. Use
|
|
1217
1262
|
* {@link XcodeProject.get} or a typed query instead of constructing
|
|
1218
1263
|
* views directly.
|
|
1219
1264
|
*/
|
|
1220
1265
|
constructor(project: XcodeProject, id: string);
|
|
1221
1266
|
/**
|
|
1222
|
-
*
|
|
1223
|
-
*
|
|
1224
|
-
*
|
|
1267
|
+
* Whether a value is a view of this class, narrowing it when so. Views
|
|
1268
|
+
* come typed out of the factory, and this is the readable way to
|
|
1269
|
+
* discriminate them when iterating mixed objects.
|
|
1270
|
+
*
|
|
1271
|
+
* ```ts
|
|
1272
|
+
* for (const [, object] of project.objects()) {
|
|
1273
|
+
* if (NativeTarget.is(object)) {
|
|
1274
|
+
* console.log(object.name);
|
|
1275
|
+
* }
|
|
1276
|
+
* }
|
|
1277
|
+
* ```
|
|
1278
|
+
*
|
|
1279
|
+
* Subclass views match their parent class too, the way `instanceof`
|
|
1280
|
+
* does, so a `VersionGroup` is a `Group`.
|
|
1281
|
+
*/
|
|
1282
|
+
static is<Constructor extends abstract new (...args: never) => unknown>(this: Constructor, value: unknown): value is InstanceType<Constructor>;
|
|
1283
|
+
/**
|
|
1284
|
+
* The object's raw dictionary inside the document. Mutations through
|
|
1285
|
+
* the model write here, direct writes are equally valid, and the model
|
|
1286
|
+
* adds no caching over these properties.
|
|
1225
1287
|
*
|
|
1226
1288
|
* The typed shape is asserted, not checked. It describes what a
|
|
1227
1289
|
* well-formed object of this kind carries (see `properties.ts`).
|
|
@@ -1285,19 +1347,20 @@ declare function parsePbxproj(text: string): PbxprojValue;
|
|
|
1285
1347
|
/**
|
|
1286
1348
|
* The node model of a scheme document.
|
|
1287
1349
|
*
|
|
1288
|
-
* A parsed `.xcscheme` is a tree of plain objects
|
|
1289
|
-
* attributes and child nodes,
|
|
1290
|
-
* in this tree. There is no wrapper
|
|
1291
|
-
* mutate nodes directly and serialize whatever the tree currently
|
|
1350
|
+
* A parsed `.xcscheme` is a tree of plain objects. Elements carry ordered
|
|
1351
|
+
* attributes and child nodes, and comments survive as their own nodes.
|
|
1352
|
+
* All state lives in this tree. There is no wrapper to keep in sync, so
|
|
1353
|
+
* callers mutate nodes directly and serialize whatever the tree currently
|
|
1354
|
+
* says.
|
|
1292
1355
|
*
|
|
1293
1356
|
* @module
|
|
1294
1357
|
*/
|
|
1295
1358
|
/**
|
|
1296
1359
|
* An XML element of a scheme document.
|
|
1297
1360
|
*
|
|
1298
|
-
* Attribute order is preserved and meaningful
|
|
1299
|
-
* in insertion order, which is how
|
|
1300
|
-
*
|
|
1361
|
+
* Attribute order is preserved and meaningful. The writer emits
|
|
1362
|
+
* attributes in insertion order, which is how Xcode-written files
|
|
1363
|
+
* round-trip byte-identically. Assigning an existing key keeps its
|
|
1301
1364
|
* position, and adding a new key appends it.
|
|
1302
1365
|
*/
|
|
1303
1366
|
interface XcschemeElement {
|
|
@@ -1322,9 +1385,10 @@ interface XcschemeComment {
|
|
|
1322
1385
|
*/
|
|
1323
1386
|
type XcschemeNode = XcschemeElement | XcschemeComment;
|
|
1324
1387
|
/**
|
|
1325
|
-
* A parsed scheme file
|
|
1326
|
-
* outside it. Xcode writes only the root, so the comment lists
|
|
1327
|
-
* always empty, but files that carry them round-trip without
|
|
1388
|
+
* A parsed scheme file, made of its root element plus any comments
|
|
1389
|
+
* sitting outside it. Xcode writes only the root, so the comment lists
|
|
1390
|
+
* are almost always empty, but files that carry them round-trip without
|
|
1391
|
+
* loss.
|
|
1328
1392
|
*/
|
|
1329
1393
|
interface XcschemeDocument {
|
|
1330
1394
|
/** Comments before the root element. */
|
|
@@ -1354,14 +1418,99 @@ declare function buildXcscheme(document: XcschemeDocument): string;
|
|
|
1354
1418
|
* Collects elements of the given name anywhere in the tree, in document
|
|
1355
1419
|
* order, the subtree root included. Passing no name collects every
|
|
1356
1420
|
* element.
|
|
1421
|
+
*/
|
|
1422
|
+
declare function xcschemeElements(root: XcschemeElement, name?: string): XcschemeElement[];
|
|
1423
|
+
/**
|
|
1424
|
+
* A `BuildableReference` element with property-style attribute access.
|
|
1425
|
+
*
|
|
1426
|
+
* Buildable references are the elements editing flows touch most, since
|
|
1427
|
+
* every action points at its target through one. The view reads and
|
|
1428
|
+
* writes the element's attributes directly, so it never goes stale and
|
|
1429
|
+
* needs no separate save step.
|
|
1430
|
+
*/
|
|
1431
|
+
declare class BuildableReference {
|
|
1432
|
+
/** The underlying element inside the document tree. */
|
|
1433
|
+
readonly element: XcschemeElement;
|
|
1434
|
+
constructor(element: XcschemeElement);
|
|
1435
|
+
/**
|
|
1436
|
+
* The referenced target's object id in the project document, when
|
|
1437
|
+
* present. Xcode repairs a missing or stale identifier on first open.
|
|
1438
|
+
*/
|
|
1439
|
+
get blueprintIdentifier(): string | undefined;
|
|
1440
|
+
set blueprintIdentifier(value: string);
|
|
1441
|
+
/**
|
|
1442
|
+
* The referenced target's name, when present.
|
|
1443
|
+
*/
|
|
1444
|
+
get blueprintName(): string | undefined;
|
|
1445
|
+
set blueprintName(value: string);
|
|
1446
|
+
/**
|
|
1447
|
+
* The built product's file name, for example `DemoApp.app`, when
|
|
1448
|
+
* present.
|
|
1449
|
+
*/
|
|
1450
|
+
get buildableName(): string | undefined;
|
|
1451
|
+
set buildableName(value: string);
|
|
1452
|
+
/**
|
|
1453
|
+
* The container the target lives in, for example
|
|
1454
|
+
* `container:DemoApp.xcodeproj`, when present.
|
|
1455
|
+
*/
|
|
1456
|
+
get referencedContainer(): string | undefined;
|
|
1457
|
+
set referencedContainer(value: string);
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* A scheme document with typed access to the elements editing flows
|
|
1461
|
+
* touch.
|
|
1462
|
+
*
|
|
1463
|
+
* The model is a thin layer over the node tree. All state lives in the
|
|
1464
|
+
* document itself, and {@link build} serializes whatever the tree
|
|
1465
|
+
* currently says, so typed edits and direct tree edits compose freely.
|
|
1357
1466
|
*
|
|
1358
1467
|
* ```ts
|
|
1359
|
-
*
|
|
1360
|
-
*
|
|
1468
|
+
* const scheme = Xcscheme.parse(xcschemeText);
|
|
1469
|
+
* for (const reference of scheme.buildableReferences()) {
|
|
1470
|
+
* reference.blueprintName = "RenamedApp";
|
|
1471
|
+
* reference.buildableName = "RenamedApp.app";
|
|
1361
1472
|
* }
|
|
1473
|
+
* const text = scheme.build();
|
|
1362
1474
|
* ```
|
|
1363
1475
|
*/
|
|
1364
|
-
declare
|
|
1476
|
+
declare class Xcscheme {
|
|
1477
|
+
/** The underlying parsed document. */
|
|
1478
|
+
readonly document: XcschemeDocument;
|
|
1479
|
+
constructor(document: XcschemeDocument);
|
|
1480
|
+
/**
|
|
1481
|
+
* Parses the text of a `.xcscheme` file into a scheme model.
|
|
1482
|
+
*
|
|
1483
|
+
* @throws XcschemeParseError when the text is not a well-formed scheme
|
|
1484
|
+
* document, with the line and column of the failure.
|
|
1485
|
+
*/
|
|
1486
|
+
static parse(text: string): Xcscheme;
|
|
1487
|
+
/**
|
|
1488
|
+
* Creates the scheme Xcode writes for an application target. See
|
|
1489
|
+
* {@link createXcscheme} for the shape it produces.
|
|
1490
|
+
*/
|
|
1491
|
+
static create(options: CreateXcschemeOptions): Xcscheme;
|
|
1492
|
+
/**
|
|
1493
|
+
* The document's `Scheme` element.
|
|
1494
|
+
*/
|
|
1495
|
+
get root(): XcschemeElement;
|
|
1496
|
+
/**
|
|
1497
|
+
* Serializes the scheme to the text of a `.xcscheme` file in Xcode's
|
|
1498
|
+
* canonical layout.
|
|
1499
|
+
*/
|
|
1500
|
+
build(): string;
|
|
1501
|
+
/**
|
|
1502
|
+
* Collects elements of the given name anywhere in the document, in
|
|
1503
|
+
* document order. Passing no name collects every element.
|
|
1504
|
+
*/
|
|
1505
|
+
elements(name?: string): XcschemeElement[];
|
|
1506
|
+
/**
|
|
1507
|
+
* The views of every buildable reference in the document, in document
|
|
1508
|
+
* order. Build entries, testables, macro expansions, runnables, and
|
|
1509
|
+
* action environment buildables all point at their target through one
|
|
1510
|
+
* of these, so rename flows iterate this list.
|
|
1511
|
+
*/
|
|
1512
|
+
buildableReferences(): BuildableReference[];
|
|
1513
|
+
}
|
|
1365
1514
|
/**
|
|
1366
1515
|
* Options for {@link createXcscheme}.
|
|
1367
1516
|
*/
|
|
@@ -1381,10 +1530,11 @@ interface CreateXcschemeOptions {
|
|
|
1381
1530
|
blueprintIdentifier?: string;
|
|
1382
1531
|
}
|
|
1383
1532
|
/**
|
|
1384
|
-
* Creates the scheme Xcode writes for an application target
|
|
1385
|
-
* launch, profile, analyze, and archive actions
|
|
1386
|
-
* with Xcode's default configuration choices
|
|
1387
|
-
* actions
|
|
1533
|
+
* Creates the scheme Xcode writes for an application target. The
|
|
1534
|
+
* document carries build, launch, profile, analyze, and archive actions
|
|
1535
|
+
* wired to the app product, with Xcode's default configuration choices
|
|
1536
|
+
* of Debug for development actions and Release for profiling and
|
|
1537
|
+
* archiving.
|
|
1388
1538
|
*/
|
|
1389
1539
|
declare function createXcscheme(options: CreateXcschemeOptions): XcschemeDocument;
|
|
1390
1540
|
//#endregion
|
|
@@ -1424,4 +1574,4 @@ declare function parseXcscheme(text: string): XcschemeDocument;
|
|
|
1424
1574
|
*/
|
|
1425
1575
|
declare function generateObjectId(seed: string, existing: ReadonlySet<string>): string;
|
|
1426
1576
|
//#endregion
|
|
1427
|
-
export { type AddNativeTargetOptions, AggregateTarget, type ApplePlatform, type BuildConfigurationProperties, type BuildFileProperties, BuildPhase, type BuildPhaseProperties, BuildRule, type BuildRuleProperties, type BuildSettings, type ConfigurationListProperties, type ContainerItemProxyProperties, CopyFilesDestination, type CreateXcschemeOptions, type ExceptionSetProperties, type FileReferenceProperties, Group, type GroupProperties, Isa, LegacyTarget, type LegacyTargetProperties, NativeTarget, type NativeTargetProperties, type PbxprojArray, PbxprojBuildError, type PbxprojErrorPosition, type PbxprojObject, PbxprojParseError, type PbxprojValue, ProductType, type ProjectIssue, type ProjectIssueKind, ReferenceProxy, type ReferenceProxyProperties, RootProject, type RootProjectProperties, type SwiftPackageProductDependencyProperties, type SwiftPackageReferenceProperties, SyncRootGroup, type SyncRootGroupProperties, Target, type TargetDependencyProperties, type TargetProperties, VersionGroup, type VersionGroupProperties, XcodeModelError, XcodeObject, XcodeProject, XcschemeBuildError, type XcschemeComment, type XcschemeDocument, type XcschemeElement, type XcschemeNode, XcschemeParseError, buildPbxproj, buildXcscheme, createXcscheme, generateObjectId, isXcschemeElement, parsePbxproj, parseXcscheme, xcschemeElements };
|
|
1577
|
+
export { type AddNativeTargetOptions, AggregateTarget, type ApplePlatform, BuildConfiguration, type BuildConfigurationProperties, type BuildFileProperties, BuildPhase, type BuildPhaseProperties, BuildRule, type BuildRuleProperties, type BuildSettings, BuildableReference, type ConfigurationListProperties, ContainerItemProxy, type ContainerItemProxyProperties, CopyFilesDestination, type CreateXcschemeOptions, type ExceptionSetProperties, FileReference, type FileReferenceProperties, Group, type GroupProperties, Isa, LegacyTarget, type LegacyTargetProperties, NativeTarget, type NativeTargetProperties, type PbxprojArray, PbxprojBuildError, type PbxprojErrorPosition, type PbxprojObject, PbxprojParseError, type PbxprojValue, ProductType, type ProjectIssue, type ProjectIssueKind, ReferenceProxy, type ReferenceProxyProperties, RootProject, type RootProjectProperties, type SwiftPackageProductDependencyProperties, type SwiftPackageReferenceProperties, SyncRootGroup, type SyncRootGroupProperties, Target, type TargetDependencyProperties, type TargetProperties, VersionGroup, type VersionGroupProperties, XcodeModelError, XcodeObject, XcodeProject, Xcscheme, XcschemeBuildError, type XcschemeComment, type XcschemeDocument, type XcschemeElement, type XcschemeNode, XcschemeParseError, buildPbxproj, buildXcscheme, createXcscheme, generateObjectId, isXcschemeElement, parsePbxproj, parseXcscheme, xcschemeElements };
|
package/dist/index.js
CHANGED
|
@@ -1006,9 +1006,9 @@ function stringItems(value) {
|
|
|
1006
1006
|
*
|
|
1007
1007
|
* Views are lightweight, identity-mapped facades over entries of the
|
|
1008
1008
|
* document's `objects` dictionary. All state lives in the parsed document
|
|
1009
|
-
* itself
|
|
1010
|
-
* document text produced from the model reflects every
|
|
1011
|
-
* separate serialization step.
|
|
1009
|
+
* itself, and a view holds only its id and a reference back to the
|
|
1010
|
+
* project, so document text produced from the model reflects every
|
|
1011
|
+
* mutation with no separate serialization step.
|
|
1012
1012
|
*
|
|
1013
1013
|
* @module
|
|
1014
1014
|
*/
|
|
@@ -1030,7 +1030,7 @@ var XcodeObject = class {
|
|
|
1030
1030
|
/** The object's 24-character identifier (its key in `objects`). */
|
|
1031
1031
|
id;
|
|
1032
1032
|
/**
|
|
1033
|
-
* Views are created by the project's identity map
|
|
1033
|
+
* Views are created by the project's identity map. Use
|
|
1034
1034
|
* {@link XcodeProject.get} or a typed query instead of constructing
|
|
1035
1035
|
* views directly.
|
|
1036
1036
|
*/
|
|
@@ -1039,9 +1039,28 @@ var XcodeObject = class {
|
|
|
1039
1039
|
this.id = id;
|
|
1040
1040
|
}
|
|
1041
1041
|
/**
|
|
1042
|
-
*
|
|
1043
|
-
*
|
|
1044
|
-
*
|
|
1042
|
+
* Whether a value is a view of this class, narrowing it when so. Views
|
|
1043
|
+
* come typed out of the factory, and this is the readable way to
|
|
1044
|
+
* discriminate them when iterating mixed objects.
|
|
1045
|
+
*
|
|
1046
|
+
* ```ts
|
|
1047
|
+
* for (const [, object] of project.objects()) {
|
|
1048
|
+
* if (NativeTarget.is(object)) {
|
|
1049
|
+
* console.log(object.name);
|
|
1050
|
+
* }
|
|
1051
|
+
* }
|
|
1052
|
+
* ```
|
|
1053
|
+
*
|
|
1054
|
+
* Subclass views match their parent class too, the way `instanceof`
|
|
1055
|
+
* does, so a `VersionGroup` is a `Group`.
|
|
1056
|
+
*/
|
|
1057
|
+
static is(value) {
|
|
1058
|
+
return value instanceof this;
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* The object's raw dictionary inside the document. Mutations through
|
|
1062
|
+
* the model write here, direct writes are equally valid, and the model
|
|
1063
|
+
* adds no caching over these properties.
|
|
1045
1064
|
*
|
|
1046
1065
|
* The typed shape is asserted, not checked. It describes what a
|
|
1047
1066
|
* well-formed object of this kind carries (see `properties.ts`).
|
|
@@ -1342,6 +1361,59 @@ var VersionGroup = class extends Group {
|
|
|
1342
1361
|
}
|
|
1343
1362
|
};
|
|
1344
1363
|
/**
|
|
1364
|
+
* An `XCBuildConfiguration` holds one named settings dictionary of a
|
|
1365
|
+
* target or of the project, for example Debug or Release.
|
|
1366
|
+
*/
|
|
1367
|
+
var BuildConfiguration = class extends XcodeObject {
|
|
1368
|
+
/**
|
|
1369
|
+
* The configuration's name, when present.
|
|
1370
|
+
*/
|
|
1371
|
+
get name() {
|
|
1372
|
+
return this.getString("name");
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* The configuration's settings dictionary, typed with the keys
|
|
1376
|
+
* programmatic edits touch most, or `undefined` when the configuration
|
|
1377
|
+
* carries none. The dictionary is live. Writes through it land in the
|
|
1378
|
+
* document.
|
|
1379
|
+
*/
|
|
1380
|
+
get buildSettings() {
|
|
1381
|
+
return asDictionary(this.properties["buildSettings"]);
|
|
1382
|
+
}
|
|
1383
|
+
};
|
|
1384
|
+
/**
|
|
1385
|
+
* A `PBXFileReference` names one file on disk, from source files to the
|
|
1386
|
+
* built products themselves.
|
|
1387
|
+
*/
|
|
1388
|
+
var FileReference = class extends XcodeObject {
|
|
1389
|
+
/**
|
|
1390
|
+
* The reference's path, relative to its `sourceTree`, when present.
|
|
1391
|
+
*/
|
|
1392
|
+
get path() {
|
|
1393
|
+
return this.getString("path");
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* The reference's display name, when it carries one distinct from the
|
|
1397
|
+
* path.
|
|
1398
|
+
*/
|
|
1399
|
+
get name() {
|
|
1400
|
+
return this.getString("name");
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
/**
|
|
1404
|
+
* A `PBXContainerItemProxy` is the indirection Xcode places between a
|
|
1405
|
+
* target dependency and the target it points at.
|
|
1406
|
+
*/
|
|
1407
|
+
var ContainerItemProxy = class extends XcodeObject {
|
|
1408
|
+
/**
|
|
1409
|
+
* The display name of the object the proxy points at, when present.
|
|
1410
|
+
* For target dependencies this is the target's name.
|
|
1411
|
+
*/
|
|
1412
|
+
get remoteInfo() {
|
|
1413
|
+
return this.getString("remoteInfo");
|
|
1414
|
+
}
|
|
1415
|
+
};
|
|
1416
|
+
/**
|
|
1345
1417
|
* A `PBXReferenceProxy` stands in for a product built by a target of
|
|
1346
1418
|
* another project referenced from this one.
|
|
1347
1419
|
*/
|
|
@@ -3370,6 +3442,9 @@ var XcodeProject = class XcodeProject {
|
|
|
3370
3442
|
case Isa.versionGroup: return new VersionGroup(this, id);
|
|
3371
3443
|
case Isa.fileSystemSynchronizedRootGroup: return new SyncRootGroup(this, id);
|
|
3372
3444
|
case Isa.buildRule: return new BuildRule(this, id);
|
|
3445
|
+
case Isa.buildConfiguration: return new BuildConfiguration(this, id);
|
|
3446
|
+
case Isa.fileReference: return new FileReference(this, id);
|
|
3447
|
+
case Isa.containerItemProxy: return new ContainerItemProxy(this, id);
|
|
3373
3448
|
case Isa.referenceProxy: return new ReferenceProxy(this, id);
|
|
3374
3449
|
default: return isa.endsWith("BuildPhase") ? new BuildPhase(this, id) : new XcodeObject(this, id);
|
|
3375
3450
|
}
|
|
@@ -3454,26 +3529,28 @@ function isXcschemeElement(node) {
|
|
|
3454
3529
|
/**
|
|
3455
3530
|
* Serializer for the `.xcscheme` XML dialect.
|
|
3456
3531
|
*
|
|
3457
|
-
* The output reproduces Xcode's own layout byte for byte
|
|
3458
|
-
* declaration, three
|
|
3459
|
-
* spaces around the equals sign, the closing
|
|
3460
|
-
* last attribute, and
|
|
3461
|
-
* Xcode-written scheme and building it
|
|
3462
|
-
*
|
|
3532
|
+
* The output reproduces Xcode's own layout byte for byte. Xcode writes
|
|
3533
|
+
* the UTF-8 declaration, indents with three spaces, puts each attribute
|
|
3534
|
+
* on its own line with spaces around the equals sign, glues the closing
|
|
3535
|
+
* angle bracket to the last attribute, and closes every element with an
|
|
3536
|
+
* explicit close tag. Parsing an Xcode-written scheme and building it
|
|
3537
|
+
* back therefore yields the identical file, and any other input reaches
|
|
3538
|
+
* that canonical form in one build.
|
|
3463
3539
|
*
|
|
3464
3540
|
* @module
|
|
3465
3541
|
*/
|
|
3542
|
+
/** The declaration line Xcode writes at the top of every scheme file. */
|
|
3466
3543
|
const XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
|
3467
3544
|
/** One indentation step of Xcode's scheme writer. */
|
|
3468
3545
|
const INDENT = " ";
|
|
3469
3546
|
/**
|
|
3470
|
-
* Matches
|
|
3471
|
-
*
|
|
3547
|
+
* Matches valid XML names. A stray non-name, such as an empty string or
|
|
3548
|
+
* one carrying spaces or XML syntax, must fail rather than produce a
|
|
3472
3549
|
* document no parser accepts.
|
|
3473
3550
|
*/
|
|
3474
3551
|
const NAME_PATTERN = /^[A-Za-z_:][A-Za-z0-9_:.-]*$/u;
|
|
3475
3552
|
/**
|
|
3476
|
-
* Matches characters that cannot appear in an attribute value
|
|
3553
|
+
* Matches characters that cannot appear in an attribute value. XML 1.0
|
|
3477
3554
|
* has no representation for control characters other than tab, line
|
|
3478
3555
|
* feed, and carriage return.
|
|
3479
3556
|
*/
|
|
@@ -3493,9 +3570,10 @@ function buildXcscheme(document) {
|
|
|
3493
3570
|
return output;
|
|
3494
3571
|
}
|
|
3495
3572
|
/**
|
|
3496
|
-
* Renders one element with Xcode's layout
|
|
3497
|
-
* line indented one step past the tag, `>`
|
|
3498
|
-
* children
|
|
3573
|
+
* Renders one element with Xcode's layout. Attributes go each on their
|
|
3574
|
+
* own line indented one step past the tag, the `>` glues to the last
|
|
3575
|
+
* attribute, children indent one step, and the close tag is always
|
|
3576
|
+
* explicit.
|
|
3499
3577
|
*/
|
|
3500
3578
|
function renderElement(element, depth, path) {
|
|
3501
3579
|
if (!NAME_PATTERN.test(element.name)) throw new XcschemeBuildError(`Element name ${JSON.stringify(element.name)} is not a valid XML name`, path);
|
|
@@ -3533,10 +3611,10 @@ function renderChildren(children, depth, path) {
|
|
|
3533
3611
|
return output;
|
|
3534
3612
|
}
|
|
3535
3613
|
/**
|
|
3536
|
-
* Escapes an attribute value the way Xcode's writer does
|
|
3537
|
-
*
|
|
3538
|
-
*
|
|
3539
|
-
* normalization on the next parse.
|
|
3614
|
+
* Escapes an attribute value the way Xcode's writer does. XML syntax
|
|
3615
|
+
* characters become the five named entities, and tab, line feed, and
|
|
3616
|
+
* carriage return become character references so they survive
|
|
3617
|
+
* attribute-value normalization on the next parse.
|
|
3540
3618
|
*/
|
|
3541
3619
|
function escapeAttribute(value, path, attributeName) {
|
|
3542
3620
|
const unencodable = UNENCODABLE_PATTERN.exec(value);
|
|
@@ -3544,152 +3622,17 @@ function escapeAttribute(value, path, attributeName) {
|
|
|
3544
3622
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'").replaceAll(" ", "	").replaceAll("\n", " ").replaceAll("\r", " ");
|
|
3545
3623
|
}
|
|
3546
3624
|
//#endregion
|
|
3547
|
-
//#region src/scheme/model.ts
|
|
3548
|
-
/**
|
|
3549
|
-
* Helpers over the scheme node tree: element queries and the default
|
|
3550
|
-
* scheme factory.
|
|
3551
|
-
*
|
|
3552
|
-
* The tree itself is plain data, so most edits are direct property
|
|
3553
|
-
* writes. What this module adds is the recursive query that editing
|
|
3554
|
-
* flows start from and a factory producing the scheme Xcode's own "New
|
|
3555
|
-
* Scheme" action writes for an application target.
|
|
3556
|
-
*
|
|
3557
|
-
* @module
|
|
3558
|
-
*/
|
|
3559
|
-
/**
|
|
3560
|
-
* Collects elements of the given name anywhere in the tree, in document
|
|
3561
|
-
* order, the subtree root included. Passing no name collects every
|
|
3562
|
-
* element.
|
|
3563
|
-
*
|
|
3564
|
-
* ```ts
|
|
3565
|
-
* for (const reference of xcschemeElements(scheme.root, "BuildableReference")) {
|
|
3566
|
-
* reference.attributes["BlueprintName"] = "RenamedApp";
|
|
3567
|
-
* }
|
|
3568
|
-
* ```
|
|
3569
|
-
*/
|
|
3570
|
-
function xcschemeElements(root, name) {
|
|
3571
|
-
const found = [];
|
|
3572
|
-
const visit = (element) => {
|
|
3573
|
-
if (name == null || element.name === name) found.push(element);
|
|
3574
|
-
for (const child of element.children) if (isXcschemeElement(child)) visit(child);
|
|
3575
|
-
};
|
|
3576
|
-
visit(root);
|
|
3577
|
-
return found;
|
|
3578
|
-
}
|
|
3579
|
-
/**
|
|
3580
|
-
* Creates the scheme Xcode writes for an application target: build,
|
|
3581
|
-
* launch, profile, analyze, and archive actions wired to the app product,
|
|
3582
|
-
* with Xcode's default configuration choices (Debug for development
|
|
3583
|
-
* actions, Release for profiling and archiving).
|
|
3584
|
-
*/
|
|
3585
|
-
function createXcscheme(options) {
|
|
3586
|
-
const xcodeprojName = options.xcodeprojName ?? `${options.appName}.xcodeproj`;
|
|
3587
|
-
/**
|
|
3588
|
-
* Buildable references appear once per action; each site gets its own
|
|
3589
|
-
* element so a mutation through one action cannot alias into another.
|
|
3590
|
-
*/
|
|
3591
|
-
const buildableReference = () => ({
|
|
3592
|
-
name: "BuildableReference",
|
|
3593
|
-
attributes: {
|
|
3594
|
-
BuildableIdentifier: "primary",
|
|
3595
|
-
BlueprintIdentifier: options.blueprintIdentifier ?? "",
|
|
3596
|
-
BuildableName: `${options.appName}.app`,
|
|
3597
|
-
BlueprintName: options.appName,
|
|
3598
|
-
ReferencedContainer: `container:${xcodeprojName}`
|
|
3599
|
-
},
|
|
3600
|
-
children: []
|
|
3601
|
-
});
|
|
3602
|
-
return {
|
|
3603
|
-
leading: [],
|
|
3604
|
-
root: {
|
|
3605
|
-
name: "Scheme",
|
|
3606
|
-
attributes: { version: "1.7" },
|
|
3607
|
-
children: [
|
|
3608
|
-
{
|
|
3609
|
-
name: "BuildAction",
|
|
3610
|
-
attributes: {
|
|
3611
|
-
parallelizeBuildables: "YES",
|
|
3612
|
-
buildImplicitDependencies: "YES"
|
|
3613
|
-
},
|
|
3614
|
-
children: [{
|
|
3615
|
-
name: "BuildActionEntries",
|
|
3616
|
-
attributes: {},
|
|
3617
|
-
children: [{
|
|
3618
|
-
name: "BuildActionEntry",
|
|
3619
|
-
attributes: {
|
|
3620
|
-
buildForTesting: "YES",
|
|
3621
|
-
buildForRunning: "YES",
|
|
3622
|
-
buildForProfiling: "YES",
|
|
3623
|
-
buildForArchiving: "YES",
|
|
3624
|
-
buildForAnalyzing: "YES"
|
|
3625
|
-
},
|
|
3626
|
-
children: [buildableReference()]
|
|
3627
|
-
}]
|
|
3628
|
-
}]
|
|
3629
|
-
},
|
|
3630
|
-
{
|
|
3631
|
-
name: "LaunchAction",
|
|
3632
|
-
attributes: {
|
|
3633
|
-
buildConfiguration: "Debug",
|
|
3634
|
-
selectedDebuggerIdentifier: "Xcode.DebuggerFoundation.Debugger.LLDB",
|
|
3635
|
-
selectedLauncherIdentifier: "Xcode.DebuggerFoundation.Launcher.LLDB",
|
|
3636
|
-
launchStyle: "0",
|
|
3637
|
-
useCustomWorkingDirectory: "NO",
|
|
3638
|
-
ignoresPersistentStateOnLaunch: "NO",
|
|
3639
|
-
debugDocumentVersioning: "YES",
|
|
3640
|
-
debugServiceExtension: "internal",
|
|
3641
|
-
allowLocationSimulation: "YES"
|
|
3642
|
-
},
|
|
3643
|
-
children: [{
|
|
3644
|
-
name: "BuildableProductRunnable",
|
|
3645
|
-
attributes: { runnableDebuggingMode: "0" },
|
|
3646
|
-
children: [buildableReference()]
|
|
3647
|
-
}]
|
|
3648
|
-
},
|
|
3649
|
-
{
|
|
3650
|
-
name: "ProfileAction",
|
|
3651
|
-
attributes: {
|
|
3652
|
-
buildConfiguration: "Release",
|
|
3653
|
-
shouldUseLaunchSchemeArgsEnv: "YES",
|
|
3654
|
-
savedToolIdentifier: "",
|
|
3655
|
-
useCustomWorkingDirectory: "NO",
|
|
3656
|
-
debugDocumentVersioning: "YES"
|
|
3657
|
-
},
|
|
3658
|
-
children: [{
|
|
3659
|
-
name: "BuildableProductRunnable",
|
|
3660
|
-
attributes: { runnableDebuggingMode: "0" },
|
|
3661
|
-
children: [buildableReference()]
|
|
3662
|
-
}]
|
|
3663
|
-
},
|
|
3664
|
-
{
|
|
3665
|
-
name: "AnalyzeAction",
|
|
3666
|
-
attributes: { buildConfiguration: "Debug" },
|
|
3667
|
-
children: []
|
|
3668
|
-
},
|
|
3669
|
-
{
|
|
3670
|
-
name: "ArchiveAction",
|
|
3671
|
-
attributes: {
|
|
3672
|
-
buildConfiguration: "Release",
|
|
3673
|
-
revealArchiveInOrganizer: "YES"
|
|
3674
|
-
},
|
|
3675
|
-
children: []
|
|
3676
|
-
}
|
|
3677
|
-
]
|
|
3678
|
-
},
|
|
3679
|
-
trailing: []
|
|
3680
|
-
};
|
|
3681
|
-
}
|
|
3682
|
-
//#endregion
|
|
3683
3625
|
//#region src/scheme/parse.ts
|
|
3684
3626
|
/**
|
|
3685
3627
|
* Parser for the `.xcscheme` XML dialect.
|
|
3686
3628
|
*
|
|
3687
|
-
* Scheme files are XML with a narrow shape
|
|
3688
|
-
* child elements, no text content, no
|
|
3689
|
-
* accepts that shape from any writer
|
|
3690
|
-
* whitespace vary across tools
|
|
3691
|
-
* in attribute values
|
|
3692
|
-
*
|
|
3629
|
+
* Scheme files are XML with a narrow shape. Elements carry attributes and
|
|
3630
|
+
* child elements, and there is no text content, no namespace, and no
|
|
3631
|
+
* DOCTYPE. The parser accepts that shape from any writer, since attribute
|
|
3632
|
+
* order, quoting style, and whitespace vary across tools. Character and
|
|
3633
|
+
* entity references in attribute values resolve to their characters, and
|
|
3634
|
+
* comments are preserved. Anything outside the shape fails loudly with a
|
|
3635
|
+
* position, in line with the pbxproj parser.
|
|
3693
3636
|
*
|
|
3694
3637
|
* @module
|
|
3695
3638
|
*/
|
|
@@ -3746,13 +3689,24 @@ const NAMED_ENTITIES = {
|
|
|
3746
3689
|
function parseXcscheme(text) {
|
|
3747
3690
|
return new Parser(text).parseDocument();
|
|
3748
3691
|
}
|
|
3692
|
+
/**
|
|
3693
|
+
* Single-pass recursive-descent parser over the source text. One instance
|
|
3694
|
+
* parses one document and is discarded.
|
|
3695
|
+
*/
|
|
3749
3696
|
var Parser = class {
|
|
3697
|
+
/** The full source text of the scheme file. */
|
|
3750
3698
|
input;
|
|
3699
|
+
/** Cursor into {@link input}, in UTF-16 code units. */
|
|
3751
3700
|
pos = 0;
|
|
3752
3701
|
constructor(input) {
|
|
3753
3702
|
this.input = input;
|
|
3754
3703
|
if (input.charCodeAt(0) === CODE_BOM) this.pos = 1;
|
|
3755
3704
|
}
|
|
3705
|
+
/**
|
|
3706
|
+
* Parses the whole document. The XML declaration is skipped, comments
|
|
3707
|
+
* around the root element are collected, and anything left after the
|
|
3708
|
+
* root fails.
|
|
3709
|
+
*/
|
|
3756
3710
|
parseDocument() {
|
|
3757
3711
|
this.skipWhitespace();
|
|
3758
3712
|
this.skipDeclaration();
|
|
@@ -3778,7 +3732,8 @@ var Parser = class {
|
|
|
3778
3732
|
}
|
|
3779
3733
|
/**
|
|
3780
3734
|
* Skips the `<?xml ... ?>` declaration when present. Its attributes are
|
|
3781
|
-
* not retained
|
|
3735
|
+
* not retained, since the writer always emits the canonical UTF-8
|
|
3736
|
+
* declaration.
|
|
3782
3737
|
*/
|
|
3783
3738
|
skipDeclaration() {
|
|
3784
3739
|
if (this.input.charCodeAt(this.pos) !== CODE_LESS_THAN || this.input.charCodeAt(this.pos + 1) !== CODE_QUESTION) return;
|
|
@@ -3786,6 +3741,12 @@ var Parser = class {
|
|
|
3786
3741
|
if (end === -1) this.fail("Unterminated XML declaration");
|
|
3787
3742
|
this.pos = end + 2;
|
|
3788
3743
|
}
|
|
3744
|
+
/**
|
|
3745
|
+
* Parses one element with the cursor on its opening `<`, including its
|
|
3746
|
+
* attributes and children, through to the matching close tag. Xcode
|
|
3747
|
+
* never writes self-closing tags, but other generators do, so both
|
|
3748
|
+
* forms are accepted.
|
|
3749
|
+
*/
|
|
3789
3750
|
parseElement() {
|
|
3790
3751
|
if (this.input.charCodeAt(this.pos) !== CODE_LESS_THAN) this.fail("Expected an element");
|
|
3791
3752
|
this.pos++;
|
|
@@ -3839,9 +3800,16 @@ var Parser = class {
|
|
|
3839
3800
|
else children.push(this.parseElement());
|
|
3840
3801
|
}
|
|
3841
3802
|
}
|
|
3803
|
+
/**
|
|
3804
|
+
* Whether the cursor sits on a `<!--` comment opener.
|
|
3805
|
+
*/
|
|
3842
3806
|
peekIsCommentStart() {
|
|
3843
3807
|
return this.input.charCodeAt(this.pos) === CODE_LESS_THAN && this.input.charCodeAt(this.pos + 1) === CODE_BANG && this.input.charCodeAt(this.pos + 2) === CODE_HYPHEN && this.input.charCodeAt(this.pos + 3) === CODE_HYPHEN;
|
|
3844
3808
|
}
|
|
3809
|
+
/**
|
|
3810
|
+
* Parses one comment with the cursor on its `<!--` opener. The text
|
|
3811
|
+
* between the markers is kept verbatim.
|
|
3812
|
+
*/
|
|
3845
3813
|
parseComment() {
|
|
3846
3814
|
const end = this.input.indexOf("-->", this.pos + 4);
|
|
3847
3815
|
if (end === -1) this.fail("Unterminated comment");
|
|
@@ -3849,6 +3817,10 @@ var Parser = class {
|
|
|
3849
3817
|
this.pos = end + 3;
|
|
3850
3818
|
return { comment };
|
|
3851
3819
|
}
|
|
3820
|
+
/**
|
|
3821
|
+
* Parses an XML name at the cursor. The `what` label names the
|
|
3822
|
+
* expectation in the error when no name starts here.
|
|
3823
|
+
*/
|
|
3852
3824
|
parseName(what) {
|
|
3853
3825
|
const start = this.pos;
|
|
3854
3826
|
if (!isNameStart(this.input.charCodeAt(this.pos))) this.fail(`Expected ${what}`);
|
|
@@ -3856,6 +3828,12 @@ var Parser = class {
|
|
|
3856
3828
|
while (isNameChar(this.input.charCodeAt(this.pos))) this.pos++;
|
|
3857
3829
|
return this.input.slice(start, this.pos);
|
|
3858
3830
|
}
|
|
3831
|
+
/**
|
|
3832
|
+
* Parses a quoted attribute value with the cursor on the opening quote,
|
|
3833
|
+
* resolving character and entity references along the way. Both quote
|
|
3834
|
+
* styles are accepted, and a raw `<` inside the value fails as XML
|
|
3835
|
+
* requires.
|
|
3836
|
+
*/
|
|
3859
3837
|
parseAttributeValue() {
|
|
3860
3838
|
const quote = this.input.charCodeAt(this.pos);
|
|
3861
3839
|
if (quote !== CODE_QUOTE && quote !== CODE_APOSTROPHE) this.fail("Expected a quoted attribute value");
|
|
@@ -3912,12 +3890,275 @@ var Parser = class {
|
|
|
3912
3890
|
while (isWhitespace(this.input.charCodeAt(this.pos))) this.pos++;
|
|
3913
3891
|
return this.pos > start;
|
|
3914
3892
|
}
|
|
3893
|
+
/**
|
|
3894
|
+
* Throws a parse error at the cursor.
|
|
3895
|
+
*/
|
|
3915
3896
|
fail(message) {
|
|
3916
3897
|
this.failAt(message, this.pos);
|
|
3917
3898
|
}
|
|
3899
|
+
/**
|
|
3900
|
+
* Throws a parse error at an explicit offset, which reference parsing
|
|
3901
|
+
* uses to point at the start of a bad reference rather than its end.
|
|
3902
|
+
*/
|
|
3918
3903
|
failAt(message, offset) {
|
|
3919
3904
|
throw new XcschemeParseError(message, this.input, offset);
|
|
3920
3905
|
}
|
|
3921
3906
|
};
|
|
3922
3907
|
//#endregion
|
|
3923
|
-
|
|
3908
|
+
//#region src/scheme/model.ts
|
|
3909
|
+
/**
|
|
3910
|
+
* The scheme object model and its helpers.
|
|
3911
|
+
*
|
|
3912
|
+
* {@link Xcscheme} wraps a parsed document with the typed access editing
|
|
3913
|
+
* flows want, {@link BuildableReference} gives buildable references
|
|
3914
|
+
* property-style attribute access, {@link xcschemeElements} is the
|
|
3915
|
+
* recursive query underneath it all, and {@link createXcscheme} produces
|
|
3916
|
+
* the scheme Xcode's own "New Scheme" action writes. The node tree stays
|
|
3917
|
+
* the single source of truth. Views hold only a reference into it, so
|
|
3918
|
+
* model calls and direct tree edits compose freely.
|
|
3919
|
+
*
|
|
3920
|
+
* @module
|
|
3921
|
+
*/
|
|
3922
|
+
/**
|
|
3923
|
+
* Collects elements of the given name anywhere in the tree, in document
|
|
3924
|
+
* order, the subtree root included. Passing no name collects every
|
|
3925
|
+
* element.
|
|
3926
|
+
*/
|
|
3927
|
+
function xcschemeElements(root, name) {
|
|
3928
|
+
const found = [];
|
|
3929
|
+
const visit = (element) => {
|
|
3930
|
+
if (name == null || element.name === name) found.push(element);
|
|
3931
|
+
for (const child of element.children) if (isXcschemeElement(child)) visit(child);
|
|
3932
|
+
};
|
|
3933
|
+
visit(root);
|
|
3934
|
+
return found;
|
|
3935
|
+
}
|
|
3936
|
+
/**
|
|
3937
|
+
* A `BuildableReference` element with property-style attribute access.
|
|
3938
|
+
*
|
|
3939
|
+
* Buildable references are the elements editing flows touch most, since
|
|
3940
|
+
* every action points at its target through one. The view reads and
|
|
3941
|
+
* writes the element's attributes directly, so it never goes stale and
|
|
3942
|
+
* needs no separate save step.
|
|
3943
|
+
*/
|
|
3944
|
+
var BuildableReference = class {
|
|
3945
|
+
/** The underlying element inside the document tree. */
|
|
3946
|
+
element;
|
|
3947
|
+
constructor(element) {
|
|
3948
|
+
this.element = element;
|
|
3949
|
+
}
|
|
3950
|
+
/**
|
|
3951
|
+
* The referenced target's object id in the project document, when
|
|
3952
|
+
* present. Xcode repairs a missing or stale identifier on first open.
|
|
3953
|
+
*/
|
|
3954
|
+
get blueprintIdentifier() {
|
|
3955
|
+
return this.element.attributes["BlueprintIdentifier"];
|
|
3956
|
+
}
|
|
3957
|
+
set blueprintIdentifier(value) {
|
|
3958
|
+
this.element.attributes["BlueprintIdentifier"] = value;
|
|
3959
|
+
}
|
|
3960
|
+
/**
|
|
3961
|
+
* The referenced target's name, when present.
|
|
3962
|
+
*/
|
|
3963
|
+
get blueprintName() {
|
|
3964
|
+
return this.element.attributes["BlueprintName"];
|
|
3965
|
+
}
|
|
3966
|
+
set blueprintName(value) {
|
|
3967
|
+
this.element.attributes["BlueprintName"] = value;
|
|
3968
|
+
}
|
|
3969
|
+
/**
|
|
3970
|
+
* The built product's file name, for example `DemoApp.app`, when
|
|
3971
|
+
* present.
|
|
3972
|
+
*/
|
|
3973
|
+
get buildableName() {
|
|
3974
|
+
return this.element.attributes["BuildableName"];
|
|
3975
|
+
}
|
|
3976
|
+
set buildableName(value) {
|
|
3977
|
+
this.element.attributes["BuildableName"] = value;
|
|
3978
|
+
}
|
|
3979
|
+
/**
|
|
3980
|
+
* The container the target lives in, for example
|
|
3981
|
+
* `container:DemoApp.xcodeproj`, when present.
|
|
3982
|
+
*/
|
|
3983
|
+
get referencedContainer() {
|
|
3984
|
+
return this.element.attributes["ReferencedContainer"];
|
|
3985
|
+
}
|
|
3986
|
+
set referencedContainer(value) {
|
|
3987
|
+
this.element.attributes["ReferencedContainer"] = value;
|
|
3988
|
+
}
|
|
3989
|
+
};
|
|
3990
|
+
/**
|
|
3991
|
+
* A scheme document with typed access to the elements editing flows
|
|
3992
|
+
* touch.
|
|
3993
|
+
*
|
|
3994
|
+
* The model is a thin layer over the node tree. All state lives in the
|
|
3995
|
+
* document itself, and {@link build} serializes whatever the tree
|
|
3996
|
+
* currently says, so typed edits and direct tree edits compose freely.
|
|
3997
|
+
*
|
|
3998
|
+
* ```ts
|
|
3999
|
+
* const scheme = Xcscheme.parse(xcschemeText);
|
|
4000
|
+
* for (const reference of scheme.buildableReferences()) {
|
|
4001
|
+
* reference.blueprintName = "RenamedApp";
|
|
4002
|
+
* reference.buildableName = "RenamedApp.app";
|
|
4003
|
+
* }
|
|
4004
|
+
* const text = scheme.build();
|
|
4005
|
+
* ```
|
|
4006
|
+
*/
|
|
4007
|
+
var Xcscheme = class Xcscheme {
|
|
4008
|
+
/** The underlying parsed document. */
|
|
4009
|
+
document;
|
|
4010
|
+
constructor(document) {
|
|
4011
|
+
this.document = document;
|
|
4012
|
+
}
|
|
4013
|
+
/**
|
|
4014
|
+
* Parses the text of a `.xcscheme` file into a scheme model.
|
|
4015
|
+
*
|
|
4016
|
+
* @throws XcschemeParseError when the text is not a well-formed scheme
|
|
4017
|
+
* document, with the line and column of the failure.
|
|
4018
|
+
*/
|
|
4019
|
+
static parse(text) {
|
|
4020
|
+
return new Xcscheme(parseXcscheme(text));
|
|
4021
|
+
}
|
|
4022
|
+
/**
|
|
4023
|
+
* Creates the scheme Xcode writes for an application target. See
|
|
4024
|
+
* {@link createXcscheme} for the shape it produces.
|
|
4025
|
+
*/
|
|
4026
|
+
static create(options) {
|
|
4027
|
+
return new Xcscheme(createXcscheme(options));
|
|
4028
|
+
}
|
|
4029
|
+
/**
|
|
4030
|
+
* The document's `Scheme` element.
|
|
4031
|
+
*/
|
|
4032
|
+
get root() {
|
|
4033
|
+
return this.document.root;
|
|
4034
|
+
}
|
|
4035
|
+
/**
|
|
4036
|
+
* Serializes the scheme to the text of a `.xcscheme` file in Xcode's
|
|
4037
|
+
* canonical layout.
|
|
4038
|
+
*/
|
|
4039
|
+
build() {
|
|
4040
|
+
return buildXcscheme(this.document);
|
|
4041
|
+
}
|
|
4042
|
+
/**
|
|
4043
|
+
* Collects elements of the given name anywhere in the document, in
|
|
4044
|
+
* document order. Passing no name collects every element.
|
|
4045
|
+
*/
|
|
4046
|
+
elements(name) {
|
|
4047
|
+
return xcschemeElements(this.document.root, name);
|
|
4048
|
+
}
|
|
4049
|
+
/**
|
|
4050
|
+
* The views of every buildable reference in the document, in document
|
|
4051
|
+
* order. Build entries, testables, macro expansions, runnables, and
|
|
4052
|
+
* action environment buildables all point at their target through one
|
|
4053
|
+
* of these, so rename flows iterate this list.
|
|
4054
|
+
*/
|
|
4055
|
+
buildableReferences() {
|
|
4056
|
+
return this.elements("BuildableReference").map((element) => new BuildableReference(element));
|
|
4057
|
+
}
|
|
4058
|
+
};
|
|
4059
|
+
/**
|
|
4060
|
+
* Creates the scheme Xcode writes for an application target. The
|
|
4061
|
+
* document carries build, launch, profile, analyze, and archive actions
|
|
4062
|
+
* wired to the app product, with Xcode's default configuration choices
|
|
4063
|
+
* of Debug for development actions and Release for profiling and
|
|
4064
|
+
* archiving.
|
|
4065
|
+
*/
|
|
4066
|
+
function createXcscheme(options) {
|
|
4067
|
+
const xcodeprojName = options.xcodeprojName ?? `${options.appName}.xcodeproj`;
|
|
4068
|
+
/**
|
|
4069
|
+
* Buildable references appear once per action. Each site gets its own
|
|
4070
|
+
* element so a mutation through one action cannot alias into another.
|
|
4071
|
+
*/
|
|
4072
|
+
const buildableReference = () => ({
|
|
4073
|
+
name: "BuildableReference",
|
|
4074
|
+
attributes: {
|
|
4075
|
+
BuildableIdentifier: "primary",
|
|
4076
|
+
BlueprintIdentifier: options.blueprintIdentifier ?? "",
|
|
4077
|
+
BuildableName: `${options.appName}.app`,
|
|
4078
|
+
BlueprintName: options.appName,
|
|
4079
|
+
ReferencedContainer: `container:${xcodeprojName}`
|
|
4080
|
+
},
|
|
4081
|
+
children: []
|
|
4082
|
+
});
|
|
4083
|
+
return {
|
|
4084
|
+
leading: [],
|
|
4085
|
+
root: {
|
|
4086
|
+
name: "Scheme",
|
|
4087
|
+
attributes: { version: "1.7" },
|
|
4088
|
+
children: [
|
|
4089
|
+
{
|
|
4090
|
+
name: "BuildAction",
|
|
4091
|
+
attributes: {
|
|
4092
|
+
parallelizeBuildables: "YES",
|
|
4093
|
+
buildImplicitDependencies: "YES"
|
|
4094
|
+
},
|
|
4095
|
+
children: [{
|
|
4096
|
+
name: "BuildActionEntries",
|
|
4097
|
+
attributes: {},
|
|
4098
|
+
children: [{
|
|
4099
|
+
name: "BuildActionEntry",
|
|
4100
|
+
attributes: {
|
|
4101
|
+
buildForTesting: "YES",
|
|
4102
|
+
buildForRunning: "YES",
|
|
4103
|
+
buildForProfiling: "YES",
|
|
4104
|
+
buildForArchiving: "YES",
|
|
4105
|
+
buildForAnalyzing: "YES"
|
|
4106
|
+
},
|
|
4107
|
+
children: [buildableReference()]
|
|
4108
|
+
}]
|
|
4109
|
+
}]
|
|
4110
|
+
},
|
|
4111
|
+
{
|
|
4112
|
+
name: "LaunchAction",
|
|
4113
|
+
attributes: {
|
|
4114
|
+
buildConfiguration: "Debug",
|
|
4115
|
+
selectedDebuggerIdentifier: "Xcode.DebuggerFoundation.Debugger.LLDB",
|
|
4116
|
+
selectedLauncherIdentifier: "Xcode.DebuggerFoundation.Launcher.LLDB",
|
|
4117
|
+
launchStyle: "0",
|
|
4118
|
+
useCustomWorkingDirectory: "NO",
|
|
4119
|
+
ignoresPersistentStateOnLaunch: "NO",
|
|
4120
|
+
debugDocumentVersioning: "YES",
|
|
4121
|
+
debugServiceExtension: "internal",
|
|
4122
|
+
allowLocationSimulation: "YES"
|
|
4123
|
+
},
|
|
4124
|
+
children: [{
|
|
4125
|
+
name: "BuildableProductRunnable",
|
|
4126
|
+
attributes: { runnableDebuggingMode: "0" },
|
|
4127
|
+
children: [buildableReference()]
|
|
4128
|
+
}]
|
|
4129
|
+
},
|
|
4130
|
+
{
|
|
4131
|
+
name: "ProfileAction",
|
|
4132
|
+
attributes: {
|
|
4133
|
+
buildConfiguration: "Release",
|
|
4134
|
+
shouldUseLaunchSchemeArgsEnv: "YES",
|
|
4135
|
+
savedToolIdentifier: "",
|
|
4136
|
+
useCustomWorkingDirectory: "NO",
|
|
4137
|
+
debugDocumentVersioning: "YES"
|
|
4138
|
+
},
|
|
4139
|
+
children: [{
|
|
4140
|
+
name: "BuildableProductRunnable",
|
|
4141
|
+
attributes: { runnableDebuggingMode: "0" },
|
|
4142
|
+
children: [buildableReference()]
|
|
4143
|
+
}]
|
|
4144
|
+
},
|
|
4145
|
+
{
|
|
4146
|
+
name: "AnalyzeAction",
|
|
4147
|
+
attributes: { buildConfiguration: "Debug" },
|
|
4148
|
+
children: []
|
|
4149
|
+
},
|
|
4150
|
+
{
|
|
4151
|
+
name: "ArchiveAction",
|
|
4152
|
+
attributes: {
|
|
4153
|
+
buildConfiguration: "Release",
|
|
4154
|
+
revealArchiveInOrganizer: "YES"
|
|
4155
|
+
},
|
|
4156
|
+
children: []
|
|
4157
|
+
}
|
|
4158
|
+
]
|
|
4159
|
+
},
|
|
4160
|
+
trailing: []
|
|
4161
|
+
};
|
|
4162
|
+
}
|
|
4163
|
+
//#endregion
|
|
4164
|
+
export { AggregateTarget, BuildConfiguration, BuildPhase, BuildRule, BuildableReference, ContainerItemProxy, CopyFilesDestination, FileReference, Group, Isa, LegacyTarget, NativeTarget, PbxprojBuildError, PbxprojParseError, ProductType, ReferenceProxy, RootProject, SyncRootGroup, Target, VersionGroup, XcodeModelError, XcodeObject, XcodeProject, Xcscheme, XcschemeBuildError, XcschemeParseError, buildPbxproj, buildXcscheme, createXcscheme, generateObjectId, isXcschemeElement, parsePbxproj, parseXcscheme, xcschemeElements };
|
package/package.json
CHANGED