@swiftbrowser/cli 0.1.0 → 0.3.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.
Files changed (90) hide show
  1. package/README.md +340 -65
  2. package/dist/assetSymbols.d.ts +25 -0
  3. package/dist/assetSymbols.js +218 -0
  4. package/dist/assetSymbols.js.map +1 -0
  5. package/dist/build.d.ts +27 -2
  6. package/dist/build.js +71 -14
  7. package/dist/build.js.map +1 -1
  8. package/dist/buildSettings.d.ts +15 -0
  9. package/dist/buildSettings.js +74 -0
  10. package/dist/buildSettings.js.map +1 -0
  11. package/dist/cli.d.ts +6 -0
  12. package/dist/cli.js +66 -11
  13. package/dist/cli.js.map +1 -1
  14. package/dist/commands/build.js +6 -5
  15. package/dist/commands/build.js.map +1 -1
  16. package/dist/commands/check.js +2 -2
  17. package/dist/commands/check.js.map +1 -1
  18. package/dist/commands/dev.d.ts +5 -2
  19. package/dist/commands/dev.js +19 -13
  20. package/dist/commands/dev.js.map +1 -1
  21. package/dist/commands/init.d.ts +18 -0
  22. package/dist/commands/init.js +59 -0
  23. package/dist/commands/init.js.map +1 -0
  24. package/dist/commands/kit.d.ts +3 -0
  25. package/dist/commands/kit.js +21 -0
  26. package/dist/commands/kit.js.map +1 -0
  27. package/dist/commands/shared.d.ts +28 -4
  28. package/dist/commands/shared.js +106 -11
  29. package/dist/commands/shared.js.map +1 -1
  30. package/dist/commands/test.d.ts +22 -0
  31. package/dist/commands/test.js +95 -0
  32. package/dist/commands/test.js.map +1 -0
  33. package/dist/compat.d.ts +44 -6
  34. package/dist/compat.js +64 -36
  35. package/dist/compat.js.map +1 -1
  36. package/dist/config.d.ts +48 -10
  37. package/dist/config.js +64 -8
  38. package/dist/config.js.map +1 -1
  39. package/dist/coreml.d.ts +194 -0
  40. package/dist/coreml.js +626 -0
  41. package/dist/coreml.js.map +1 -0
  42. package/dist/icu.d.ts +17 -4
  43. package/dist/icu.js +32 -18
  44. package/dist/icu.js.map +1 -1
  45. package/dist/kit.d.ts +133 -0
  46. package/dist/kit.js +403 -0
  47. package/dist/kit.js.map +1 -0
  48. package/dist/objc.d.ts +31 -0
  49. package/dist/objc.js +319 -0
  50. package/dist/objc.js.map +1 -0
  51. package/dist/packagePatches.d.ts +24 -0
  52. package/dist/packagePatches.js +107 -0
  53. package/dist/packagePatches.js.map +1 -0
  54. package/dist/packages.d.ts +59 -0
  55. package/dist/packages.js +149 -0
  56. package/dist/packages.js.map +1 -0
  57. package/dist/project.d.ts +97 -4
  58. package/dist/project.js +157 -20
  59. package/dist/project.js.map +1 -1
  60. package/dist/protobuf.d.ts +62 -0
  61. package/dist/protobuf.js +233 -0
  62. package/dist/protobuf.js.map +1 -0
  63. package/dist/stage.d.ts +175 -28
  64. package/dist/stage.js +551 -85
  65. package/dist/stage.js.map +1 -1
  66. package/dist/strings.d.ts +44 -0
  67. package/dist/strings.js +386 -0
  68. package/dist/strings.js.map +1 -0
  69. package/dist/template.d.ts +39 -0
  70. package/dist/template.js +678 -0
  71. package/dist/template.js.map +1 -0
  72. package/dist/testRunner.d.ts +1 -0
  73. package/dist/testRunner.js +44 -0
  74. package/dist/testRunner.js.map +1 -0
  75. package/dist/threadStacks.d.ts +14 -0
  76. package/dist/threadStacks.js +78 -0
  77. package/dist/threadStacks.js.map +1 -0
  78. package/dist/toolchain.d.ts +7 -1
  79. package/dist/toolchain.js +8 -2
  80. package/dist/toolchain.js.map +1 -1
  81. package/dist/urlSchemes.d.ts +11 -0
  82. package/dist/urlSchemes.js +58 -0
  83. package/dist/urlSchemes.js.map +1 -0
  84. package/dist/vendor.d.ts +109 -0
  85. package/dist/vendor.js +440 -0
  86. package/dist/vendor.js.map +1 -0
  87. package/dist/wasm.d.ts +28 -0
  88. package/dist/wasm.js +84 -0
  89. package/dist/wasm.js.map +1 -0
  90. package/package.json +7 -6
@@ -0,0 +1,149 @@
1
+ // The Swift packages an Xcode project depends on (Package Dependencies in
2
+ // Xcode: `XCRemoteSwiftPackageReference`, `XCLocalSwiftPackageReference`).
3
+ // Each becomes one of two things in the staging package:
4
+ //
5
+ // - a real SwiftPM dependency, with the version rule the project records, so
6
+ // a pure-Swift package that builds for WASI is fetched and compiled into
7
+ // the module like any other code;
8
+ // - nothing, for a package SwiftBrowser knows cannot work in the browser
9
+ // (a camera scanner over AVFoundation and UIKit): the shim ships a module
10
+ // of the same name with the package's public API (`STAND_IN_FRAMEWORKS`
11
+ // in compat.ts), linked when a source imports it.
12
+ import path from 'node:path';
13
+ import { packageIdentity } from '@swiftbrowser/xcodeproj';
14
+ /**
15
+ * Packages the shim stands in for, by SwiftPM identity, with the reason the
16
+ * real one cannot be used and the products it provides. Each product is a
17
+ * module of the SwiftBrowser package (Sources/<Product>), listed in
18
+ * `STAND_IN_FRAMEWORKS`. When another package depends on one, the staging
19
+ * gives that package a redirect in its place (vendor.ts).
20
+ */
21
+ export const PACKAGE_STAND_INS = {};
22
+ /** `swiftbrowser.json`'s packages (config.ts), as the Xcode project's are: paths relative to `root`. */
23
+ export function configPackages(packages, root) {
24
+ return packages.map((entry) => {
25
+ if (entry.path) {
26
+ const dir = path.resolve(root, entry.path);
27
+ return { reference: { kind: 'local', path: dir, identity: packageIdentity(dir) }, products: [...entry.products] };
28
+ }
29
+ const url = entry.url;
30
+ const requirement = entry.from
31
+ ? { kind: 'upToNextMajorVersion', minimumVersion: entry.from }
32
+ : entry.exact
33
+ ? { kind: 'exactVersion', version: entry.exact }
34
+ : entry.branch
35
+ ? { kind: 'branch', branch: entry.branch }
36
+ : { kind: 'revision', revision: entry.revision };
37
+ return { reference: { kind: 'remote', url, identity: packageIdentity(url), requirement }, products: [...entry.products] };
38
+ });
39
+ }
40
+ /** Groups a target's package products by package, in the order the project lists them. */
41
+ export function groupPackageProducts(dependencies) {
42
+ const byIdentity = new Map();
43
+ const out = [];
44
+ for (const dependency of dependencies) {
45
+ if (!dependency.package) {
46
+ out.push({ products: [dependency.product] });
47
+ continue;
48
+ }
49
+ const key = `${dependency.package.kind}:${dependency.package.identity}`;
50
+ let entry = byIdentity.get(key);
51
+ if (!entry) {
52
+ entry = { reference: dependency.package, products: [] };
53
+ byIdentity.set(key, entry);
54
+ out.push(entry);
55
+ }
56
+ if (!entry.products.includes(dependency.product))
57
+ entry.products.push(dependency.product);
58
+ }
59
+ return out;
60
+ }
61
+ /** What the staging does with each package (`standIns` for tests: none today). */
62
+ export function planPackages(packages, standIns = PACKAGE_STAND_INS) {
63
+ return packages.map((entry) => {
64
+ const reference = entry.reference;
65
+ if (!reference) {
66
+ return { kind: 'unresolved', package: entry, problem: 'the project does not say which package it comes from' };
67
+ }
68
+ const standIn = standIns[reference.identity];
69
+ if (standIn)
70
+ return { kind: 'stand-in', package: entry, reason: standIn.reason };
71
+ if (reference.kind === 'local') {
72
+ return { kind: 'dependency', package: entry, identity: reference.identity, declaration: `.package(path: ${JSON.stringify(reference.path)})` };
73
+ }
74
+ if (!reference.requirement) {
75
+ return { kind: 'unresolved', package: entry, problem: 'its dependency rule is not one SwiftBrowser knows; set a version in Xcode' };
76
+ }
77
+ return {
78
+ kind: 'dependency',
79
+ package: entry,
80
+ identity: reference.identity,
81
+ declaration: `.package(url: ${JSON.stringify(reference.url)}, ${requirementArgument(reference.requirement)})`,
82
+ };
83
+ });
84
+ }
85
+ /** Xcode's dependency rule as the argument of SwiftPM's `.package(url:…)`. */
86
+ export function requirementArgument(requirement) {
87
+ switch (requirement.kind) {
88
+ case 'upToNextMajorVersion':
89
+ return `from: ${JSON.stringify(requirement.minimumVersion)}`;
90
+ case 'upToNextMinorVersion':
91
+ return `.upToNextMinor(from: ${JSON.stringify(requirement.minimumVersion)})`;
92
+ case 'exactVersion':
93
+ return `exact: ${JSON.stringify(requirement.version)}`;
94
+ case 'versionRange':
95
+ return `${JSON.stringify(requirement.minimumVersion)}..<${JSON.stringify(requirement.maximumVersion)}`;
96
+ case 'branch':
97
+ return `branch: ${JSON.stringify(requirement.branch)}`;
98
+ case 'revision':
99
+ return `revision: ${JSON.stringify(requirement.revision)}`;
100
+ }
101
+ }
102
+ /** The rule in words, as Xcode's package list shows it. */
103
+ export function describeRequirement(requirement) {
104
+ switch (requirement.kind) {
105
+ case 'upToNextMajorVersion':
106
+ return `up to next major from ${requirement.minimumVersion}`;
107
+ case 'upToNextMinorVersion':
108
+ return `up to next minor from ${requirement.minimumVersion}`;
109
+ case 'exactVersion':
110
+ return `exactly ${requirement.version}`;
111
+ case 'versionRange':
112
+ return `${requirement.minimumVersion} to ${requirement.maximumVersion}`;
113
+ case 'branch':
114
+ return `branch ${requirement.branch}`;
115
+ case 'revision':
116
+ return `revision ${requirement.revision.slice(0, 12)}`;
117
+ }
118
+ }
119
+ /** A package as people name it: the URL's last component (case kept), or the folder. */
120
+ export function packageDisplayName(entry) {
121
+ const reference = entry.reference;
122
+ if (!reference)
123
+ return entry.products.join(', ');
124
+ const location = reference.kind === 'remote' ? reference.url : reference.path;
125
+ const last = location.replace(/[/\\]+$/, '').split(/[/\\:]/).pop() ?? location;
126
+ return last.replace(/\.git$/i, '');
127
+ }
128
+ /** Where a package comes from, and on what rule, for the `check` report. */
129
+ export function packageSource(entry) {
130
+ const reference = entry.reference;
131
+ if (!reference)
132
+ return '';
133
+ if (reference.kind === 'local')
134
+ return reference.path;
135
+ const url = reference.url.replace(/^https?:\/\//, '').replace(/\.git$/i, '');
136
+ return reference.requirement ? `${url}, ${describeRequirement(reference.requirement)}` : url;
137
+ }
138
+ /** Module names the dependency packages provide, guessed as their product names → the package's name. */
139
+ export function packageModules(plans) {
140
+ const modules = new Map();
141
+ for (const plan of plans) {
142
+ if (plan.kind !== 'dependency')
143
+ continue;
144
+ for (const product of plan.package.products)
145
+ modules.set(product, packageDisplayName(plan.package));
146
+ }
147
+ return modules;
148
+ }
149
+ //# sourceMappingURL=packages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packages.js","sourceRoot":"","sources":["../src/packages.ts"],"names":[],"mappings":"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"}
package/dist/project.d.ts CHANGED
@@ -1,4 +1,7 @@
1
- import { type FoundationMode, type SwiftLanguageMode } from './config.js';
1
+ import type { SwiftPackageReference } from '@swiftbrowser/xcodeproj';
2
+ import { type SwiftLanguageMode } from './config.js';
3
+ import { type AppPackage } from './packages.js';
4
+ import { type SwiftSettings } from './buildSettings.js';
2
5
  export interface ResolvedApp {
3
6
  /** Swift module name (`[A-Za-z_][A-Za-z0-9_]*`); also the URL name. */
4
7
  name: string;
@@ -7,17 +10,53 @@ export interface ResolvedApp {
7
10
  root: string;
8
11
  /** Absolute paths of the Swift sources, in build order. */
9
12
  sources: string[];
13
+ /**
14
+ * Absolute paths of the Core ML models (`.mlmodel`, `.mlpackage`) the app
15
+ * compiles; each becomes a generated class (coreml.ts). Absent means none.
16
+ */
17
+ models?: string[];
10
18
  /** Absolute paths of `*.xcassets` directories. */
11
19
  catalogs: string[];
12
20
  /** Absolute paths of the resource files and folders `Bundle.main` serves. */
13
21
  resources: string[];
22
+ /**
23
+ * Absolute paths of the string catalogs (`.xcstrings`) and `.strings` /
24
+ * `.stringsdict` files compiled into the app's string tables (strings.ts).
25
+ * Absent means none.
26
+ */
27
+ localizations?: string[];
14
28
  /** A PNG app icon, when one was found. */
15
29
  icon?: string;
16
30
  swiftLanguageMode: SwiftLanguageMode;
17
- foundation: FoundationMode;
31
+ /**
32
+ * The Xcode target's concurrency settings (default actor isolation,
33
+ * upcoming features; buildSettings.ts). Absent for a folder of sources.
34
+ */
35
+ swiftSettings?: SwiftSettings;
36
+ /**
37
+ * The generated asset symbols (assetSymbols.ts): `false` when the Xcode
38
+ * target turns them off (`ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOLS =
39
+ * NO`), `'extensions'` when it also wants the `Color` and `ShapeStyle`
40
+ * members (`ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS =
41
+ * YES`, which Xcode's templates set). Absent: the resource members only.
42
+ */
43
+ assetSymbols?: false | 'extensions';
18
44
  /** Languages whose ICU data a full-Foundation build keeps. */
19
45
  locales: string[];
46
+ /** The app's URL schemes (Info.plist `CFBundleURLTypes`, or `urlSchemes` in swiftbrowser.json); see urlSchemes.ts. */
47
+ urlSchemes?: string[];
48
+ /** A CORS proxy the page retries refused cross-origin reads through (`corsProxy` in swiftbrowser.json, `--cors-proxy`). */
49
+ corsProxy?: string;
50
+ /** Hosts the app's requests may not reach (`blockHosts` in swiftbrowser.json, `--block-host`). */
51
+ blockHosts?: string[];
52
+ /** Whether ICU's collation tables ship (`collation` in swiftbrowser.json; default true). */
53
+ collation: boolean;
20
54
  defines: string[];
55
+ /**
56
+ * The Swift packages the Xcode target links (Package Dependencies), and
57
+ * those `swiftbrowser.json` lists, each with its products. See packages.ts.
58
+ */
59
+ packages: AppPackage[];
21
60
  /** How the app was found. */
22
61
  origin: {
23
62
  kind: 'xcodeproj';
@@ -26,23 +65,76 @@ export interface ResolvedApp {
26
65
  } | {
27
66
  kind: 'folder';
28
67
  };
68
+ /**
69
+ * The app's unit tests, for `swiftbrowser test` (testTargets): the Xcode
70
+ * unit-test targets hosted by the app, or a folder's `*Tests` folders.
71
+ * Absent means none.
72
+ */
73
+ tests?: TestTarget[];
29
74
  }
75
+ /** A unit-test target of the app: a module of tests that `@testable import` it. */
76
+ export interface TestTarget {
77
+ /** Swift module name. */
78
+ name: string;
79
+ /** Absolute paths of its Swift sources. */
80
+ sources: string[];
81
+ }
82
+ /** Xcode's product type of a unit-test bundle (UI tests, `bundle.ui-testing`, drive a simulator and are not run). */
83
+ export declare const UNIT_TEST_PRODUCT_TYPE = "com.apple.product-type.bundle.unit-test";
30
84
  export interface ResolveOptions {
31
85
  target?: string;
32
86
  configFile?: string;
33
- foundation?: FoundationMode;
34
87
  }
35
88
  /** `Guess The Flag` → `GuessTheFlag`; a leading digit gets an underscore. */
36
89
  export declare function moduleName(raw: string): string;
37
- /** Every `.swift` file and `.xcassets` folder under `dir`, by convention. */
90
+ /** A Core ML model Xcode compiles into a class: an `.mlmodel` file or an `.mlpackage` bundle. */
91
+ export declare function isCoreMLModel(file: string): boolean;
92
+ /**
93
+ * The unit tests of a folder of sources, by convention: the `.swift` files
94
+ * under its `Tests` and `*Tests` folders (`*UITests` excepted), which the app's
95
+ * sources leave out, as one target `<app>Tests`.
96
+ */
97
+ export declare function discoverTests(dir: string, appName: string): TestTarget[];
98
+ /**
99
+ * The Xcode unit-test targets that test `app`: those that depend on it or
100
+ * load into it (`TEST_HOST`, `BUNDLE_LOADER` naming its product). Each is a
101
+ * module of its own, as in Xcode.
102
+ */
103
+ export declare function xcodeTestTargets(targets: readonly XcodeTargetLike[], app: XcodeTargetLike): TestTarget[];
104
+ /** Every `.swift` file, Core ML model and `.xcassets` folder under `dir`, by convention. */
38
105
  export declare function discoverByConvention(dir: string): {
39
106
  sources: string[];
107
+ models: string[];
40
108
  catalogs: string[];
109
+ localizations: string[];
41
110
  };
42
111
  /** The largest PNG of the app icon set, for the home-screen manifest. */
43
112
  export declare function findAppIcon(catalogs: readonly string[], iconName?: string): string | undefined;
44
113
  /** `.xcodeproj` bundles directly in `dir`, else one level down. */
45
114
  export declare function xcodeProjectsIn(dir: string): string[];
115
+ interface XcodeTargetLike {
116
+ name: string;
117
+ productName: string;
118
+ productType: string;
119
+ displayName?: string;
120
+ appIconName?: string;
121
+ swiftVersion?: string;
122
+ sources: string[];
123
+ /** Absent in @swiftbrowser/xcodeproj releases before Core ML models were read. */
124
+ models?: string[];
125
+ /** String catalogs and `.strings` / `.stringsdict` files; absent in older @swiftbrowser/xcodeproj releases. */
126
+ localizations?: string[];
127
+ resources: string[];
128
+ assetCatalogs: string[];
129
+ packageDependencies?: {
130
+ product: string;
131
+ package?: SwiftPackageReference;
132
+ }[];
133
+ /** Names of the targets it depends on; absent in older @swiftbrowser/xcodeproj releases' typings. */
134
+ dependencies?: string[];
135
+ /** Build settings (`INFOPLIST_FILE` for the URL schemes); absent in older @swiftbrowser/xcodeproj releases' typings. */
136
+ settings?(configuration?: string): Record<string, string>;
137
+ }
46
138
  /** The Xcode target's copied resources: everything but asset catalogs and compiled resources, in project order. */
47
139
  export declare function bundleResources(resources: readonly string[]): string[];
48
140
  /** Resolves `input` (an .xcodeproj, a folder with one, or a folder of sources) to an app. */
@@ -54,3 +146,4 @@ export declare function resolveApp(input: string, options?: ResolveOptions): Pro
54
146
  * against its directory; the path comes back unchanged when nothing matches.
55
147
  */
56
148
  export declare function repairCase(file: string): string;
149
+ export {};
package/dist/project.js CHANGED
@@ -5,6 +5,12 @@ import { existsSync, readdirSync, statSync } from 'node:fs';
5
5
  import path from 'node:path';
6
6
  import { loadConfig } from './config.js';
7
7
  import { CliError } from './log.js';
8
+ import { configPackages, groupPackageProducts } from './packages.js';
9
+ import { swiftSettingsOf } from './buildSettings.js';
10
+ import { isLocalizationFile, localizationsAmong } from './strings.js';
11
+ import { urlSchemesOfTarget } from './urlSchemes.js';
12
+ /** Xcode's product type of a unit-test bundle (UI tests, `bundle.ui-testing`, drive a simulator and are not run). */
13
+ export const UNIT_TEST_PRODUCT_TYPE = 'com.apple.product-type.bundle.unit-test';
8
14
  /** Directories never searched for sources or catalogs. */
9
15
  const SKIP_DIRS = new Set(['.build', '.swiftbrowser', '.git', 'node_modules', 'DerivedData', 'Pods', 'Carthage', 'build', 'Preview Content']);
10
16
  /** `Guess The Flag` → `GuessTheFlag`; a leading digit gets an underscore. */
@@ -20,7 +26,7 @@ function walk(dir, visit) {
20
26
  if (entry.isDirectory()) {
21
27
  if (SKIP_DIRS.has(entry.name) || entry.name.startsWith('.') || /(Tests|UITests)$/.test(entry.name))
22
28
  continue;
23
- if (entry.name.endsWith('.xcassets') || entry.name.endsWith('.xcodeproj') || entry.name.endsWith('.xcworkspace')) {
29
+ if (entry.name.endsWith('.xcassets') || entry.name.endsWith('.xcodeproj') || entry.name.endsWith('.xcworkspace') || entry.name.endsWith('.mlpackage')) {
24
30
  visit(full, statSync(full));
25
31
  continue;
26
32
  }
@@ -31,31 +37,100 @@ function walk(dir, visit) {
31
37
  }
32
38
  }
33
39
  }
34
- /** Every `.swift` file and `.xcassets` folder under `dir`, by convention. */
40
+ /** A Core ML model Xcode compiles into a class: an `.mlmodel` file or an `.mlpackage` bundle. */
41
+ export function isCoreMLModel(file) {
42
+ return file.endsWith('.mlmodel') || file.endsWith('.mlpackage');
43
+ }
44
+ /** A folder of tests by convention: `Tests`, `MyAppTests`, but not `MyAppUITests`. */
45
+ const TESTS_DIR = /Tests$/;
46
+ const UI_TESTS_DIR = /UITests$/;
47
+ /**
48
+ * The unit tests of a folder of sources, by convention: the `.swift` files
49
+ * under its `Tests` and `*Tests` folders (`*UITests` excepted), which the app's
50
+ * sources leave out, as one target `<app>Tests`.
51
+ */
52
+ export function discoverTests(dir, appName) {
53
+ const sources = [];
54
+ const visit = (current, inTests) => {
55
+ for (const entry of readdirSync(current, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
56
+ const full = path.join(current, entry.name);
57
+ if (entry.isDirectory()) {
58
+ if (SKIP_DIRS.has(entry.name) || entry.name.startsWith('.') || UI_TESTS_DIR.test(entry.name) || entry.name.endsWith('.xcodeproj'))
59
+ continue;
60
+ visit(full, inTests || TESTS_DIR.test(entry.name));
61
+ }
62
+ else if (inTests && entry.isFile() && entry.name.endsWith('.swift')) {
63
+ sources.push(full);
64
+ }
65
+ }
66
+ };
67
+ visit(dir, false);
68
+ return sources.length > 0 ? [{ name: `${appName}Tests`, sources }] : [];
69
+ }
70
+ /**
71
+ * The Xcode unit-test targets that test `app`: those that depend on it or
72
+ * load into it (`TEST_HOST`, `BUNDLE_LOADER` naming its product). Each is a
73
+ * module of its own, as in Xcode.
74
+ */
75
+ export function xcodeTestTargets(targets, app) {
76
+ const product = app.productName || app.name;
77
+ const hosted = (target) => {
78
+ if (target.dependencies?.includes(app.name))
79
+ return true;
80
+ const settings = target.settings?.() ?? {};
81
+ return [settings['TEST_HOST'], settings['BUNDLE_LOADER']].some((value) => value !== undefined && value.includes(`/${product}.app/`));
82
+ };
83
+ return targets
84
+ .filter((target) => target.productType === UNIT_TEST_PRODUCT_TYPE && hosted(target))
85
+ .map((target) => ({ name: moduleName(target.productName || target.name), sources: target.sources.filter((file) => file.endsWith('.swift')) }))
86
+ .filter((target) => target.sources.length > 0);
87
+ }
88
+ /** Every `.swift` file, Core ML model and `.xcassets` folder under `dir`, by convention. */
35
89
  export function discoverByConvention(dir) {
36
90
  const sources = [];
91
+ const models = [];
37
92
  const catalogs = [];
93
+ const localizations = [];
38
94
  walk(dir, (file) => {
39
95
  if (file.endsWith('.swift') && !/(Tests|UITests)\.swift$/.test(path.basename(file)) && !/Package\.swift$/.test(file))
40
96
  sources.push(file);
97
+ else if (isCoreMLModel(file))
98
+ models.push(file);
41
99
  else if (file.endsWith('.xcassets'))
42
100
  catalogs.push(file);
101
+ else if (file.endsWith('.xcstrings') || (isLocalizationFile(file) && path.dirname(file).endsWith('.lproj')))
102
+ localizations.push(file);
43
103
  });
44
- return { sources, catalogs };
104
+ return { sources, models, catalogs, localizations };
105
+ }
106
+ /** A string catalog, table or `.lproj` folder: compiled into string tables, not copied. */
107
+ function isLocalizationSource(file) {
108
+ return isLocalizationFile(file) || file.endsWith('.lproj');
45
109
  }
46
- /** Swift files named by a config `sources` list: files as is, directories recursively. */
110
+ /**
111
+ * Swift files and Core ML models named by a config `sources` list: files as
112
+ * is (a model by its extension), directories recursively.
113
+ */
47
114
  function expandSources(root, entries) {
48
- const out = [];
115
+ const sources = [];
116
+ const models = [];
49
117
  for (const entry of entries) {
50
118
  const full = path.resolve(root, entry);
51
119
  if (!existsSync(full))
52
120
  throw new CliError(`sources entry not found: ${entry}`);
53
- if (statSync(full).isDirectory())
54
- out.push(...discoverByConvention(full).sources);
55
- else
56
- out.push(full);
121
+ if (isCoreMLModel(full)) {
122
+ models.push(full);
123
+ }
124
+ else if (statSync(full).isDirectory()) {
125
+ const found = discoverByConvention(full);
126
+ sources.push(...found.sources);
127
+ models.push(...found.models);
128
+ }
129
+ else {
130
+ sources.push(full);
131
+ }
57
132
  }
58
- return out;
133
+ return { sources, models };
59
134
  }
60
135
  function applyExcludes(root, files, exclude) {
61
136
  if (!exclude || exclude.length === 0)
@@ -137,9 +212,9 @@ export async function resolveApp(input, options = {}) {
137
212
  }
138
213
  const config = loadConfig(root, options.configFile);
139
214
  const swiftLanguageMode = config.swiftLanguageMode ?? '5';
140
- const foundation = options.foundation ?? config.foundation ?? 'full';
141
215
  const defines = config.defines ?? [];
142
216
  const locales = config.locales ?? ['en'];
217
+ const collation = config.collation ?? true;
143
218
  if (projectPath) {
144
219
  const project = await readXcode(projectPath);
145
220
  const apps = project.appTargets();
@@ -161,48 +236,92 @@ export async function resolveApp(input, options = {}) {
161
236
  }
162
237
  if (!chosen)
163
238
  throw new CliError('no target chosen');
164
- const sources = applyExcludes(root, config.sources ? expandSources(root, config.sources) : chosen.sources.filter((f) => f.endsWith('.swift')), config.exclude);
239
+ const listed = config.sources ? expandSources(root, config.sources) : null;
240
+ const sources = applyExcludes(root, listed ? listed.sources : chosen.sources.filter((f) => f.endsWith('.swift')), config.exclude);
241
+ const models = applyExcludes(root, listed ? listed.models : (chosen.models ?? []), config.exclude);
165
242
  const catalogs = config.catalogs ? config.catalogs.map((c) => path.resolve(root, c)) : chosen.assetCatalogs;
166
- const resources = config.resources ? config.resources.map((r) => path.resolve(root, r)) : bundleResources(chosen.resources ?? []);
243
+ const listedResources = config.resources?.map((r) => path.resolve(root, r));
244
+ const resources = listedResources ? listedResources.filter((r) => !isLocalizationSource(r)) : bundleResources(chosen.resources ?? []);
245
+ const localizations = listedResources ? localizationsAmong(listedResources) : (chosen.localizations ?? []);
167
246
  const icon = config.icon ? path.resolve(root, config.icon) : findAppIcon(catalogs, chosen.appIconName);
168
247
  const app = {
169
248
  name: config.name ?? moduleName(chosen.productName || chosen.name),
170
249
  displayName: config.displayName ?? chosen.displayName ?? chosen.productName ?? chosen.name,
171
250
  root,
172
251
  sources,
252
+ models,
173
253
  catalogs,
174
254
  resources,
255
+ ...(localizations.length > 0 ? { localizations } : {}),
175
256
  swiftLanguageMode: config.swiftLanguageMode ?? (chosen.swiftVersion?.startsWith('6') ? '6' : swiftLanguageMode),
176
- foundation,
177
257
  locales,
258
+ collation,
178
259
  defines,
260
+ packages: [...groupPackageProducts(chosen.packageDependencies ?? []), ...configPackages(config.packages ?? [], root)],
179
261
  origin: { kind: 'xcodeproj', project: projectPath, target: chosen.name },
180
262
  };
181
263
  if (icon)
182
264
  app.icon = icon;
265
+ const buildSettings = chosen.settings?.();
266
+ if (buildSettings) {
267
+ const settings = swiftSettingsOf(buildSettings, app.swiftLanguageMode);
268
+ if (settings.defaultIsolationMainActor || settings.upcomingFeatures.length > 0)
269
+ app.swiftSettings = settings;
270
+ if (/^(NO|FALSE|0)$/i.test(buildSettings['ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOLS'] ?? ''))
271
+ app.assetSymbols = false;
272
+ else if (/^(YES|TRUE|1)$/i.test(buildSettings['ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS'] ?? ''))
273
+ app.assetSymbols = 'extensions';
274
+ }
275
+ const tests = xcodeTestTargets(project.targets, chosen);
276
+ if (tests.length > 0)
277
+ app.tests = tests;
278
+ const urlSchemes = config.urlSchemes ?? urlSchemesOfTarget(chosen, root);
279
+ if (urlSchemes.length > 0)
280
+ app.urlSchemes = urlSchemes;
281
+ if (config.corsProxy)
282
+ app.corsProxy = config.corsProxy;
283
+ if (config.blockHosts?.length)
284
+ app.blockHosts = config.blockHosts;
183
285
  return finish(app);
184
286
  }
185
287
  const discovered = discoverByConvention(root);
186
- const sources = applyExcludes(root, config.sources ? expandSources(root, config.sources) : discovered.sources, config.exclude);
288
+ const listed = config.sources ? expandSources(root, config.sources) : null;
289
+ const sources = applyExcludes(root, listed ? listed.sources : discovered.sources, config.exclude);
290
+ const models = applyExcludes(root, listed ? listed.models : discovered.models, config.exclude);
187
291
  const catalogs = config.catalogs ? config.catalogs.map((c) => path.resolve(root, c)) : discovered.catalogs;
188
- // A plain folder has no Copy Bundle Resources phase: resources come from the config only.
189
- const resources = (config.resources ?? []).map((r) => path.resolve(root, r));
292
+ // A plain folder has no Copy Bundle Resources phase: resources come from the
293
+ // config only, string tables from the config or the folder.
294
+ const listedResources = (config.resources ?? []).map((r) => path.resolve(root, r));
295
+ const resources = listedResources.filter((r) => !isLocalizationSource(r));
296
+ const localizations = config.resources ? localizationsAmong(listedResources) : discovered.localizations;
190
297
  const icon = config.icon ? path.resolve(root, config.icon) : findAppIcon(catalogs);
191
298
  const app = {
192
299
  name: config.name ?? moduleName(path.basename(root)),
193
300
  displayName: config.displayName ?? path.basename(root),
194
301
  root,
195
302
  sources,
303
+ models,
196
304
  catalogs,
197
305
  resources,
306
+ ...(localizations.length > 0 ? { localizations } : {}),
198
307
  swiftLanguageMode,
199
- foundation,
200
308
  locales,
309
+ collation,
201
310
  defines,
311
+ packages: configPackages(config.packages ?? [], root),
202
312
  origin: { kind: 'folder' },
203
313
  };
204
314
  if (icon)
205
315
  app.icon = icon;
316
+ const tests = discoverTests(root, app.name);
317
+ if (tests.length > 0)
318
+ app.tests = tests;
319
+ if (config.urlSchemes?.length)
320
+ app.urlSchemes = config.urlSchemes;
321
+ if (config.corsProxy)
322
+ app.corsProxy = config.corsProxy;
323
+ if (config.blockHosts?.length)
324
+ app.blockHosts = config.blockHosts;
206
325
  return finish(app);
207
326
  }
208
327
  /**
@@ -239,6 +358,8 @@ function finish(app) {
239
358
  // An Xcode project made on macOS can name files whose case differs from the
240
359
  // disk's; repair what can be repaired, report the rest.
241
360
  app.sources = app.sources.map(repairCase);
361
+ if (app.models)
362
+ app.models = app.models.map(repairCase);
242
363
  app.catalogs = app.catalogs.map(repairCase);
243
364
  app.resources = app.resources.map(repairCase);
244
365
  if (app.icon)
@@ -247,18 +368,34 @@ function finish(app) {
247
368
  if (missing.length > 0) {
248
369
  throw new CliError(`${missing.length} source file${missing.length === 1 ? '' : 's'} named by the project ${missing.length === 1 ? 'does' : 'do'} not exist:\n ${missing.join('\n ')}`);
249
370
  }
371
+ const missingModels = (app.models ?? []).filter((file) => !existsSync(file));
372
+ if (missingModels.length > 0) {
373
+ throw new CliError(`${missingModels.length} Core ML model${missingModels.length === 1 ? '' : 's'} named by the project ${missingModels.length === 1 ? 'does' : 'do'} not exist:\n ${missingModels.join('\n ')}`);
374
+ }
250
375
  const missingResources = app.resources.filter((file) => !existsSync(file));
251
376
  if (missingResources.length > 0) {
252
377
  throw new CliError(`${missingResources.length} resource${missingResources.length === 1 ? '' : 's'} named by the project ${missingResources.length === 1 ? 'does' : 'do'} not exist:\n ${missingResources.join('\n ')}`);
253
378
  }
379
+ uniqueFileNames(app.sources);
380
+ for (const target of app.tests ?? []) {
381
+ target.sources = target.sources.map(repairCase);
382
+ const gone = target.sources.filter((file) => !existsSync(file));
383
+ if (gone.length > 0) {
384
+ throw new CliError(`${gone.length} test source${gone.length === 1 ? '' : 's'} named by the project ${gone.length === 1 ? 'does' : 'do'} not exist:\n ${gone.join('\n ')}`);
385
+ }
386
+ uniqueFileNames(target.sources);
387
+ }
388
+ return app;
389
+ }
390
+ /** Swift needs the files of a module to have unique names. */
391
+ function uniqueFileNames(files) {
254
392
  const names = new Map();
255
- for (const file of app.sources) {
393
+ for (const file of files) {
256
394
  const base = path.basename(file);
257
395
  const other = names.get(base);
258
396
  if (other)
259
397
  throw new CliError(`two sources share the file name ${base} (Swift needs unique names in a module):\n ${other}\n ${file}`);
260
398
  names.set(base, file);
261
399
  }
262
- return app;
263
400
  }
264
401
  //# sourceMappingURL=project.js.map