@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
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// `swiftbrowser dev <path>`: the app in the browser with rebuild on save.
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { createDevServer } from '@swiftbrowser/web';
|
|
5
|
+
import { buildWasm, fileSize, formatBytes } from '../build.js';
|
|
6
|
+
import { describeICUData, prepareICUData } from '../icu.js';
|
|
7
|
+
import { CliError, bold, dim, info, ok } from '../log.js';
|
|
8
|
+
import { commonSourceBase, stage, wasmOutput } from '../stage.js';
|
|
9
|
+
import { swiftPackagePath } from '@swiftbrowser/swift';
|
|
10
|
+
import { describeApp, describeReport, prepare, requireToolchain } from './shared.js';
|
|
11
|
+
/** For a full-Foundation app, the trimmed ICU data (made once per SDK and language set, then cached). */
|
|
12
|
+
export function icuDataFor(app, toolchain) {
|
|
13
|
+
if (app.foundation !== 'full')
|
|
14
|
+
return null;
|
|
15
|
+
const icu = prepareICUData(toolchain, app.locales);
|
|
16
|
+
info(dim(` ${describeICUData(icu)}`));
|
|
17
|
+
return icu;
|
|
18
|
+
}
|
|
19
|
+
/** The registry entry the web dev server runs the app from. */
|
|
20
|
+
export function appSpec(app, staged, toolchain, options) {
|
|
21
|
+
const spec = {
|
|
22
|
+
name: app.name,
|
|
23
|
+
displayName: app.displayName,
|
|
24
|
+
description: app.origin.kind === 'xcodeproj' ? `${path.basename(app.origin.project)} › ${app.origin.target}` : path.basename(app.root),
|
|
25
|
+
wasm: wasmOutput(app),
|
|
26
|
+
catalogs: app.catalogs,
|
|
27
|
+
resources: app.resources,
|
|
28
|
+
locales: app.locales,
|
|
29
|
+
watch: [commonSourceBase(app)],
|
|
30
|
+
build: async () => {
|
|
31
|
+
// Re-stage first: a saved file is copied (and adjusted) before SwiftPM runs.
|
|
32
|
+
const fresh = stage(app, swiftPackagePath(), { icu: options.icu ?? null });
|
|
33
|
+
staged.originals = fresh.originals;
|
|
34
|
+
await buildWasm({ toolchain, staged: fresh, app, ...(options.wasmOpt ? { wasmOpt: options.wasmOpt } : {}) });
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
if (app.icon)
|
|
38
|
+
spec.icon = app.icon;
|
|
39
|
+
return spec;
|
|
40
|
+
}
|
|
41
|
+
export async function dev(input, options) {
|
|
42
|
+
const { app, report, staged } = await prepare(input, options);
|
|
43
|
+
describeApp(app);
|
|
44
|
+
describeReport(report, app);
|
|
45
|
+
const toolchain = requireToolchain({ wasm: true });
|
|
46
|
+
const spec = appSpec(app, staged, toolchain, { icu: icuDataFor(app, toolchain) });
|
|
47
|
+
info(dim(`\nbuilding ${app.name} for WebAssembly (Swift ${toolchain.version}, ${staged.dir})…`));
|
|
48
|
+
const started = Date.now();
|
|
49
|
+
try {
|
|
50
|
+
await spec.build();
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error instanceof CliError) {
|
|
54
|
+
info(error.message);
|
|
55
|
+
info(dim('\nfix the errors above; the dev server rebuilds when a source changes'));
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (existsSync(spec.wasm)) {
|
|
62
|
+
info(ok(`${bold(app.name)}.wasm ${dim(`${formatBytes(fileSize(spec.wasm))}, ${((Date.now() - started) / 1000).toFixed(1)} s`)}`));
|
|
63
|
+
}
|
|
64
|
+
const serverOptions = { apps: [spec], strictPort: false };
|
|
65
|
+
if (options.port !== undefined)
|
|
66
|
+
serverOptions.port = options.port;
|
|
67
|
+
if (options.host !== undefined)
|
|
68
|
+
serverOptions.host = options.host;
|
|
69
|
+
if (options.open !== undefined)
|
|
70
|
+
serverOptions.open = options.open;
|
|
71
|
+
const server = await createDevServer(serverOptions);
|
|
72
|
+
info('');
|
|
73
|
+
server.printUrls();
|
|
74
|
+
info(dim(`\n open /app/?app=${app.name} on this machine, or the landing page's QR code on a phone`));
|
|
75
|
+
await new Promise((resolve) => {
|
|
76
|
+
const stop = () => {
|
|
77
|
+
void server.close().then(resolve);
|
|
78
|
+
};
|
|
79
|
+
process.once('SIGINT', stop);
|
|
80
|
+
process.once('SIGTERM', stop);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=dev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Toolchain } from '../toolchain.js';
|
|
2
|
+
export interface DoctorOptions {
|
|
3
|
+
install?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export interface DoctorResult {
|
|
6
|
+
toolchain: Toolchain | null;
|
|
7
|
+
wasmSdk: boolean;
|
|
8
|
+
wasmOpt: string | null;
|
|
9
|
+
node: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare const SWIFT_INSTALL_HINT: string;
|
|
12
|
+
export declare function doctor(options?: DoctorOptions): Promise<DoctorResult>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// `swiftbrowser doctor [--install]`: is everything a build needs here?
|
|
2
|
+
import { SWIFT_VERSION, WASM_SDK, WASM_SDK_CHECKSUM, WASM_SDK_URL, findToolchain, findWasmOpt, hasWasmSdk, installBinaryen, installWasmSdk } from '../toolchain.js';
|
|
3
|
+
import { bad, bold, dim, info, ok, warn } from '../log.js';
|
|
4
|
+
export const SWIFT_INSTALL_HINT = [
|
|
5
|
+
`install Swift ${SWIFT_VERSION} with swiftly (https://www.swift.org/install/):`,
|
|
6
|
+
` swiftly install ${SWIFT_VERSION} --use`,
|
|
7
|
+
` or point SWIFT_TOOLCHAIN_BIN at a toolchain's usr/bin`,
|
|
8
|
+
].join('\n');
|
|
9
|
+
export async function doctor(options = {}) {
|
|
10
|
+
info(bold('swiftbrowser doctor'));
|
|
11
|
+
const nodeMajor = Number(process.versions.node.split('.')[0]);
|
|
12
|
+
const node = nodeMajor >= 22;
|
|
13
|
+
info(node ? ok(`Node ${process.versions.node}`) : bad(`Node ${process.versions.node}: 22 or newer is needed`));
|
|
14
|
+
const found = findToolchain();
|
|
15
|
+
let toolchain = found.toolchain;
|
|
16
|
+
if (toolchain) {
|
|
17
|
+
info(ok(`Swift ${toolchain.version} ${dim(toolchain.swift)}`));
|
|
18
|
+
}
|
|
19
|
+
else if (found.fallback) {
|
|
20
|
+
info(bad(`Swift ${found.fallback.version} at ${found.fallback.swift}: SwiftBrowser needs ${SWIFT_VERSION}`));
|
|
21
|
+
info(` ${SWIFT_INSTALL_HINT}`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
info(bad(`Swift ${SWIFT_VERSION} not found`));
|
|
25
|
+
info(` ${SWIFT_INSTALL_HINT}`);
|
|
26
|
+
}
|
|
27
|
+
let wasmSdk = false;
|
|
28
|
+
if (toolchain) {
|
|
29
|
+
wasmSdk = hasWasmSdk(toolchain);
|
|
30
|
+
if (wasmSdk) {
|
|
31
|
+
info(ok(`Swift SDK for WebAssembly ${dim(WASM_SDK)}`));
|
|
32
|
+
}
|
|
33
|
+
else if (options.install) {
|
|
34
|
+
info(` installing ${WASM_SDK} (about 400 MB)…`);
|
|
35
|
+
const result = installWasmSdk(toolchain);
|
|
36
|
+
wasmSdk = result.ok && hasWasmSdk(toolchain);
|
|
37
|
+
info(wasmSdk ? ok(`Swift SDK for WebAssembly ${dim(WASM_SDK)}`) : bad(`installing the SDK failed:\n${result.output}`));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
info(bad(`Swift SDK for WebAssembly (${WASM_SDK}) not installed`));
|
|
41
|
+
info(` swift sdk install ${WASM_SDK_URL} --checksum ${WASM_SDK_CHECKSUM}`);
|
|
42
|
+
info(dim(' (or run `swiftbrowser doctor --install`)'));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
let wasmOpt = findWasmOpt();
|
|
46
|
+
if (wasmOpt) {
|
|
47
|
+
info(ok(`wasm-opt ${dim(wasmOpt)}`));
|
|
48
|
+
}
|
|
49
|
+
else if (options.install) {
|
|
50
|
+
info(' downloading binaryen…');
|
|
51
|
+
try {
|
|
52
|
+
wasmOpt = await installBinaryen();
|
|
53
|
+
info(ok(`wasm-opt ${dim(wasmOpt)}`));
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
info(bad(`installing binaryen failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
info(warn('wasm-opt not found: `build` ships unoptimized modules (about 2.5× larger); `swiftbrowser doctor --install` downloads binaryen'));
|
|
61
|
+
}
|
|
62
|
+
if (toolchain && wasmSdk)
|
|
63
|
+
info(`\n${ok(bold('ready'))}: swiftbrowser dev <path-to-your-app>`);
|
|
64
|
+
return { toolchain, wasmSdk, wasmOpt, node };
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type CompatReport } from '../compat.js';
|
|
2
|
+
import type { FoundationMode } from '../config.js';
|
|
3
|
+
import { type ResolvedApp } from '../project.js';
|
|
4
|
+
import { type StagedApp } from '../stage.js';
|
|
5
|
+
import { type Toolchain } from '../toolchain.js';
|
|
6
|
+
export interface AppOptions {
|
|
7
|
+
target?: string;
|
|
8
|
+
config?: string;
|
|
9
|
+
foundation?: FoundationMode;
|
|
10
|
+
}
|
|
11
|
+
export interface Prepared {
|
|
12
|
+
app: ResolvedApp;
|
|
13
|
+
report: CompatReport;
|
|
14
|
+
staged: StagedApp;
|
|
15
|
+
}
|
|
16
|
+
/** The required toolchain, or a CliError saying how to get it. */
|
|
17
|
+
export declare function requireToolchain(options: {
|
|
18
|
+
wasm: boolean;
|
|
19
|
+
}): Toolchain;
|
|
20
|
+
export declare function prepare(input: string, options: AppOptions): Promise<Prepared>;
|
|
21
|
+
export declare function describeApp(app: ResolvedApp): void;
|
|
22
|
+
/** Prints the import report; returns whether anything is known to be unsupported. */
|
|
23
|
+
export declare function describeReport(report: CompatReport, app: ResolvedApp): boolean;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// What `check`, `dev` and `build` share: resolving the app, reporting its
|
|
2
|
+
// imports, staging it and finding the toolchain.
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { swiftPackagePath } from '@swiftbrowser/swift';
|
|
5
|
+
import { compatReport, rewrittenTo } from '../compat.js';
|
|
6
|
+
import { CliError, bad, bold, cyan, dim, info, ok, warn, yellow } from '../log.js';
|
|
7
|
+
import { resolveApp } from '../project.js';
|
|
8
|
+
import { stage } from '../stage.js';
|
|
9
|
+
import { SWIFT_VERSION, findToolchain, hasWasmSdk } from '../toolchain.js';
|
|
10
|
+
import { SWIFT_INSTALL_HINT } from './doctor.js';
|
|
11
|
+
/** The required toolchain, or a CliError saying how to get it. */
|
|
12
|
+
export function requireToolchain(options) {
|
|
13
|
+
const found = findToolchain();
|
|
14
|
+
if (!found.toolchain) {
|
|
15
|
+
const have = found.fallback ? `found Swift ${found.fallback.version} at ${found.fallback.swift}; ` : '';
|
|
16
|
+
throw new CliError(`${have}SwiftBrowser needs Swift ${SWIFT_VERSION}\n ${SWIFT_INSTALL_HINT}`);
|
|
17
|
+
}
|
|
18
|
+
if (options.wasm && !hasWasmSdk(found.toolchain)) {
|
|
19
|
+
throw new CliError('the Swift SDK for WebAssembly is not installed; run `swiftbrowser doctor --install`');
|
|
20
|
+
}
|
|
21
|
+
return found.toolchain;
|
|
22
|
+
}
|
|
23
|
+
export async function prepare(input, options) {
|
|
24
|
+
const resolveOptions = {};
|
|
25
|
+
if (options.target)
|
|
26
|
+
resolveOptions.target = options.target;
|
|
27
|
+
if (options.config)
|
|
28
|
+
resolveOptions.configFile = options.config;
|
|
29
|
+
if (options.foundation)
|
|
30
|
+
resolveOptions.foundation = options.foundation;
|
|
31
|
+
const app = await resolveApp(input, resolveOptions);
|
|
32
|
+
const report = compatReport(app.sources, app.foundation);
|
|
33
|
+
const staged = stage(app, swiftPackagePath());
|
|
34
|
+
return { app, report, staged };
|
|
35
|
+
}
|
|
36
|
+
export function describeApp(app) {
|
|
37
|
+
const origin = app.origin.kind === 'xcodeproj'
|
|
38
|
+
? `${path.relative(process.cwd(), app.origin.project) || '.'} › target ${app.origin.target}`
|
|
39
|
+
: `${path.relative(process.cwd(), app.root) || '.'} (folder)`;
|
|
40
|
+
info(`${bold(app.displayName)} ${dim(`(${app.name})`)} from ${cyan(origin)}`);
|
|
41
|
+
info(dim(` ${app.sources.length} Swift file${app.sources.length === 1 ? '' : 's'}, ${app.catalogs.length} asset catalog${app.catalogs.length === 1 ? '' : 's'}, Foundation: ${app.foundation}, Swift ${app.swiftLanguageMode} mode`));
|
|
42
|
+
}
|
|
43
|
+
/** Prints the import report; returns whether anything is known to be unsupported. */
|
|
44
|
+
export function describeReport(report, app) {
|
|
45
|
+
let blocked = false;
|
|
46
|
+
for (const module of report.modules) {
|
|
47
|
+
const where = module.files.length === 1 ? path.basename(module.files[0]) : `${module.files.length} files`;
|
|
48
|
+
switch (module.status) {
|
|
49
|
+
case 'supported':
|
|
50
|
+
info(ok(`import ${module.module} ${dim(where)}`));
|
|
51
|
+
break;
|
|
52
|
+
case 'rewritten':
|
|
53
|
+
info(ok(`import ${module.module} ${dim(`→ ${rewrittenTo(module.module, app.foundation)} (${where})`)}`));
|
|
54
|
+
break;
|
|
55
|
+
case 'unsupported':
|
|
56
|
+
blocked = true;
|
|
57
|
+
info(bad(`import ${module.module} ${dim(where)}`));
|
|
58
|
+
if (module.hint)
|
|
59
|
+
info(` ${yellow(module.hint)}`);
|
|
60
|
+
break;
|
|
61
|
+
case 'unknown':
|
|
62
|
+
info(warn(`import ${module.module} ${dim(`${where}; not a module the shim provides, the build will say`)}`));
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const previews = [...report.previews.values()].reduce((a, b) => a + b, 0);
|
|
67
|
+
if (previews > 0)
|
|
68
|
+
info(dim(` ${previews} #Preview block${previews === 1 ? '' : 's'} left out`));
|
|
69
|
+
if (!report.hasMain)
|
|
70
|
+
info(warn(`no @main App found among the sources of ${app.name}`));
|
|
71
|
+
return blocked;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/commands/shared.ts"],"names":[],"mappings":"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"}
|
package/dist/compat.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { FoundationMode } from './config.js';
|
|
2
|
+
export type ModuleStatus = 'supported' | 'rewritten' | 'unsupported' | 'unknown';
|
|
3
|
+
export interface ModuleReport {
|
|
4
|
+
module: string;
|
|
5
|
+
status: ModuleStatus;
|
|
6
|
+
/** Files importing it (absolute paths). */
|
|
7
|
+
files: string[];
|
|
8
|
+
/** For unsupported modules: what to do. */
|
|
9
|
+
hint?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface CompatReport {
|
|
12
|
+
modules: ModuleReport[];
|
|
13
|
+
/** `#Preview` blocks the staging step strips, by file. */
|
|
14
|
+
previews: Map<string, number>;
|
|
15
|
+
/** Whether some file declares `@main`. */
|
|
16
|
+
hasMain: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** The modules a Swift source imports (first path component of each import). */
|
|
19
|
+
export declare function importsOf(source: string): string[];
|
|
20
|
+
/** Number of `#Preview` macro uses in a source. */
|
|
21
|
+
export declare function previewCount(source: string): number;
|
|
22
|
+
export declare function classify(module: string, foundation: FoundationMode): ModuleStatus;
|
|
23
|
+
export declare function hintFor(module: string): string | undefined;
|
|
24
|
+
/** What a `rewritten` import becomes in the staging package. */
|
|
25
|
+
export declare function rewrittenTo(module: string, foundation: FoundationMode): string;
|
|
26
|
+
/** Scans `files` (absolute paths) and groups their imports. */
|
|
27
|
+
export declare function compatReport(files: readonly string[], foundation: FoundationMode): CompatReport;
|
package/dist/compat.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// What a project imports, and how the shim treats each module: before a
|
|
2
|
+
// build, so the report names the frameworks that will not compile instead of
|
|
3
|
+
// a wall of unresolved-identifier errors.
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
// The C libraries are usually behind `#if canImport(...)`; whichever branch the wasm build takes is fine.
|
|
6
|
+
const SUPPORTED = new Set(['SwiftUI', 'Swift', 'Observation', 'FoundationEssentials', 'FoundationLite', 'FoundationBridge', 'Charts', 'SwiftBrowser', 'WASILibc', 'Glibc', 'Musl', 'Darwin']);
|
|
7
|
+
/** Frameworks the browser has no counterpart for yet, with the usual way out. */
|
|
8
|
+
const UNSUPPORTED = {
|
|
9
|
+
UIKit: 'UIKit has no browser counterpart; keep UIKit code behind `#if !SWIFTBROWSER`',
|
|
10
|
+
AppKit: 'AppKit has no browser counterpart; keep it behind `#if !SWIFTBROWSER`',
|
|
11
|
+
Combine: 'Combine is not available (`Timer.publish` + `onReceive` work without the import); use async/await, `.task` and `@Observable` for the rest',
|
|
12
|
+
SwiftData: 'SwiftData is not available; keep models in an `@Observable` store behind `#if SWIFTBROWSER`',
|
|
13
|
+
CoreData: 'Core Data is not available; keep persistence behind `#if !SWIFTBROWSER`',
|
|
14
|
+
CoreML: 'Core ML is not available; stub the model behind `#if SWIFTBROWSER`',
|
|
15
|
+
CreateML: 'Create ML is not available',
|
|
16
|
+
Vision: 'Vision is not available',
|
|
17
|
+
MapKit: 'MapKit is not available yet; show a placeholder behind `#if SWIFTBROWSER`',
|
|
18
|
+
CoreLocation: 'Core Location is not available yet',
|
|
19
|
+
AVFoundation: 'AVFoundation is not available; audio and video need a browser-side counterpart first',
|
|
20
|
+
AVKit: 'AVKit is not available',
|
|
21
|
+
UserNotifications: 'UserNotifications is not available; guard it with `#if !SWIFTBROWSER`',
|
|
22
|
+
StoreKit: 'StoreKit is not available',
|
|
23
|
+
CloudKit: 'CloudKit is not available',
|
|
24
|
+
WidgetKit: 'WidgetKit extensions do not run in the browser',
|
|
25
|
+
PhotosUI: 'PhotosUI is not available yet',
|
|
26
|
+
Photos: 'Photos is not available',
|
|
27
|
+
WebKit: 'WebKit is not available (an inline web view would be a renderer feature)',
|
|
28
|
+
CoreImage: 'Core Image is not available',
|
|
29
|
+
CoreGraphics: 'Core Graphics is only partly available (CGFloat, CGPoint, CGSize, CGRect are in the shim); remove the import',
|
|
30
|
+
QuartzCore: 'QuartzCore is not available',
|
|
31
|
+
Metal: 'Metal is not available',
|
|
32
|
+
MetalKit: 'Metal is not available',
|
|
33
|
+
SpriteKit: 'SpriteKit is not available',
|
|
34
|
+
SceneKit: 'SceneKit is not available',
|
|
35
|
+
RealityKit: 'RealityKit is not available',
|
|
36
|
+
ARKit: 'ARKit is not available',
|
|
37
|
+
GameKit: 'GameKit is not available',
|
|
38
|
+
HealthKit: 'HealthKit is not available',
|
|
39
|
+
EventKit: 'EventKit is not available',
|
|
40
|
+
Contacts: 'Contacts is not available',
|
|
41
|
+
MessageUI: 'MessageUI is not available',
|
|
42
|
+
SafariServices: 'SafariServices is not available',
|
|
43
|
+
LocalAuthentication: 'LocalAuthentication is not available',
|
|
44
|
+
Security: 'Security is not available',
|
|
45
|
+
Network: 'Network.framework is not available; use URLSession-free fetching through the host (planned)',
|
|
46
|
+
TipKit: 'TipKit is not available',
|
|
47
|
+
CodeScanner: 'CodeScanner (a camera package) is not available; simulate the scan behind `#if SWIFTBROWSER`',
|
|
48
|
+
};
|
|
49
|
+
const IMPORT = /^\s*(?:@[A-Za-z_]+(?:\([^)]*\))?\s+)*import\s+(?:(?:struct|class|enum|protocol|typealias|func|let|var|actor)\s+)?([A-Za-z_][A-Za-z0-9_]*)/gm;
|
|
50
|
+
/** The modules a Swift source imports (first path component of each import). */
|
|
51
|
+
export function importsOf(source) {
|
|
52
|
+
const out = [];
|
|
53
|
+
for (const match of source.matchAll(IMPORT))
|
|
54
|
+
out.push(match[1]);
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
/** Number of `#Preview` macro uses in a source. */
|
|
58
|
+
export function previewCount(source) {
|
|
59
|
+
return (source.match(/^\s*#Preview\b/gm) ?? []).length;
|
|
60
|
+
}
|
|
61
|
+
export function classify(module, foundation) {
|
|
62
|
+
if (module === 'Foundation')
|
|
63
|
+
return foundation === 'lean' ? 'rewritten' : 'supported';
|
|
64
|
+
if (module === 'FoundationLite')
|
|
65
|
+
return foundation === 'full' ? 'rewritten' : 'supported';
|
|
66
|
+
if (SUPPORTED.has(module))
|
|
67
|
+
return 'supported';
|
|
68
|
+
if (module in UNSUPPORTED)
|
|
69
|
+
return 'unsupported';
|
|
70
|
+
return 'unknown';
|
|
71
|
+
}
|
|
72
|
+
export function hintFor(module) {
|
|
73
|
+
return UNSUPPORTED[module];
|
|
74
|
+
}
|
|
75
|
+
/** What a `rewritten` import becomes in the staging package. */
|
|
76
|
+
export function rewrittenTo(module, foundation) {
|
|
77
|
+
if (module === 'Foundation' && foundation === 'lean')
|
|
78
|
+
return 'FoundationEssentials + FoundationLite';
|
|
79
|
+
if (module === 'FoundationLite' && foundation === 'full')
|
|
80
|
+
return 'FoundationBridge';
|
|
81
|
+
return module;
|
|
82
|
+
}
|
|
83
|
+
/** Scans `files` (absolute paths) and groups their imports. */
|
|
84
|
+
export function compatReport(files, foundation) {
|
|
85
|
+
const byModule = new Map();
|
|
86
|
+
const previews = new Map();
|
|
87
|
+
let hasMain = false;
|
|
88
|
+
for (const file of files) {
|
|
89
|
+
const source = readFileSync(file, 'utf8');
|
|
90
|
+
if (/^\s*@main\b/m.test(source))
|
|
91
|
+
hasMain = true;
|
|
92
|
+
const count = previewCount(source);
|
|
93
|
+
if (count > 0)
|
|
94
|
+
previews.set(file, count);
|
|
95
|
+
for (const module of new Set(importsOf(source))) {
|
|
96
|
+
let report = byModule.get(module);
|
|
97
|
+
if (!report) {
|
|
98
|
+
report = { module, status: classify(module, foundation), files: [] };
|
|
99
|
+
const hint = hintFor(module);
|
|
100
|
+
if (hint)
|
|
101
|
+
report.hint = hint;
|
|
102
|
+
byModule.set(module, report);
|
|
103
|
+
}
|
|
104
|
+
report.files.push(file);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const order = { unsupported: 0, unknown: 1, rewritten: 2, supported: 3 };
|
|
108
|
+
const modules = [...byModule.values()].sort((a, b) => order[a.status] - order[b.status] || a.module.localeCompare(b.module));
|
|
109
|
+
return { modules, previews, hasMain };
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=compat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compat.js","sourceRoot":"","sources":["../src/compat.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export type FoundationMode = 'lean' | 'full';
|
|
2
|
+
export type SwiftLanguageMode = '5' | '6';
|
|
3
|
+
export interface Config {
|
|
4
|
+
/** Module name of the app (letters, digits, `_`); defaults to the target or folder name. */
|
|
5
|
+
name?: string;
|
|
6
|
+
/** Shown on the landing page and in the home-screen manifest. */
|
|
7
|
+
displayName?: string;
|
|
8
|
+
/** The Xcode app target to build when the project has several. */
|
|
9
|
+
target?: string;
|
|
10
|
+
/** Files or directories (relative to the project) whose `.swift` files are the app's sources; replaces discovery. */
|
|
11
|
+
sources?: string[];
|
|
12
|
+
/** Files or directories (relative to the project) left out of the sources. */
|
|
13
|
+
exclude?: string[];
|
|
14
|
+
/** `*.xcassets` directories (relative to the project); replaces discovery. */
|
|
15
|
+
catalogs?: string[];
|
|
16
|
+
/**
|
|
17
|
+
* Files and folders (relative to the project) the app reads through
|
|
18
|
+
* `Bundle.main`; replaces the Xcode target's Copy Bundle Resources. A folder
|
|
19
|
+
* keeps its name (a folder reference), a file is published under its name.
|
|
20
|
+
*/
|
|
21
|
+
resources?: string[];
|
|
22
|
+
/** A PNG for the home-screen icon (relative to the project). */
|
|
23
|
+
icon?: string;
|
|
24
|
+
/** Swift language mode the sources are compiled in (default 5, like Xcode's default for apps). */
|
|
25
|
+
swiftLanguageMode?: SwiftLanguageMode;
|
|
26
|
+
/**
|
|
27
|
+
* `full` (default): the wasm SDK's Foundation, nothing rewritten, its ICU
|
|
28
|
+
* data trimmed to `locales` (see below); about 19 MB optimized, 6.6 MB
|
|
29
|
+
* gzipped. `lean`: `import Foundation` is rewritten to FoundationEssentials
|
|
30
|
+
* + FoundationLite, a smaller module (4.5 to 9 MB) with a smaller API.
|
|
31
|
+
*/
|
|
32
|
+
foundation?: FoundationMode;
|
|
33
|
+
/**
|
|
34
|
+
* In full mode, the languages whose ICU locale data is kept (BCP 47 tags;
|
|
35
|
+
* the region part is ignored, every regional variant of the language stays).
|
|
36
|
+
* Default `["en"]`, about 5.5 MB; `["*"]` keeps the SDK's whole 34 MB.
|
|
37
|
+
*/
|
|
38
|
+
locales?: string[];
|
|
39
|
+
/** Extra `-D` compilation conditions (SWIFTBROWSER is always defined). */
|
|
40
|
+
defines?: string[];
|
|
41
|
+
}
|
|
42
|
+
export declare const CONFIG_FILE = "swiftbrowser.json";
|
|
43
|
+
/** Reads and validates `swiftbrowser.json` in `dir` (or the file at `file`); `{}` when absent. */
|
|
44
|
+
export declare function loadConfig(dir: string, file?: string): Config;
|
|
45
|
+
export declare function validateConfig(raw: unknown, source?: string): Config;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// `swiftbrowser.json`: optional per-project settings. Everything has a
|
|
2
|
+
// default worked out from the Xcode project or the folder, so most projects
|
|
3
|
+
// need no file at all.
|
|
4
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { CliError } from './log.js';
|
|
7
|
+
export const CONFIG_FILE = 'swiftbrowser.json';
|
|
8
|
+
const STRING_KEYS = ['name', 'displayName', 'target', 'icon'];
|
|
9
|
+
const LIST_KEYS = ['sources', 'exclude', 'catalogs', 'resources', 'defines', 'locales'];
|
|
10
|
+
/** Reads and validates `swiftbrowser.json` in `dir` (or the file at `file`); `{}` when absent. */
|
|
11
|
+
export function loadConfig(dir, file) {
|
|
12
|
+
const configPath = file ?? path.join(dir, CONFIG_FILE);
|
|
13
|
+
if (!existsSync(configPath)) {
|
|
14
|
+
if (file)
|
|
15
|
+
throw new CliError(`config file not found: ${configPath}`);
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
let raw;
|
|
19
|
+
try {
|
|
20
|
+
raw = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
throw new CliError(`${configPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
24
|
+
}
|
|
25
|
+
return validateConfig(raw, configPath);
|
|
26
|
+
}
|
|
27
|
+
export function validateConfig(raw, source = CONFIG_FILE) {
|
|
28
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
29
|
+
throw new CliError(`${source}: expected a JSON object`);
|
|
30
|
+
const record = raw;
|
|
31
|
+
const config = {};
|
|
32
|
+
for (const key of STRING_KEYS) {
|
|
33
|
+
const value = record[key];
|
|
34
|
+
if (value === undefined)
|
|
35
|
+
continue;
|
|
36
|
+
if (typeof value !== 'string')
|
|
37
|
+
throw new CliError(`${source}: "${key}" must be a string`);
|
|
38
|
+
config[key] = value;
|
|
39
|
+
}
|
|
40
|
+
for (const key of LIST_KEYS) {
|
|
41
|
+
const value = record[key];
|
|
42
|
+
if (value === undefined)
|
|
43
|
+
continue;
|
|
44
|
+
if (!Array.isArray(value) || !value.every((v) => typeof v === 'string'))
|
|
45
|
+
throw new CliError(`${source}: "${key}" must be an array of strings`);
|
|
46
|
+
config[key] = value;
|
|
47
|
+
}
|
|
48
|
+
if (record['swiftLanguageMode'] !== undefined) {
|
|
49
|
+
const mode = String(record['swiftLanguageMode']);
|
|
50
|
+
if (mode !== '5' && mode !== '6')
|
|
51
|
+
throw new CliError(`${source}: "swiftLanguageMode" must be "5" or "6"`);
|
|
52
|
+
config.swiftLanguageMode = mode;
|
|
53
|
+
}
|
|
54
|
+
if (record['foundation'] !== undefined) {
|
|
55
|
+
const mode = record['foundation'];
|
|
56
|
+
if (mode !== 'lean' && mode !== 'full')
|
|
57
|
+
throw new CliError(`${source}: "foundation" must be "lean" or "full"`);
|
|
58
|
+
config.foundation = mode;
|
|
59
|
+
}
|
|
60
|
+
if (config.name !== undefined && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(config.name)) {
|
|
61
|
+
throw new CliError(`${source}: "name" must be a Swift module name (letters, digits, underscores)`);
|
|
62
|
+
}
|
|
63
|
+
for (const key of Object.keys(record)) {
|
|
64
|
+
if (!STRING_KEYS.includes(key) && !LIST_KEYS.includes(key) && key !== 'swiftLanguageMode' && key !== 'foundation' && key !== '$schema') {
|
|
65
|
+
throw new CliError(`${source}: unknown key "${key}"`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return config;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|