@swiftbrowser/cli 0.1.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 +166 -0
- package/bin/swiftbrowser.js +11 -0
- package/dist/build.d.ts +36 -0
- package/dist/build.js +124 -0
- package/dist/build.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +137 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/build.d.ts +8 -0
- package/dist/commands/build.js +33 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/check.d.ts +4 -0
- package/dist/commands/check.js +27 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/dev.d.ts +19 -0
- package/dist/commands/dev.js +83 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/doctor.d.ts +12 -0
- package/dist/commands/doctor.js +66 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/shared.d.ts +23 -0
- package/dist/commands/shared.js +73 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/compat.d.ts +27 -0
- package/dist/compat.js +111 -0
- package/dist/compat.js.map +1 -0
- package/dist/config.d.ts +45 -0
- package/dist/config.js +70 -0
- package/dist/config.js.map +1 -0
- package/dist/icu.d.ts +94 -0
- package/dist/icu.js +368 -0
- package/dist/icu.js.map +1 -0
- package/dist/log.d.ts +16 -0
- package/dist/log.js +28 -0
- package/dist/log.js.map +1 -0
- package/dist/project.d.ts +56 -0
- package/dist/project.js +264 -0
- package/dist/project.js.map +1 -0
- package/dist/stage.d.ts +78 -0
- package/dist/stage.js +344 -0
- package/dist/stage.js.map +1 -0
- package/dist/toolchain.d.ts +42 -0
- package/dist/toolchain.js +159 -0
- package/dist/toolchain.js.map +1 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# @swiftbrowser/cli
|
|
2
|
+
|
|
3
|
+
Run an iOS SwiftUI app in the browser, from its Xcode project, without a Mac.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npx @swiftbrowser/cli doctor --install # Swift 6.4, the wasm SDK, wasm-opt
|
|
7
|
+
npx @swiftbrowser/cli dev ~/Projects/MyApp # http://localhost:5173, rebuilds on save
|
|
8
|
+
npx @swiftbrowser/cli build ~/Projects/MyApp # ./dist: a static site to deploy anywhere
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The app's sources compile unchanged against [SwiftBrowser](https://github.com/dabbott/SwiftBrowser)'s
|
|
12
|
+
SwiftUI shim and run as WebAssembly inside an iPhone frame (or full screen on a
|
|
13
|
+
phone: open the dev server's QR code, then Share → Add to Home Screen). What
|
|
14
|
+
the shim does not provide is a compile error, never a wrong rendering.
|
|
15
|
+
|
|
16
|
+
## Commands
|
|
17
|
+
|
|
18
|
+
| Command | What it does |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `doctor [--install]` | Checks Node ≥ 22, Swift 6.4, the Swift SDK for WebAssembly and `wasm-opt`; `--install` fetches the SDK (`swift sdk install`) and Binaryen into `~/.cache/swiftbrowser`. Swift itself comes from [swiftly](https://www.swift.org/install/) (`swiftly install 6.4.0`), or point `SWIFT_TOOLCHAIN_BIN` at a toolchain's `usr/bin`. |
|
|
21
|
+
| `check <path>` | Lists what the app imports (supported / rewritten / unsupported, with the usual way around each), then compiles the app natively against the shim. Seconds, no wasm; every error names your file and line. |
|
|
22
|
+
| `dev <path>` | Builds the app for WebAssembly and serves it with the SwiftBrowser dev server: landing page with a QR code at `/`, the app at `/app/?app=<Name>`; saving a Swift file rebuilds and reloads with the app's state carried over. `--port`, `--host` (expose on the LAN for phones), `--open`. |
|
|
23
|
+
| `build <path> [-o dist]` | A release build, optimized and stripped with `wasm-opt` (about 18 MB for a small app, 4.6 MB with brotli; 4.5 MB and 1.2 MB in lean mode), and a complete static site around it. `--no-optimize` skips wasm-opt. |
|
|
24
|
+
|
|
25
|
+
`<path>` is an `.xcodeproj`, a folder holding one (directly or one level
|
|
26
|
+
down), or a plain folder of Swift sources. Options for all three:
|
|
27
|
+
`--target <name>` when the project has several app targets, `--config <file>`,
|
|
28
|
+
`--foundation lean|full`, `--verbose`.
|
|
29
|
+
|
|
30
|
+
## What happens to your project
|
|
31
|
+
|
|
32
|
+
Nothing, outside `<project>/.swiftbrowser/` (add it to `.gitignore`). The CLI
|
|
33
|
+
reads the Xcode project (`project.pbxproj`, including Xcode 16 synchronized
|
|
34
|
+
folders) for the app target's Swift sources, asset catalogs, display name and
|
|
35
|
+
app icon, then writes a SwiftPM package under `.swiftbrowser/<Name>/` whose one
|
|
36
|
+
executable target is a copy of those sources, adjusted for the shim:
|
|
37
|
+
|
|
38
|
+
- `import SwiftUI` also imports Foundation and the shim's `FoundationBridge`
|
|
39
|
+
(Apple's SwiftUI re-exports Foundation and Combine, so `UUID`, `JSONEncoder`,
|
|
40
|
+
`UserDefaults`, `Timer.publish` are in scope without an import). In lean mode
|
|
41
|
+
it imports `FoundationEssentials` and `FoundationLite` instead, and
|
|
42
|
+
`import Foundation` becomes those two (see Foundation below). Both also
|
|
43
|
+
import Observation, so `@Observable` works in a file that imports only
|
|
44
|
+
Foundation, as it does under Xcode.
|
|
45
|
+
- A generated `_SwiftBrowserFoundation.swift` settles the names both the shim
|
|
46
|
+
and Foundation declare (`CGFloat`, `CGPoint`, `CGSize`, `CGRect`, `Timer`,
|
|
47
|
+
`RunLoop`, `UserDefaults`, `Bundle`) with typealiases, in the shim's favor.
|
|
48
|
+
- The target's Copy Bundle Resources (or `resources` in `swiftbrowser.json`)
|
|
49
|
+
are served next to the module and mounted at `/bundle` before the app
|
|
50
|
+
starts, so `Bundle.main.url(forResource:withExtension:)` and
|
|
51
|
+
`Data(contentsOf:)` read them. Asset catalogs are handled separately
|
|
52
|
+
(`Image("name")`, `Color("name")`).
|
|
53
|
+
- `#Preview { … }` blocks are blanked (the shim has no preview macro).
|
|
54
|
+
- `SWIFTBROWSER` is defined, so platform-specific code can go behind
|
|
55
|
+
`#if SWIFTBROWSER` / `#if !SWIFTBROWSER`, like `#if os(iOS)`.
|
|
56
|
+
|
|
57
|
+
Every change keeps line numbers, so compiler diagnostics point at your files.
|
|
58
|
+
The package depends on `@swiftbrowser/swift` (the shim at the version this CLI
|
|
59
|
+
was released with).
|
|
60
|
+
|
|
61
|
+
## Foundation
|
|
62
|
+
|
|
63
|
+
Two modes, measured through the same pipeline (release, `wasm-opt -Os`,
|
|
64
|
+
stripped) on the Counter example:
|
|
65
|
+
|
|
66
|
+
| Mode | What `import Foundation` means | Module | gzipped | brotli |
|
|
67
|
+
|---|---|---|---|---|
|
|
68
|
+
| `full` (default) | the wasm SDK's Foundation, ICU data trimmed to English | 18.3 MB | 6.7 MB | 4.6 MB |
|
|
69
|
+
| `full`, `"locales": ["*"]` | the same with ICU's whole data package | 47 MB | 19.4 MB | 12.5 MB |
|
|
70
|
+
| `lean` | `FoundationEssentials` + `FoundationLite` | 4.5 MB | 1.7 MB | 1.2 MB |
|
|
71
|
+
|
|
72
|
+
**Full** links the real Foundation (swift-corelibs-foundation, as the wasm SDK
|
|
73
|
+
ships it): `DateFormatter`, `NumberFormatter`, `NSString`, `Locale`-aware
|
|
74
|
+
`formatted()`, `Measurement`, `NSRegularExpression`, `UserDefaults`, and the
|
|
75
|
+
rest compile as on iOS. The SDK's Foundation carries ICU's complete 34 MB
|
|
76
|
+
locale data package; the CLI pulls that package out of the SDK's archive, keeps
|
|
77
|
+
the locale-independent tables plus the bundles of the languages in `"locales"`
|
|
78
|
+
(default `["en"]`, 4.7 MB; the region part of a tag is ignored, every regional
|
|
79
|
+
variant of a language stays), and links the trimmed package in the original's
|
|
80
|
+
place. Formatting for a language that was dropped falls back to ICU's root
|
|
81
|
+
locale (`11/14/2023, 22:13` and `€ 0.00` for French instead of
|
|
82
|
+
`14/11/2023 22:13` and `0,00 €`); it never fails. Each language adds 100 to
|
|
83
|
+
300 kB, Chinese 1.7 MB (its collation table); `["*"]` keeps everything. The
|
|
84
|
+
trimmed package is cached per SDK and language set under
|
|
85
|
+
`~/.cache/swiftbrowser/icu/`; the SDK itself is not modified. What Foundation
|
|
86
|
+
lacks on WASI, the shim's `FoundationBridge` supplies: `Timer` (corelibs' needs
|
|
87
|
+
a run loop WASI does not have) with `Timer.publish` + `onReceive`, and
|
|
88
|
+
`RunLoop.main` for its signature; `UserDefaults`, which Foundation's cannot
|
|
89
|
+
persist on WASI, saved by the browser per app (localStorage) and restored at
|
|
90
|
+
the next launch; and `Bundle`, whose Foundation counterpart traps, reading the
|
|
91
|
+
app's resources. Known gaps of corelibs Foundation on WASI: `NotificationCenter`'s
|
|
92
|
+
closure observers and `OperationQueue` are missing.
|
|
93
|
+
|
|
94
|
+
### Locale and time zone
|
|
95
|
+
|
|
96
|
+
The app runs in the viewer's time zone and, within the languages it ships, the
|
|
97
|
+
viewer's language, as an iOS app follows the device: `Locale.current`,
|
|
98
|
+
`TimeZone.current`, `formatted()`, `DateFormatter` and `Text(_:format:)` all
|
|
99
|
+
follow. The browser's first preferred language whose data the module carries
|
|
100
|
+
(`"locales"`) is used with its region, so a British visitor to an app shipping
|
|
101
|
+
`["en"]` gets `14/11/2023`; a visitor whose language is not shipped gets the
|
|
102
|
+
first shipped language rather than a half-formatted root locale. Add
|
|
103
|
+
`?locale=fr-FR` to the app URL to try a locale without changing the browser
|
|
104
|
+
(an Xcode scheme's "App Language"). The headless harness pins `en_001` and
|
|
105
|
+
GMT unless `SB_LOCALE` / `TZ` are set, so snapshots stay stable. Lean mode
|
|
106
|
+
formats in US English regardless.
|
|
107
|
+
|
|
108
|
+
The hook behind this: swift-foundation hard-codes `en_001` on WASI, so the
|
|
109
|
+
CLI redirects that one function to FoundationBridge at link time
|
|
110
|
+
(`--wrap`); it depends on the SDK's swift-foundation, which the CLI pins and
|
|
111
|
+
CI checks. Lean mode does not have it.
|
|
112
|
+
|
|
113
|
+
**Lean** (`--foundation lean`, or `"foundation": "lean"` in `swiftbrowser.json`)
|
|
114
|
+
is for apps that must stay small: `FoundationEssentials` (Date, Calendar, UUID,
|
|
115
|
+
Data, JSON, URL) plus `FoundationLite`, SwiftBrowser's Foundation-compatible
|
|
116
|
+
layer (number and date format styles, `IndexSet`, `CharacterSet`,
|
|
117
|
+
`UserDefaults`, `Timer`). A Foundation API outside it is a compile error naming
|
|
118
|
+
it. Output is fixed to US English conventions.
|
|
119
|
+
|
|
120
|
+
## swiftbrowser.json (optional)
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"name": "MyApp",
|
|
125
|
+
"displayName": "My App",
|
|
126
|
+
"target": "MyApp",
|
|
127
|
+
"sources": ["MyApp"],
|
|
128
|
+
"exclude": ["MyApp/Watch"],
|
|
129
|
+
"catalogs": ["MyApp/Assets.xcassets"],
|
|
130
|
+
"resources": ["MyApp/missions.json", "MyApp/Data"],
|
|
131
|
+
"icon": "MyApp/Assets.xcassets/AppIcon.appiconset/icon-1024.png",
|
|
132
|
+
"swiftLanguageMode": "5",
|
|
133
|
+
"foundation": "full",
|
|
134
|
+
"locales": ["en"],
|
|
135
|
+
"defines": ["DEMO"]
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Everything is optional; `sources`, `catalogs` and `resources` replace what the
|
|
140
|
+
Xcode project says when set (a plain folder has resources only through the
|
|
141
|
+
config); `locales` matters in full Foundation mode only (see above).
|
|
142
|
+
|
|
143
|
+
## Frameworks
|
|
144
|
+
|
|
145
|
+
Supported: SwiftUI (the shim's compatibility matrix is in the repository
|
|
146
|
+
README), Observation, Swift Charts, Foundation (full mode), FoundationEssentials
|
|
147
|
+
and FoundationLite (lean mode).
|
|
148
|
+
`check` names the frameworks it knows have no browser counterpart yet (UIKit,
|
|
149
|
+
SwiftData, Core Data, Combine, MapKit, Core ML, AVFoundation, UserNotifications,
|
|
150
|
+
StoreKit, WidgetKit, PhotosUI, WebKit, …) with the usual way out: keep that code
|
|
151
|
+
behind `#if !SWIFTBROWSER` and give the browser a stand-in behind `#if SWIFTBROWSER`.
|
|
152
|
+
|
|
153
|
+
## Example
|
|
154
|
+
|
|
155
|
+
Twelve of the nineteen "Hacking with SwiftUI" projects run from their Xcode
|
|
156
|
+
projects without a change (WeSplit, Guess the Flag, Views and Modifiers,
|
|
157
|
+
WordScramble, Animations, iExpense, Moonshot, Navigation, Accessibility,
|
|
158
|
+
Flashzilla, Layout and Geometry, SnowSeeker); `check` names what stops each
|
|
159
|
+
of the others, which all need a framework the browser has no counterpart for
|
|
160
|
+
yet (Core ML, URLSession, SwiftData, Core Image, MapKit):
|
|
161
|
+
|
|
162
|
+
```sh
|
|
163
|
+
git clone --depth 1 https://github.com/twostraws/HackingWithSwift
|
|
164
|
+
npx @swiftbrowser/cli check HackingWithSwift/SwiftUI/project4 # BetterRest: Core ML
|
|
165
|
+
npx @swiftbrowser/cli dev HackingWithSwift/SwiftUI/project17 # Flashzilla
|
|
166
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Entry point of the `swiftbrowser` command (see ../src/cli.ts).
|
|
3
|
+
import { main } from '../dist/cli.js';
|
|
4
|
+
|
|
5
|
+
main(process.argv.slice(2)).then(
|
|
6
|
+
(code) => process.exit(code),
|
|
7
|
+
(error) => {
|
|
8
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
9
|
+
process.exit(1);
|
|
10
|
+
},
|
|
11
|
+
);
|
package/dist/build.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ResolvedApp } from './project.js';
|
|
2
|
+
import { type StagedApp } from './stage.js';
|
|
3
|
+
import { type Toolchain } from './toolchain.js';
|
|
4
|
+
export interface RunResult {
|
|
5
|
+
status: number;
|
|
6
|
+
output: string;
|
|
7
|
+
}
|
|
8
|
+
/** Runs a command, capturing stdout+stderr interleaved; `onOutput` streams it as it arrives. */
|
|
9
|
+
export declare function run(command: string, args: string[], options: {
|
|
10
|
+
cwd: string;
|
|
11
|
+
env: NodeJS.ProcessEnv;
|
|
12
|
+
onOutput?: (chunk: string) => void;
|
|
13
|
+
}): Promise<RunResult>;
|
|
14
|
+
/** Only the diagnostics and the summary lines of a SwiftPM build log. */
|
|
15
|
+
export declare function stripAnsi(text: string): string;
|
|
16
|
+
export declare function diagnosticsOnly(output: string): string;
|
|
17
|
+
export interface BuildOptions {
|
|
18
|
+
toolchain: Toolchain;
|
|
19
|
+
staged: StagedApp;
|
|
20
|
+
app: ResolvedApp;
|
|
21
|
+
/** Stream the compiler output while it runs. */
|
|
22
|
+
verbose?: boolean;
|
|
23
|
+
onOutput?: (chunk: string) => void;
|
|
24
|
+
}
|
|
25
|
+
/** `swift build` for the host: compile errors only, no module produced that we use. */
|
|
26
|
+
export declare function checkHost(options: BuildOptions): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Builds the module for WebAssembly (release) and copies it to `wasmOutput(app)`;
|
|
29
|
+
* with `wasmOpt` set it is also optimized for size and stripped (the ~2.5×
|
|
30
|
+
* smaller download of a `build`; `dev` skips it for quick rebuilds).
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildWasm(options: BuildOptions & {
|
|
33
|
+
wasmOpt?: string | null;
|
|
34
|
+
}): Promise<string>;
|
|
35
|
+
export declare function fileSize(file: string): number;
|
|
36
|
+
export declare function formatBytes(bytes: number): string;
|
package/dist/build.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// Compiling the staging package: natively for `check` (fast, same
|
|
2
|
+
// diagnostics), for WebAssembly for `dev` and `build`, then wasm-opt.
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { copyFileSync, existsSync, mkdirSync, statSync } from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { CliError } from './log.js';
|
|
7
|
+
import { remapDiagnostics, wasmOutput } from './stage.js';
|
|
8
|
+
import { WASM_SDK, toolchainEnv } from './toolchain.js';
|
|
9
|
+
/** Runs a command, capturing stdout+stderr interleaved; `onOutput` streams it as it arrives. */
|
|
10
|
+
export function run(command, args, options) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const child = spawn(command, args, { cwd: options.cwd, env: options.env, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
13
|
+
let output = '';
|
|
14
|
+
const collect = (chunk) => {
|
|
15
|
+
const text = chunk.toString('utf8');
|
|
16
|
+
output += text;
|
|
17
|
+
options.onOutput?.(text);
|
|
18
|
+
};
|
|
19
|
+
child.stdout.on('data', collect);
|
|
20
|
+
child.stderr.on('data', collect);
|
|
21
|
+
child.on('error', reject);
|
|
22
|
+
child.on('close', (status) => resolve({ status: status ?? 1, output }));
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/** Only the diagnostics and the summary lines of a SwiftPM build log. */
|
|
26
|
+
export function stripAnsi(text) {
|
|
27
|
+
// eslint-disable-next-line no-control-regex
|
|
28
|
+
return text.replace(/\x1b\[[0-9;]*[A-Za-z]/g, '');
|
|
29
|
+
}
|
|
30
|
+
export function diagnosticsOnly(output) {
|
|
31
|
+
const lines = stripAnsi(output).split('\n');
|
|
32
|
+
const kept = [];
|
|
33
|
+
let inDiagnostic = false;
|
|
34
|
+
for (const line of lines) {
|
|
35
|
+
// SwiftPM's own summary lines repeat what the compiler already said.
|
|
36
|
+
if (/^error: (SwiftCompile|Build failed|fatalError)/.test(line) || /^\s+(cd |builtin-)/.test(line)) {
|
|
37
|
+
inDiagnostic = false;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (/:\d+:\d+: warning: /.test(line)) {
|
|
41
|
+
// Warnings (mostly the shim's own, compiled in the same build) are noise next to the errors.
|
|
42
|
+
inDiagnostic = false;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (/:\d+:\d+: note: /.test(line)) {
|
|
46
|
+
// A note explains the diagnostic above it; keep it only after an error.
|
|
47
|
+
if (inDiagnostic)
|
|
48
|
+
kept.push(line);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (/:\d+:\d+: error: /.test(line) || /^error: /.test(line)) {
|
|
52
|
+
inDiagnostic = true;
|
|
53
|
+
kept.push(line);
|
|
54
|
+
}
|
|
55
|
+
else if (inDiagnostic && (/^\s/.test(line) || line === '')) {
|
|
56
|
+
// Source excerpts and carets belong to the diagnostic above.
|
|
57
|
+
kept.push(line);
|
|
58
|
+
if (line === '')
|
|
59
|
+
inDiagnostic = false;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
inDiagnostic = false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return kept.join('\n').trim();
|
|
66
|
+
}
|
|
67
|
+
/** `swift build` for the host: compile errors only, no module produced that we use. */
|
|
68
|
+
export async function checkHost(options) {
|
|
69
|
+
const { toolchain, staged, app } = options;
|
|
70
|
+
const result = await run(toolchain.swift, ['build', '--product', app.name], {
|
|
71
|
+
cwd: staged.dir,
|
|
72
|
+
env: toolchainEnv(toolchain),
|
|
73
|
+
...(options.onOutput ? { onOutput: options.onOutput } : {}),
|
|
74
|
+
});
|
|
75
|
+
if (result.status !== 0)
|
|
76
|
+
throw new CliError(remapDiagnostics(diagnosticsOnly(result.output) || result.output, staged));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Builds the module for WebAssembly (release) and copies it to `wasmOutput(app)`;
|
|
80
|
+
* with `wasmOpt` set it is also optimized for size and stripped (the ~2.5×
|
|
81
|
+
* smaller download of a `build`; `dev` skips it for quick rebuilds).
|
|
82
|
+
*/
|
|
83
|
+
export async function buildWasm(options) {
|
|
84
|
+
const { toolchain, staged, app } = options;
|
|
85
|
+
const env = toolchainEnv(toolchain);
|
|
86
|
+
const common = ['--swift-sdk', WASM_SDK, '-c', 'release'];
|
|
87
|
+
const result = await run(toolchain.swift, ['build', ...common, '--product', app.name], {
|
|
88
|
+
cwd: staged.dir,
|
|
89
|
+
env,
|
|
90
|
+
...(options.onOutput ? { onOutput: options.onOutput } : {}),
|
|
91
|
+
});
|
|
92
|
+
if (result.status !== 0)
|
|
93
|
+
throw new CliError(remapDiagnostics(diagnosticsOnly(result.output) || result.output, staged));
|
|
94
|
+
const binPath = await run(toolchain.swift, ['build', ...common, '--show-bin-path'], { cwd: staged.dir, env });
|
|
95
|
+
if (binPath.status !== 0)
|
|
96
|
+
throw new CliError(`swift build --show-bin-path failed:\n${binPath.output}`);
|
|
97
|
+
const built = path.join(binPath.output.trim().split('\n').pop(), `${app.name}.wasm`);
|
|
98
|
+
if (!existsSync(built))
|
|
99
|
+
throw new CliError(`expected ${built} after the build`);
|
|
100
|
+
const out = wasmOutput(app);
|
|
101
|
+
mkdirSync(path.dirname(out), { recursive: true });
|
|
102
|
+
if (options.wasmOpt) {
|
|
103
|
+
// -Os without the slowest -Oz passes; the features come from the module.
|
|
104
|
+
// --strip-debug drops DWARF and names, --strip-producers the toolchain record.
|
|
105
|
+
const opt = await run(options.wasmOpt, ['-Os', '--strip-debug', '--strip-producers', built, '-o', out], { cwd: staged.dir, env });
|
|
106
|
+
if (opt.status !== 0)
|
|
107
|
+
throw new CliError(`wasm-opt failed:\n${opt.output}`);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
copyFileSync(built, out);
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
export function fileSize(file) {
|
|
115
|
+
return statSync(file).size;
|
|
116
|
+
}
|
|
117
|
+
export function formatBytes(bytes) {
|
|
118
|
+
if (bytes >= 1_000_000)
|
|
119
|
+
return `${(bytes / 1_000_000).toFixed(1)} MB`;
|
|
120
|
+
if (bytes >= 1_000)
|
|
121
|
+
return `${(bytes / 1_000).toFixed(0)} kB`;
|
|
122
|
+
return `${bytes} B`;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// `swiftbrowser <command> [path] [options]`: argument parsing and dispatch.
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { CliError, bold, dim, error } from './log.js';
|
|
4
|
+
export const VERSION = '0.1.0';
|
|
5
|
+
const HELP = `${bold('swiftbrowser')} ${VERSION}: run an iOS SwiftUI app in the browser.
|
|
6
|
+
|
|
7
|
+
${bold('Usage')}
|
|
8
|
+
swiftbrowser doctor [--install] check (and install) Swift, the wasm SDK and wasm-opt
|
|
9
|
+
swiftbrowser check <path> [options] import report and a native compile against the shim
|
|
10
|
+
swiftbrowser dev <path> [options] run the app with rebuild on save
|
|
11
|
+
swiftbrowser build <path> [-o <dir>] optimized module and a static site
|
|
12
|
+
|
|
13
|
+
${bold('<path>')} is an .xcodeproj, a folder holding one, or a folder of Swift sources
|
|
14
|
+
(optionally with a swiftbrowser.json).
|
|
15
|
+
|
|
16
|
+
${bold('Options')}
|
|
17
|
+
--target <name> the Xcode app target to build (when there are several)
|
|
18
|
+
--config <file> a swiftbrowser.json other than the project's
|
|
19
|
+
--foundation <mode> full (default: the wasm SDK's Foundation, ICU data trimmed to the
|
|
20
|
+
project's "locales"; ~19 MB) or lean (import Foundation →
|
|
21
|
+
FoundationEssentials + FoundationLite; ~5 MB, smaller API)
|
|
22
|
+
--port <n>, --host dev server port (default 5173) and LAN exposure (dev)
|
|
23
|
+
--open open the browser (dev)
|
|
24
|
+
-o, --out <dir> output directory (build; default <project>/dist)
|
|
25
|
+
--no-optimize skip wasm-opt (build)
|
|
26
|
+
--verbose stream the compiler output
|
|
27
|
+
-h, --help, --version
|
|
28
|
+
`;
|
|
29
|
+
export async function main(argv) {
|
|
30
|
+
let parsed;
|
|
31
|
+
try {
|
|
32
|
+
parsed = parseArgs({
|
|
33
|
+
args: argv,
|
|
34
|
+
allowPositionals: true,
|
|
35
|
+
options: {
|
|
36
|
+
help: { type: 'boolean', short: 'h' },
|
|
37
|
+
version: { type: 'boolean' },
|
|
38
|
+
install: { type: 'boolean' },
|
|
39
|
+
target: { type: 'string' },
|
|
40
|
+
config: { type: 'string' },
|
|
41
|
+
foundation: { type: 'string' },
|
|
42
|
+
port: { type: 'string' },
|
|
43
|
+
host: { type: 'string' },
|
|
44
|
+
open: { type: 'boolean' },
|
|
45
|
+
out: { type: 'string', short: 'o' },
|
|
46
|
+
optimize: { type: 'boolean', default: true },
|
|
47
|
+
'no-optimize': { type: 'boolean' },
|
|
48
|
+
verbose: { type: 'boolean' },
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
error(err instanceof Error ? err.message : String(err));
|
|
54
|
+
console.log(HELP);
|
|
55
|
+
return 2;
|
|
56
|
+
}
|
|
57
|
+
const { values, positionals } = parsed;
|
|
58
|
+
if (values.version) {
|
|
59
|
+
console.log(VERSION);
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
const [command, input] = positionals;
|
|
63
|
+
if (values.help || !command) {
|
|
64
|
+
console.log(HELP);
|
|
65
|
+
return command ? 0 : 2;
|
|
66
|
+
}
|
|
67
|
+
const foundationArg = values.foundation;
|
|
68
|
+
if (foundationArg !== undefined && foundationArg !== 'lean' && foundationArg !== 'full') {
|
|
69
|
+
error(`--foundation must be lean or full`);
|
|
70
|
+
return 2;
|
|
71
|
+
}
|
|
72
|
+
const foundation = foundationArg;
|
|
73
|
+
const appOptions = {
|
|
74
|
+
...(values.target ? { target: values.target } : {}),
|
|
75
|
+
...(values.config ? { config: values.config } : {}),
|
|
76
|
+
...(foundation ? { foundation } : {}),
|
|
77
|
+
};
|
|
78
|
+
const needsPath = () => {
|
|
79
|
+
if (!input)
|
|
80
|
+
throw new CliError(`${command} needs a path: an .xcodeproj, a folder with one, or a folder of Swift sources`, 2);
|
|
81
|
+
return input;
|
|
82
|
+
};
|
|
83
|
+
try {
|
|
84
|
+
switch (command) {
|
|
85
|
+
case 'doctor': {
|
|
86
|
+
const { doctor } = await import('./commands/doctor.js');
|
|
87
|
+
const result = await doctor({ ...(values.install ? { install: true } : {}) });
|
|
88
|
+
return result.toolchain && result.wasmSdk ? 0 : 1;
|
|
89
|
+
}
|
|
90
|
+
case 'check': {
|
|
91
|
+
const { check } = await import('./commands/check.js');
|
|
92
|
+
await check(needsPath(), { ...appOptions, ...(values.verbose ? { verbose: true } : {}) });
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
case 'dev': {
|
|
96
|
+
const { dev } = await import('./commands/dev.js');
|
|
97
|
+
const port = values.port !== undefined ? Number(values.port) : undefined;
|
|
98
|
+
if (port !== undefined && !Number.isInteger(port))
|
|
99
|
+
throw new CliError('--port must be a number', 2);
|
|
100
|
+
await dev(needsPath(), {
|
|
101
|
+
...appOptions,
|
|
102
|
+
...(port !== undefined ? { port } : {}),
|
|
103
|
+
...(values.host !== undefined ? { host: values.host === '' ? true : values.host } : {}),
|
|
104
|
+
...(values.open ? { open: true } : {}),
|
|
105
|
+
});
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
case 'build': {
|
|
109
|
+
const { build } = await import('./commands/build.js');
|
|
110
|
+
await build(needsPath(), {
|
|
111
|
+
...appOptions,
|
|
112
|
+
...(values.out ? { outDir: values.out } : {}),
|
|
113
|
+
...(values['no-optimize'] ? { optimize: false } : {}),
|
|
114
|
+
...(values.verbose ? { verbose: true } : {}),
|
|
115
|
+
});
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
default:
|
|
119
|
+
error(`unknown command: ${command}`);
|
|
120
|
+
console.log(HELP);
|
|
121
|
+
return 2;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
if (err instanceof CliError) {
|
|
126
|
+
error(err.message);
|
|
127
|
+
return err.exitCode;
|
|
128
|
+
}
|
|
129
|
+
error(err instanceof Error ? (err.stack ?? err.message) : String(err));
|
|
130
|
+
return 1;
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
if (process.stdout.isTTY)
|
|
134
|
+
process.stdout.write(dim(''));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type AppOptions } from './shared.js';
|
|
2
|
+
export interface BuildOptions extends AppOptions {
|
|
3
|
+
outDir?: string;
|
|
4
|
+
verbose?: boolean;
|
|
5
|
+
/** Skip wasm-opt even when available. */
|
|
6
|
+
optimize?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function build(input: string, options: BuildOptions): Promise<void>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// `swiftbrowser build <path> -o <dir>`: an optimized module and a complete
|
|
2
|
+
// static site around it (landing page, app page, catalog, manifest).
|
|
3
|
+
import { mkdirSync, rmSync } from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { buildSite } from '@swiftbrowser/web';
|
|
6
|
+
import { fileSize, formatBytes } from '../build.js';
|
|
7
|
+
import { bold, dim, info, ok, warn } from '../log.js';
|
|
8
|
+
import { findWasmOpt } from '../toolchain.js';
|
|
9
|
+
import { appSpec, icuDataFor } from './dev.js';
|
|
10
|
+
import { describeApp, describeReport, prepare, requireToolchain } from './shared.js';
|
|
11
|
+
export async function build(input, options) {
|
|
12
|
+
const { app, report, staged } = await prepare(input, options);
|
|
13
|
+
describeApp(app);
|
|
14
|
+
describeReport(report, app);
|
|
15
|
+
const toolchain = requireToolchain({ wasm: true });
|
|
16
|
+
const wasmOpt = options.optimize === false ? null : findWasmOpt();
|
|
17
|
+
if (options.optimize !== false && !wasmOpt) {
|
|
18
|
+
info(warn('wasm-opt not found; the module is not optimized (`swiftbrowser doctor --install` downloads binaryen)'));
|
|
19
|
+
}
|
|
20
|
+
const outDir = path.resolve(options.outDir ?? path.join(app.root, 'dist'));
|
|
21
|
+
const spec = appSpec(app, staged, toolchain, { wasmOpt, icu: icuDataFor(app, toolchain) });
|
|
22
|
+
info(dim(`\nbuilding ${app.name} for WebAssembly (release${wasmOpt ? ', wasm-opt -Os' : ''})…`));
|
|
23
|
+
const started = Date.now();
|
|
24
|
+
// Re-stages with the ICU data, builds, and optimizes into `spec.wasm`.
|
|
25
|
+
await spec.build();
|
|
26
|
+
info(ok(`${bold(app.name)}.wasm ${dim(`${formatBytes(fileSize(spec.wasm))}, ${((Date.now() - started) / 1000).toFixed(1)} s`)}`));
|
|
27
|
+
rmSync(outDir, { recursive: true, force: true });
|
|
28
|
+
mkdirSync(outDir, { recursive: true });
|
|
29
|
+
await buildSite({ apps: [spec], outDir, logLevel: options.verbose ? 'info' : 'warn' });
|
|
30
|
+
const shown = path.relative(process.cwd(), outDir);
|
|
31
|
+
info(ok(`site written to ${bold(shown && !shown.startsWith('..') ? shown : outDir)} ${dim(`(serve it from any static host; the app page is /app/?app=${app.name})`)}`));
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// `swiftbrowser check <path>`: the import report, then a native compile of the
|
|
2
|
+
// staged sources against the shim. Seconds, no wasm; every error names the
|
|
3
|
+
// user's own file and line.
|
|
4
|
+
import { checkHost } from '../build.js';
|
|
5
|
+
import { CliError, bold, dim, info, ok } from '../log.js';
|
|
6
|
+
import { describeApp, describeReport, prepare, requireToolchain } from './shared.js';
|
|
7
|
+
export async function check(input, options) {
|
|
8
|
+
const { app, report, staged } = await prepare(input, options);
|
|
9
|
+
describeApp(app);
|
|
10
|
+
const blocked = describeReport(report, app);
|
|
11
|
+
const toolchain = requireToolchain({ wasm: false });
|
|
12
|
+
info(dim(`\ncompiling ${app.name} against the shim (Swift ${toolchain.version}, host build)…`));
|
|
13
|
+
const started = Date.now();
|
|
14
|
+
try {
|
|
15
|
+
await checkHost({ toolchain, staged, app, ...(options.verbose ? { onOutput: (chunk) => process.stdout.write(chunk) } : {}) });
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (error instanceof CliError) {
|
|
19
|
+
info(error.message);
|
|
20
|
+
const lines = (error.message.match(/: error: /g) ?? []).length;
|
|
21
|
+
throw new CliError(`\n${bold(`${app.name} does not compile against the shim`)}: ${lines} error${lines === 1 ? '' : 's'}${blocked ? ' (see the unsupported imports above)' : ''}`);
|
|
22
|
+
}
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
info(ok(`${bold(app.name)} compiles against the shim ${dim(`(${((Date.now() - started) / 1000).toFixed(1)} s)`)}`));
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,2EAA2E;AAC3E,4BAA4B;AAE5B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAmB,WAAW,EAAE,cAAc,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEtG,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,KAAa,EAAE,OAA2C;IACpF,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC9D,WAAW,CAAC,GAAG,CAAC,CAAC;IACjB,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,gBAAgB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,IAAI,CAAC,GAAG,CAAC,eAAe,GAAG,CAAC,IAAI,4BAA4B,SAAS,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAAC;IAChG,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAChI,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC9B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACpB,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;YAC/D,MAAM,IAAI,QAAQ,CAAC,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,oCAAoC,CAAC,KAAK,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACpL,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,8BAA8B,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;AACtH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type AppSpec } from '@swiftbrowser/web';
|
|
2
|
+
import { type ICUData } from '../icu.js';
|
|
3
|
+
import { type ResolvedApp } from '../project.js';
|
|
4
|
+
import { type StagedApp } from '../stage.js';
|
|
5
|
+
import type { Toolchain } from '../toolchain.js';
|
|
6
|
+
import { type AppOptions } from './shared.js';
|
|
7
|
+
export interface DevOptions extends AppOptions {
|
|
8
|
+
port?: number;
|
|
9
|
+
host?: string | boolean;
|
|
10
|
+
open?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** For a full-Foundation app, the trimmed ICU data (made once per SDK and language set, then cached). */
|
|
13
|
+
export declare function icuDataFor(app: ResolvedApp, toolchain: Toolchain): ICUData | null;
|
|
14
|
+
/** The registry entry the web dev server runs the app from. */
|
|
15
|
+
export declare function appSpec(app: ResolvedApp, staged: StagedApp, toolchain: Toolchain, options: {
|
|
16
|
+
wasmOpt?: string | null;
|
|
17
|
+
icu?: ICUData | null;
|
|
18
|
+
}): AppSpec;
|
|
19
|
+
export declare function dev(input: string, options: DevOptions): Promise<void>;
|