@swiftbrowser/xcodeproj 0.1.0 → 0.2.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 +6 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/project.d.ts +73 -0
- package/dist/project.js +167 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -20,10 +20,13 @@ app.appIconName; // ASSETCATALOG_COMPILER_APPICON_NAME
|
|
|
20
20
|
app.swiftVersion; // SWIFT_VERSION
|
|
21
21
|
app.deploymentTarget; // IPHONEOS_DEPLOYMENT_TARGET
|
|
22
22
|
app.sources; // absolute paths of the .swift files, in navigator order
|
|
23
|
+
app.models; // the Core ML models (.mlmodel, .mlpackage) it compiles
|
|
23
24
|
app.resources; // absolute paths of the Resources build phase entries
|
|
24
25
|
app.assetCatalogs; // the *.xcassets among them (and in synchronized folders)
|
|
25
26
|
app.dependencies; // names of target dependencies
|
|
26
27
|
app.packageProducts; // Swift package products the target links
|
|
28
|
+
app.packageDependencies; // the same, each with its package (URL and version rule, or local path)
|
|
29
|
+
project.packages; // the project's Swift package references
|
|
27
30
|
app.settings('Release'); // merged build settings for a configuration
|
|
28
31
|
```
|
|
29
32
|
|
|
@@ -32,7 +35,9 @@ app.settings('Release'); // merged build settings for a configuration
|
|
|
32
35
|
- `readXcodeProject(xcodeprojPath)` parses `<xcodeprojPath>/project.pbxproj` and
|
|
33
36
|
returns an `XcodeProject`: `path`, `root` (the directory containing the
|
|
34
37
|
bundle, what `$(SRCROOT)` means), `objectVersion`, `targets` (every
|
|
35
|
-
`PBXNativeTarget`)
|
|
38
|
+
`PBXNativeTarget`), `packages` (its `XCRemoteSwiftPackageReference`s, with
|
|
39
|
+
the dependency rule, and `XCLocalSwiftPackageReference`s, with the absolute
|
|
40
|
+
path; each with SwiftPM's identity, see `packageIdentity`) and `appTargets()`.
|
|
36
41
|
- `parsePbxproj(text)` returns the raw object graph: `{ objectVersion,
|
|
37
42
|
rootObject, objects }`, where each object has an `isa` plus arbitrary fields
|
|
38
43
|
(strings, arrays, nested dictionaries). `parseOldStylePlist(text)` parses any
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
* - `findXcodeProjects(dir)` locates `.xcodeproj` bundles.
|
|
8
8
|
*/
|
|
9
9
|
export { parsePbxproj, parseOldStylePlist, tokenizePbxproj, PbxprojSyntaxError, type PbxValue, type PbxDict, type PbxObject, type PbxprojDocument, } from './pbxproj.js';
|
|
10
|
-
export { readXcodeProject, xcodeProjectFromDocument, expandVariables, APPLICATION_PRODUCT_TYPE, type XcodeProject, type XcodeTarget, } from './project.js';
|
|
10
|
+
export { readXcodeProject, xcodeProjectFromDocument, expandVariables, packageIdentity, APPLICATION_PRODUCT_TYPE, type XcodeProject, type XcodeTarget, type SwiftPackageReference, type SwiftPackageRequirement, type SwiftPackageProductDependency, } from './project.js';
|
|
11
11
|
export { findXcodeProjects } from './find.js';
|
|
12
12
|
export { parseXmlPlist, type PlistValue, type PlistDict } from './plist.js';
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
* - `findXcodeProjects(dir)` locates `.xcodeproj` bundles.
|
|
8
8
|
*/
|
|
9
9
|
export { parsePbxproj, parseOldStylePlist, tokenizePbxproj, PbxprojSyntaxError, } from './pbxproj.js';
|
|
10
|
-
export { readXcodeProject, xcodeProjectFromDocument, expandVariables, APPLICATION_PRODUCT_TYPE, } from './project.js';
|
|
10
|
+
export { readXcodeProject, xcodeProjectFromDocument, expandVariables, packageIdentity, APPLICATION_PRODUCT_TYPE, } from './project.js';
|
|
11
11
|
export { findXcodeProjects } from './find.js';
|
|
12
12
|
export { parseXmlPlist } from './plist.js';
|
package/dist/project.d.ts
CHANGED
|
@@ -21,6 +21,12 @@ export interface XcodeProject {
|
|
|
21
21
|
/** Directory containing the `.xcodeproj`: the source root, what `$(SRCROOT)` means. */
|
|
22
22
|
root: string;
|
|
23
23
|
objectVersion: number;
|
|
24
|
+
/**
|
|
25
|
+
* The Swift packages the project references (`PBXProject.packageReferences`):
|
|
26
|
+
* remote ones (`XCRemoteSwiftPackageReference`, with Xcode's dependency rule)
|
|
27
|
+
* and local ones (`XCLocalSwiftPackageReference`), in project order.
|
|
28
|
+
*/
|
|
29
|
+
packages: SwiftPackageReference[];
|
|
24
30
|
/** Every `PBXNativeTarget`, in project order. */
|
|
25
31
|
targets: XcodeTarget[];
|
|
26
32
|
/** The targets whose product is an application (`com.apple.product-type.application`). */
|
|
@@ -51,14 +57,33 @@ export interface XcodeTarget {
|
|
|
51
57
|
* folders (sorted).
|
|
52
58
|
*/
|
|
53
59
|
sources: string[];
|
|
60
|
+
/**
|
|
61
|
+
* Absolute paths of the Core ML models the target compiles (`.mlmodel`
|
|
62
|
+
* files, `.mlpackage` bundles): the Sources build phase in project order,
|
|
63
|
+
* then those in synchronized folders. Xcode generates a Swift class for each.
|
|
64
|
+
*/
|
|
65
|
+
models: string[];
|
|
54
66
|
/** Absolute paths of the files and folders in the Resources build phase, in project (navigator) order. */
|
|
55
67
|
resources: string[];
|
|
56
68
|
/** Absolute paths of the `*.xcassets` among the resources and in synchronized folders. */
|
|
57
69
|
assetCatalogs: string[];
|
|
70
|
+
/**
|
|
71
|
+
* Absolute paths of the target's localized string sources: string catalogs
|
|
72
|
+
* (`*.xcstrings`) and `.strings` / `.stringsdict` files (in `<language>.lproj`
|
|
73
|
+
* folders), among the resources and in synchronized folders. Xcode compiles
|
|
74
|
+
* them into the app's string tables.
|
|
75
|
+
*/
|
|
76
|
+
localizations: string[];
|
|
58
77
|
/** Names of the targets this one depends on (`PBXTargetDependency`). */
|
|
59
78
|
dependencies: string[];
|
|
60
79
|
/** Swift package products the target links (`XCSwiftPackageProductDependency.productName`). */
|
|
61
80
|
packageProducts: string[];
|
|
81
|
+
/**
|
|
82
|
+
* The same products with the package each comes from, when the project
|
|
83
|
+
* records it (`XCSwiftPackageProductDependency.package`; a local package
|
|
84
|
+
* added as a folder reference by older Xcodes has none).
|
|
85
|
+
*/
|
|
86
|
+
packageDependencies: SwiftPackageProductDependency[];
|
|
62
87
|
/**
|
|
63
88
|
* Merged build settings for a configuration: project-level settings first,
|
|
64
89
|
* target-level settings on top. `configuration` defaults to the target's
|
|
@@ -70,6 +95,54 @@ export interface XcodeTarget {
|
|
|
70
95
|
*/
|
|
71
96
|
settings(configuration?: string): Record<string, string>;
|
|
72
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* The version rule of a remote package, as Xcode's "Dependency Rule" stores
|
|
100
|
+
* it in `XCRemoteSwiftPackageReference.requirement`.
|
|
101
|
+
*/
|
|
102
|
+
export type SwiftPackageRequirement = {
|
|
103
|
+
kind: 'upToNextMajorVersion';
|
|
104
|
+
minimumVersion: string;
|
|
105
|
+
} | {
|
|
106
|
+
kind: 'upToNextMinorVersion';
|
|
107
|
+
minimumVersion: string;
|
|
108
|
+
} | {
|
|
109
|
+
kind: 'exactVersion';
|
|
110
|
+
version: string;
|
|
111
|
+
} | {
|
|
112
|
+
kind: 'versionRange';
|
|
113
|
+
minimumVersion: string;
|
|
114
|
+
maximumVersion: string;
|
|
115
|
+
} | {
|
|
116
|
+
kind: 'branch';
|
|
117
|
+
branch: string;
|
|
118
|
+
} | {
|
|
119
|
+
kind: 'revision';
|
|
120
|
+
revision: string;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* A Swift package the project references. `identity` is SwiftPM's name for
|
|
124
|
+
* it: the last path component of the URL (or folder), without `.git`,
|
|
125
|
+
* lower-cased; it is what `.product(name:package:)` names.
|
|
126
|
+
*/
|
|
127
|
+
export type SwiftPackageReference = {
|
|
128
|
+
kind: 'remote';
|
|
129
|
+
url: string;
|
|
130
|
+
identity: string;
|
|
131
|
+
/** Absent when the project's rule has a kind this reader does not know. */
|
|
132
|
+
requirement?: SwiftPackageRequirement;
|
|
133
|
+
} | {
|
|
134
|
+
kind: 'local';
|
|
135
|
+
/** Absolute path of the package folder (`relativePath` from the source root). */
|
|
136
|
+
path: string;
|
|
137
|
+
identity: string;
|
|
138
|
+
};
|
|
139
|
+
/** A package product a target links, with its package when the project says. */
|
|
140
|
+
export interface SwiftPackageProductDependency {
|
|
141
|
+
product: string;
|
|
142
|
+
package?: SwiftPackageReference;
|
|
143
|
+
}
|
|
144
|
+
/** SwiftPM's identity of a package at a URL or path: its last component, without `.git`, lower-cased. */
|
|
145
|
+
export declare function packageIdentity(location: string): string;
|
|
73
146
|
/** Reads `<xcodeprojPath>/project.pbxproj` and interprets it. */
|
|
74
147
|
export declare function readXcodeProject(xcodeprojPath: string): XcodeProject;
|
|
75
148
|
/**
|
package/dist/project.js
CHANGED
|
@@ -18,6 +18,11 @@ import * as path from 'node:path';
|
|
|
18
18
|
import { isDict, parsePbxproj } from './pbxproj.js';
|
|
19
19
|
import { parseXmlPlist } from './plist.js';
|
|
20
20
|
export const APPLICATION_PRODUCT_TYPE = 'com.apple.product-type.application';
|
|
21
|
+
/** SwiftPM's identity of a package at a URL or path: its last component, without `.git`, lower-cased. */
|
|
22
|
+
export function packageIdentity(location) {
|
|
23
|
+
const last = location.replace(/[/\\]+$/, '').split(/[/\\:]/).pop() ?? location;
|
|
24
|
+
return last.replace(/\.git$/i, '').toLowerCase();
|
|
25
|
+
}
|
|
21
26
|
/** Reads `<xcodeprojPath>/project.pbxproj` and interprets it. */
|
|
22
27
|
export function readXcodeProject(xcodeprojPath) {
|
|
23
28
|
const absolute = path.resolve(xcodeprojPath);
|
|
@@ -106,10 +111,14 @@ class ProjectReader {
|
|
|
106
111
|
if (target?.isa === 'PBXNativeTarget')
|
|
107
112
|
targets.push(this.readTarget(id, target));
|
|
108
113
|
}
|
|
114
|
+
const packages = strings(this.project['packageReferences'])
|
|
115
|
+
.map((id) => this.packageReference(id))
|
|
116
|
+
.filter((reference) => reference !== undefined);
|
|
109
117
|
return {
|
|
110
118
|
path: this.xcodeprojPath,
|
|
111
119
|
root: this.root,
|
|
112
120
|
objectVersion: this.doc.objectVersion,
|
|
121
|
+
packages,
|
|
113
122
|
targets,
|
|
114
123
|
appTargets: () => targets.filter((target) => target.productType === APPLICATION_PRODUCT_TYPE),
|
|
115
124
|
};
|
|
@@ -188,13 +197,16 @@ class ProjectReader {
|
|
|
188
197
|
}
|
|
189
198
|
// --- Synchronized folders (Xcode 16) ---
|
|
190
199
|
/**
|
|
191
|
-
* The `.swift` files and `.xcassets` folders under the
|
|
192
|
-
* folders, read from disk (sorted, recursive), minus
|
|
193
|
-
* exceptions. A folder missing on disk contributes
|
|
200
|
+
* The `.swift` files, Core ML models and `.xcassets` folders under the
|
|
201
|
+
* target's synchronized folders, read from disk (sorted, recursive), minus
|
|
202
|
+
* the target's membership exceptions. A folder missing on disk contributes
|
|
203
|
+
* nothing.
|
|
194
204
|
*/
|
|
195
205
|
synchronizedMembers(targetId, target) {
|
|
196
206
|
const sources = [];
|
|
207
|
+
const models = [];
|
|
197
208
|
const assetCatalogs = [];
|
|
209
|
+
const localizations = [];
|
|
198
210
|
for (const groupId of strings(target['fileSystemSynchronizedGroups'])) {
|
|
199
211
|
const group = this.objects[groupId];
|
|
200
212
|
if (!group || !SYNCHRONIZED_GROUP_ISAS.has(group.isa))
|
|
@@ -217,12 +229,16 @@ class ProjectReader {
|
|
|
217
229
|
return false;
|
|
218
230
|
if (entry.endsWith('.swift'))
|
|
219
231
|
sources.push(entry);
|
|
232
|
+
else if (isCoreMLModel(entry))
|
|
233
|
+
models.push(entry);
|
|
220
234
|
else if (entry.endsWith('.xcassets'))
|
|
221
235
|
assetCatalogs.push(entry);
|
|
236
|
+
else if (isLocalization(entry))
|
|
237
|
+
localizations.push(entry);
|
|
222
238
|
return true;
|
|
223
239
|
});
|
|
224
240
|
}
|
|
225
|
-
return { sources, assetCatalogs };
|
|
241
|
+
return { sources, models, assetCatalogs, localizations };
|
|
226
242
|
}
|
|
227
243
|
// --- Build settings ---
|
|
228
244
|
configurations(listId) {
|
|
@@ -295,7 +311,8 @@ class ProjectReader {
|
|
|
295
311
|
return result;
|
|
296
312
|
};
|
|
297
313
|
const defaults = settings();
|
|
298
|
-
const
|
|
314
|
+
const phaseFiles = this.buildPhaseFiles(target, 'PBXSourcesBuildPhase');
|
|
315
|
+
const phaseSources = phaseFiles.filter((p) => p.endsWith('.swift'));
|
|
299
316
|
const resources = this.buildPhaseFiles(target, 'PBXResourcesBuildPhase');
|
|
300
317
|
const synchronized = this.synchronizedMembers(targetId, target);
|
|
301
318
|
const productName = defaults['PRODUCT_NAME'] || str(target['productName']) || name;
|
|
@@ -304,10 +321,13 @@ class ProjectReader {
|
|
|
304
321
|
productType: str(target['productType']) ?? '',
|
|
305
322
|
productName,
|
|
306
323
|
sources: [...phaseSources, ...synchronized.sources],
|
|
324
|
+
models: [...phaseFiles.filter(isCoreMLModel), ...synchronized.models],
|
|
307
325
|
resources,
|
|
308
326
|
assetCatalogs: [...resources.filter((p) => p.endsWith('.xcassets')), ...synchronized.assetCatalogs],
|
|
327
|
+
localizations: [...localizationFiles(resources), ...synchronized.localizations],
|
|
309
328
|
dependencies: this.dependencyNames(target),
|
|
310
329
|
packageProducts: this.packageProductNames(target),
|
|
330
|
+
packageDependencies: this.packageProductDependencies(target),
|
|
311
331
|
settings,
|
|
312
332
|
};
|
|
313
333
|
const bundleIdentifier = defaults['PRODUCT_BUNDLE_IDENTIFIER'];
|
|
@@ -383,6 +403,124 @@ class ProjectReader {
|
|
|
383
403
|
}
|
|
384
404
|
return [...names];
|
|
385
405
|
}
|
|
406
|
+
/** `packageProductNames` with the package of each product, when recorded. */
|
|
407
|
+
packageProductDependencies(target) {
|
|
408
|
+
const seen = new Set();
|
|
409
|
+
const out = [];
|
|
410
|
+
const add = (productRefId) => {
|
|
411
|
+
const product = productRefId ? this.objects[productRefId] : undefined;
|
|
412
|
+
if (product?.isa !== 'XCSwiftPackageProductDependency')
|
|
413
|
+
return;
|
|
414
|
+
const name = str(product['productName']);
|
|
415
|
+
if (!name || seen.has(name))
|
|
416
|
+
return;
|
|
417
|
+
seen.add(name);
|
|
418
|
+
const reference = this.packageReference(str(product['package'])) ?? this.folderPackage(name);
|
|
419
|
+
out.push(reference ? { product: name, package: reference } : { product: name });
|
|
420
|
+
};
|
|
421
|
+
for (const id of strings(target['packageProductDependencies']))
|
|
422
|
+
add(id);
|
|
423
|
+
for (const phaseId of strings(target['buildPhases'])) {
|
|
424
|
+
const phase = this.objects[phaseId];
|
|
425
|
+
if (phase?.isa !== 'PBXFrameworksBuildPhase')
|
|
426
|
+
continue;
|
|
427
|
+
for (const buildFileId of strings(phase['files']))
|
|
428
|
+
add(str(this.objects[buildFileId]?.['productRef']));
|
|
429
|
+
}
|
|
430
|
+
return out;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Local packages added to the project as folders (how Xcode added a local
|
|
434
|
+
* package before `XCLocalSwiftPackageReference`, and still does when one is
|
|
435
|
+
* dragged in): a file reference to a folder holding a `Package.swift`. The
|
|
436
|
+
* product dependency then names no package, so the package is the folder
|
|
437
|
+
* whose manifest declares a library of that name (read from the manifest's
|
|
438
|
+
* text: `.library(name: "…"`), or, failing that, the folder of that name.
|
|
439
|
+
*/
|
|
440
|
+
folderPackagesByProduct;
|
|
441
|
+
folderPackage(product) {
|
|
442
|
+
if (!this.folderPackagesByProduct) {
|
|
443
|
+
const byProduct = new Map();
|
|
444
|
+
const byFolder = new Map();
|
|
445
|
+
for (const [id, object] of Object.entries(this.objects)) {
|
|
446
|
+
if (object.isa !== 'PBXFileReference')
|
|
447
|
+
continue;
|
|
448
|
+
const type = str(object['lastKnownFileType']) ?? str(object['explicitFileType']);
|
|
449
|
+
if (type !== undefined && type !== 'wrapper' && type !== 'folder')
|
|
450
|
+
continue;
|
|
451
|
+
const dir = this.resolvePath(id);
|
|
452
|
+
if (!dir)
|
|
453
|
+
continue;
|
|
454
|
+
let manifest;
|
|
455
|
+
try {
|
|
456
|
+
manifest = fs.readFileSync(path.join(dir, 'Package.swift'), 'utf8');
|
|
457
|
+
}
|
|
458
|
+
catch {
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
const reference = { kind: 'local', path: dir, identity: packageIdentity(dir) };
|
|
462
|
+
byFolder.set(path.basename(dir), reference);
|
|
463
|
+
for (const match of manifest.matchAll(/\.library\(\s*name:\s*"([^"]+)"/g)) {
|
|
464
|
+
if (!byProduct.has(match[1]))
|
|
465
|
+
byProduct.set(match[1], reference);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
for (const [name, reference] of byFolder)
|
|
469
|
+
if (!byProduct.has(name))
|
|
470
|
+
byProduct.set(name, reference);
|
|
471
|
+
this.folderPackagesByProduct = byProduct;
|
|
472
|
+
}
|
|
473
|
+
return this.folderPackagesByProduct.get(product);
|
|
474
|
+
}
|
|
475
|
+
/** An `XCRemoteSwiftPackageReference` or `XCLocalSwiftPackageReference`. */
|
|
476
|
+
packageReference(id) {
|
|
477
|
+
const object = id ? this.objects[id] : undefined;
|
|
478
|
+
if (object?.isa === 'XCRemoteSwiftPackageReference') {
|
|
479
|
+
const url = str(object['repositoryURL']);
|
|
480
|
+
if (!url)
|
|
481
|
+
return undefined;
|
|
482
|
+
const requirement = packageRequirement(object['requirement']);
|
|
483
|
+
return requirement ? { kind: 'remote', url, identity: packageIdentity(url), requirement } : { kind: 'remote', url, identity: packageIdentity(url) };
|
|
484
|
+
}
|
|
485
|
+
if (object?.isa === 'XCLocalSwiftPackageReference') {
|
|
486
|
+
const relative = str(object['relativePath']);
|
|
487
|
+
if (!relative)
|
|
488
|
+
return undefined;
|
|
489
|
+
const absolute = path.resolve(this.root, relative);
|
|
490
|
+
return { kind: 'local', path: absolute, identity: packageIdentity(absolute) };
|
|
491
|
+
}
|
|
492
|
+
return undefined;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
/** Xcode's dependency rule; undefined for a kind it does not spell out here. */
|
|
496
|
+
function packageRequirement(value) {
|
|
497
|
+
if (!isDict(value))
|
|
498
|
+
return undefined;
|
|
499
|
+
const minimumVersion = str(value['minimumVersion']);
|
|
500
|
+
switch (str(value['kind'])) {
|
|
501
|
+
case 'upToNextMajorVersion':
|
|
502
|
+
return minimumVersion ? { kind: 'upToNextMajorVersion', minimumVersion } : undefined;
|
|
503
|
+
case 'upToNextMinorVersion':
|
|
504
|
+
return minimumVersion ? { kind: 'upToNextMinorVersion', minimumVersion } : undefined;
|
|
505
|
+
case 'exactVersion': {
|
|
506
|
+
const version = str(value['version']);
|
|
507
|
+
return version ? { kind: 'exactVersion', version } : undefined;
|
|
508
|
+
}
|
|
509
|
+
case 'versionRange': {
|
|
510
|
+
const maximumVersion = str(value['maximumVersion']);
|
|
511
|
+
return minimumVersion && maximumVersion ? { kind: 'versionRange', minimumVersion, maximumVersion } : undefined;
|
|
512
|
+
}
|
|
513
|
+
case 'branch': {
|
|
514
|
+
const branch = str(value['branch']);
|
|
515
|
+
return branch ? { kind: 'branch', branch } : undefined;
|
|
516
|
+
}
|
|
517
|
+
case 'revision': {
|
|
518
|
+
const revision = str(value['revision']);
|
|
519
|
+
return revision ? { kind: 'revision', revision } : undefined;
|
|
520
|
+
}
|
|
521
|
+
default:
|
|
522
|
+
return undefined;
|
|
523
|
+
}
|
|
386
524
|
}
|
|
387
525
|
// ----- Helpers ----------------------------------------------------------------
|
|
388
526
|
/**
|
|
@@ -397,6 +535,25 @@ export function expandVariables(value, variables) {
|
|
|
397
535
|
* whether to descend into a directory; `.xcassets` bundles and dot-entries are
|
|
398
536
|
* never descended into. Unreadable directories are skipped.
|
|
399
537
|
*/
|
|
538
|
+
/** A string catalog, or a `.strings` / `.stringsdict` table. */
|
|
539
|
+
function isLocalization(file) {
|
|
540
|
+
return /\.(xcstrings|strings|stringsdict)$/.test(file);
|
|
541
|
+
}
|
|
542
|
+
/** The localization files among resources: catalogs and tables, and the tables in `.lproj` folders. */
|
|
543
|
+
function localizationFiles(resources) {
|
|
544
|
+
const out = [];
|
|
545
|
+
for (const resource of resources) {
|
|
546
|
+
if (isLocalization(resource)) {
|
|
547
|
+
out.push(resource);
|
|
548
|
+
}
|
|
549
|
+
else if (resource.endsWith('.lproj') && fs.existsSync(resource) && fs.statSync(resource).isDirectory()) {
|
|
550
|
+
for (const name of fs.readdirSync(resource).sort())
|
|
551
|
+
if (isLocalization(name))
|
|
552
|
+
out.push(path.join(resource, name));
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return out;
|
|
556
|
+
}
|
|
400
557
|
function walkSynchronizedFolder(dir, visit) {
|
|
401
558
|
let entries;
|
|
402
559
|
try {
|
|
@@ -411,7 +568,11 @@ function walkSynchronizedFolder(dir, visit) {
|
|
|
411
568
|
continue;
|
|
412
569
|
const full = path.join(dir, entry.name);
|
|
413
570
|
const descend = visit(full);
|
|
414
|
-
if (descend && entry.isDirectory() && !entry.name.endsWith('.xcassets'))
|
|
571
|
+
if (descend && entry.isDirectory() && !entry.name.endsWith('.xcassets') && !entry.name.endsWith('.mlpackage'))
|
|
415
572
|
walkSynchronizedFolder(full, visit);
|
|
416
573
|
}
|
|
417
574
|
}
|
|
575
|
+
/** A Core ML model Xcode compiles into a class: an `.mlmodel` file or an `.mlpackage` bundle. */
|
|
576
|
+
function isCoreMLModel(file) {
|
|
577
|
+
return file.endsWith('.mlmodel') || file.endsWith('.mlpackage');
|
|
578
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swiftbrowser/xcodeproj",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Reads Xcode projects (project.pbxproj): app targets, their Swift sources, asset catalogs and build settings. Pure TypeScript, no Xcode needed.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
|
-
"url": "git+https://github.com/
|
|
7
|
+
"url": "git+https://github.com/noya-app/SwiftBrowser.git",
|
|
8
8
|
"directory": "packages/xcodeproj"
|
|
9
9
|
},
|
|
10
10
|
"type": "module",
|