@swifttui/build 0.0.13 → 0.0.15

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 (44) hide show
  1. package/README.md +23 -4
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.js +77 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/index.d.ts +5 -0
  6. package/dist/index.js +5 -0
  7. package/dist/src/build/buildAppWasm.d.ts +21 -0
  8. package/dist/src/build/buildAppWasm.js +61 -0
  9. package/dist/src/build/buildAppWasm.js.map +1 -0
  10. package/dist/src/build/buildSwiftTUIWebApp.d.ts +10 -0
  11. package/dist/src/build/buildSwiftTUIWebApp.js +23 -0
  12. package/dist/src/build/buildSwiftTUIWebApp.js.map +1 -0
  13. package/dist/src/build/generateSceneManifest.d.ts +13 -0
  14. package/dist/src/build/generateSceneManifest.js +28 -0
  15. package/dist/src/build/generateSceneManifest.js.map +1 -0
  16. package/dist/src/build/optimizePackagedWasm.js +17 -0
  17. package/dist/src/build/optimizePackagedWasm.js.map +1 -0
  18. package/dist/src/build/resolveSwiftArtifacts.d.ts +39 -0
  19. package/dist/src/build/resolveSwiftArtifacts.js +161 -0
  20. package/dist/src/build/resolveSwiftArtifacts.js.map +1 -0
  21. package/dist/src/build/runCommand.js +55 -0
  22. package/dist/src/build/runCommand.js.map +1 -0
  23. package/dist/src/build/stripPackagedWasm.js +16 -0
  24. package/dist/src/build/stripPackagedWasm.js.map +1 -0
  25. package/dist/src/build/swiftCommandPrefix.js +14 -0
  26. package/dist/src/build/swiftCommandPrefix.js.map +1 -0
  27. package/dist/src/build/wasmTypeDiagnostics.js +204 -0
  28. package/dist/src/build/wasmTypeDiagnostics.js.map +1 -0
  29. package/package.json +25 -11
  30. package/AGENTS.md +0 -44
  31. package/cli.ts +0 -98
  32. package/index.ts +0 -4
  33. package/src/build/buildAppWasm.test.ts +0 -178
  34. package/src/build/buildAppWasm.ts +0 -107
  35. package/src/build/buildSwiftTUIWebApp.ts +0 -22
  36. package/src/build/generateSceneManifest.ts +0 -46
  37. package/src/build/optimizePackagedWasm.ts +0 -20
  38. package/src/build/resolveSwiftArtifacts.test.ts +0 -90
  39. package/src/build/resolveSwiftArtifacts.ts +0 -253
  40. package/src/build/runCommand.ts +0 -81
  41. package/src/build/stripPackagedWasm.ts +0 -19
  42. package/src/build/swiftCommandPrefix.ts +0 -9
  43. package/src/build/wasmTypeDiagnostics.ts +0 -313
  44. package/tsconfig.json +0 -21
package/README.md CHANGED
@@ -7,9 +7,19 @@ This package owns manifest generation, Swift WASI builds, browser
7
7
  outside `@swifttui/web` so browser runtime imports do not pull in Swift process
8
8
  spawning, Node filesystem APIs, or wasm packaging helpers.
9
9
 
10
- Publication status: the package name is reserved for the first public web
11
- release. Until it is published to npm or attached as a public release tarball,
12
- use the source checkout and the `swift-tui-examples/WebExample` template.
10
+ ## Installation
11
+
12
+ Published to npm as an ESM package with a Node CLI and bundled TypeScript
13
+ declarations:
14
+
15
+ ```bash
16
+ npm install --save-dev @swifttui/build
17
+ ```
18
+
19
+ This exposes the `swifttui-web` CLI (`npx swifttui-web build --app <Exe>`) and a
20
+ programmatic ESM API. The package ships compiled `dist/` JavaScript — the bin
21
+ runs on plain Node (`#!/usr/bin/env node`), no Bun or TypeScript toolchain
22
+ required to consume it.
13
23
 
14
24
  ## API
15
25
 
@@ -41,6 +51,15 @@ Callers can override `swiftCommand`, `swiftSDK`, `configuration`,
41
51
  ## Scripts
42
52
 
43
53
  - `bun test`
54
+ - `bun run build` — compile the publishable package to `dist/` with tsdown
55
+ (ESM `.js` + `.d.ts`, plus the `swifttui-web` bin). Run automatically on
56
+ publish via `prepublishOnly`.
44
57
  - `bun run build:manifest -- --app <AppExecutable>`
45
58
  - `bun run build:wasm -- --app <AppExecutable>`
46
- - `bun run build -- --app <AppExecutable>`
59
+
60
+ The full app pipeline (manifest + wasm) is exposed through the CLI:
61
+
62
+ ```bash
63
+ bun run cli.ts build --app <AppExecutable> # from source
64
+ npx swifttui-web build --app <AppExecutable> # from the published bin
65
+ ```
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/dist/cli.js ADDED
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+ import { buildAppWasm } from "./src/build/buildAppWasm.js";
3
+ import { generateSceneManifest } from "./src/build/generateSceneManifest.js";
4
+ import { buildSwiftTUIWebApp } from "./src/build/buildSwiftTUIWebApp.js";
5
+ import { resolve } from "node:path";
6
+ //#region cli.ts
7
+ runCli(process.argv.slice(2));
8
+ async function runCli(argv) {
9
+ const command = argv[0] ?? "build";
10
+ const flags = parseFlags(argv.slice(1));
11
+ const packagePath = resolve(flags["package-path"] ?? "../../");
12
+ const distPath = resolve(flags["dist"] ?? "./dist");
13
+ const appExecutable = flags.app ?? flags.product ?? flags["app-product"] ?? "";
14
+ const configuration = parseWasmBuildConfiguration(flags.configuration ?? "release");
15
+ switch (command) {
16
+ case "build:manifest":
17
+ assertAppExecutable(appExecutable);
18
+ await generateSceneManifest({
19
+ packagePath,
20
+ outputPath: resolve(distPath, "scene-manifest.json"),
21
+ appExecutable
22
+ });
23
+ return;
24
+ case "build:wasm":
25
+ assertAppExecutable(appExecutable);
26
+ await buildAppWasm({
27
+ configuration,
28
+ packagePath,
29
+ outputDirectory: distPath,
30
+ product: appExecutable
31
+ });
32
+ return;
33
+ case "build":
34
+ assertAppExecutable(appExecutable);
35
+ await buildSwiftTUIWebApp({
36
+ configuration,
37
+ packagePath,
38
+ outputDirectory: distPath,
39
+ product: appExecutable
40
+ });
41
+ return;
42
+ default: throw new Error(`unknown command: ${command}`);
43
+ }
44
+ }
45
+ function parseFlags(argv) {
46
+ const flags = {};
47
+ for (let index = 0; index < argv.length; index += 1) {
48
+ const value = argv[index];
49
+ if (!value.startsWith("--")) continue;
50
+ const equalsIndex = value.indexOf("=");
51
+ if (equalsIndex !== -1) {
52
+ flags[value.slice(2, equalsIndex)] = value.slice(equalsIndex + 1);
53
+ continue;
54
+ }
55
+ const name = value.slice(2);
56
+ const next = argv[index + 1];
57
+ if (next && !next.startsWith("--")) {
58
+ flags[name] = next;
59
+ index += 1;
60
+ } else flags[name] = "true";
61
+ }
62
+ return flags;
63
+ }
64
+ function parseWasmBuildConfiguration(value) {
65
+ switch (value) {
66
+ case "debug": return "debug";
67
+ case "release": return "release";
68
+ default: throw new Error(`unsupported wasm build configuration: ${value}`);
69
+ }
70
+ }
71
+ function assertAppExecutable(value) {
72
+ if (!value) throw new Error("missing --app or --product flag");
73
+ }
74
+ //#endregion
75
+ export {};
76
+
77
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { resolve } from \"node:path\";\nimport {\n buildAppWasm,\n buildSwiftTUIWebApp,\n generateSceneManifest,\n type WasmBuildConfiguration,\n} from \"./index.ts\";\n\nvoid runCli(process.argv.slice(2));\n\nasync function runCli(argv: string[]): Promise<void> {\n const command = argv[0] ?? \"build\";\n const flags = parseFlags(argv.slice(1));\n const packagePath = resolve(flags[\"package-path\"] ?? \"../../\");\n const distPath = resolve(flags[\"dist\"] ?? \"./dist\");\n const appExecutable =\n flags.app ?? flags.product ?? flags[\"app-product\"] ?? \"\";\n const configuration = parseWasmBuildConfiguration(\n flags.configuration ?? \"release\",\n );\n\n switch (command) {\n case \"build:manifest\":\n assertAppExecutable(appExecutable);\n await generateSceneManifest({\n packagePath,\n outputPath: resolve(distPath, \"scene-manifest.json\"),\n appExecutable,\n });\n return;\n case \"build:wasm\":\n assertAppExecutable(appExecutable);\n await buildAppWasm({\n configuration,\n packagePath,\n outputDirectory: distPath,\n product: appExecutable,\n });\n return;\n case \"build\":\n assertAppExecutable(appExecutable);\n await buildSwiftTUIWebApp({\n configuration,\n packagePath,\n outputDirectory: distPath,\n product: appExecutable,\n });\n return;\n default:\n throw new Error(`unknown command: ${command}`);\n }\n}\n\nfunction parseFlags(argv: string[]): Record<string, string> {\n const flags: Record<string, string> = {};\n for (let index = 0; index < argv.length; index += 1) {\n const value = argv[index];\n if (!value.startsWith(\"--\")) {\n continue;\n }\n const equalsIndex = value.indexOf(\"=\");\n if (equalsIndex !== -1) {\n flags[value.slice(2, equalsIndex)] = value.slice(equalsIndex + 1);\n continue;\n }\n const name = value.slice(2);\n const next = argv[index + 1];\n if (next && !next.startsWith(\"--\")) {\n flags[name] = next;\n index += 1;\n } else {\n flags[name] = \"true\";\n }\n }\n return flags;\n}\n\nfunction parseWasmBuildConfiguration(value: string): WasmBuildConfiguration {\n switch (value) {\n case \"debug\":\n return \"debug\";\n case \"release\":\n return \"release\";\n default:\n throw new Error(`unsupported wasm build configuration: ${value}`);\n }\n}\n\nfunction assertAppExecutable(value: string): asserts value is string {\n if (!value) {\n throw new Error(\"missing --app or --product flag\");\n }\n}\n"],"mappings":";;;;;;AAUK,OAAO,QAAQ,KAAK,MAAM,CAAC,CAAC;AAEjC,eAAe,OAAO,MAA+B;CACnD,MAAM,UAAU,KAAK,MAAM;CAC3B,MAAM,QAAQ,WAAW,KAAK,MAAM,CAAC,CAAC;CACtC,MAAM,cAAc,QAAQ,MAAM,mBAAmB,QAAQ;CAC7D,MAAM,WAAW,QAAQ,MAAM,WAAW,QAAQ;CAClD,MAAM,gBACJ,MAAM,OAAO,MAAM,WAAW,MAAM,kBAAkB;CACxD,MAAM,gBAAgB,4BACpB,MAAM,iBAAiB,SACzB;CAEA,QAAQ,SAAR;EACE,KAAK;GACH,oBAAoB,aAAa;GACjC,MAAM,sBAAsB;IAC1B;IACA,YAAY,QAAQ,UAAU,qBAAqB;IACnD;GACF,CAAC;GACD;EACF,KAAK;GACH,oBAAoB,aAAa;GACjC,MAAM,aAAa;IACjB;IACA;IACA,iBAAiB;IACjB,SAAS;GACX,CAAC;GACD;EACF,KAAK;GACH,oBAAoB,aAAa;GACjC,MAAM,oBAAoB;IACxB;IACA;IACA,iBAAiB;IACjB,SAAS;GACX,CAAC;GACD;EACF,SACE,MAAM,IAAI,MAAM,oBAAoB,SAAS;CACjD;AACF;AAEA,SAAS,WAAW,MAAwC;CAC1D,MAAM,QAAgC,CAAC;CACvC,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;EACnD,MAAM,QAAQ,KAAK;EACnB,IAAI,CAAC,MAAM,WAAW,IAAI,GACxB;EAEF,MAAM,cAAc,MAAM,QAAQ,GAAG;EACrC,IAAI,gBAAgB,IAAI;GACtB,MAAM,MAAM,MAAM,GAAG,WAAW,KAAK,MAAM,MAAM,cAAc,CAAC;GAChE;EACF;EACA,MAAM,OAAO,MAAM,MAAM,CAAC;EAC1B,MAAM,OAAO,KAAK,QAAQ;EAC1B,IAAI,QAAQ,CAAC,KAAK,WAAW,IAAI,GAAG;GAClC,MAAM,QAAQ;GACd,SAAS;EACX,OACE,MAAM,QAAQ;CAElB;CACA,OAAO;AACT;AAEA,SAAS,4BAA4B,OAAuC;CAC1E,QAAQ,OAAR;EACE,KAAK,SACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,SACE,MAAM,IAAI,MAAM,yCAAyC,OAAO;CACpE;AACF;AAEA,SAAS,oBAAoB,OAAwC;CACnE,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,iCAAiC;AAErD"}
@@ -0,0 +1,5 @@
1
+ import { ResolveSwiftArtifactsOptions, SwiftArtifactPaths, WasmBuildConfiguration, defaultInitialMemory, defaultMaxMemory, defaultStackSize, defaultWasmSwiftSDK, formatCommandForLogs, hasRequiredWasmFlags, requiredWasmSwiftFlags, resolveSwiftArtifacts, wasmBuildConfigurationLogLines } from "./src/build/resolveSwiftArtifacts.js";
2
+ import { BuildAppWasmOptions, buildAppWasm, packageBrowserValidatedWasm } from "./src/build/buildAppWasm.js";
3
+ import { BuildSwiftTUIWebAppOptions, buildSwiftTUIWebApp } from "./src/build/buildSwiftTUIWebApp.js";
4
+ import { GenerateSceneManifestOptions, generateSceneManifest } from "./src/build/generateSceneManifest.js";
5
+ export { BuildAppWasmOptions, BuildSwiftTUIWebAppOptions, GenerateSceneManifestOptions, ResolveSwiftArtifactsOptions, SwiftArtifactPaths, WasmBuildConfiguration, buildAppWasm, buildSwiftTUIWebApp, defaultInitialMemory, defaultMaxMemory, defaultStackSize, defaultWasmSwiftSDK, formatCommandForLogs, generateSceneManifest, hasRequiredWasmFlags, packageBrowserValidatedWasm, requiredWasmSwiftFlags, resolveSwiftArtifacts, wasmBuildConfigurationLogLines };
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import { defaultInitialMemory, defaultMaxMemory, defaultStackSize, defaultWasmSwiftSDK, formatCommandForLogs, hasRequiredWasmFlags, requiredWasmSwiftFlags, resolveSwiftArtifacts, wasmBuildConfigurationLogLines } from "./src/build/resolveSwiftArtifacts.js";
2
+ import { buildAppWasm, packageBrowserValidatedWasm } from "./src/build/buildAppWasm.js";
3
+ import { generateSceneManifest } from "./src/build/generateSceneManifest.js";
4
+ import { buildSwiftTUIWebApp } from "./src/build/buildSwiftTUIWebApp.js";
5
+ export { buildAppWasm, buildSwiftTUIWebApp, defaultInitialMemory, defaultMaxMemory, defaultStackSize, defaultWasmSwiftSDK, formatCommandForLogs, generateSceneManifest, hasRequiredWasmFlags, packageBrowserValidatedWasm, requiredWasmSwiftFlags, resolveSwiftArtifacts, wasmBuildConfigurationLogLines };
@@ -0,0 +1,21 @@
1
+ import { ResolveSwiftArtifactsOptions, SwiftArtifactPaths, WasmBuildConfiguration } from "./resolveSwiftArtifacts.js";
2
+
3
+ //#region src/build/buildAppWasm.d.ts
4
+ interface BuildAppWasmOptions extends ResolveSwiftArtifactsOptions {
5
+ configuration?: WasmBuildConfiguration;
6
+ packagePath: string;
7
+ outputDirectory: string;
8
+ product: string;
9
+ }
10
+ declare function buildAppWasm(options: BuildAppWasmOptions): Promise<SwiftArtifactPaths>;
11
+ interface PackageBrowserValidatedWasmOptions {
12
+ optimize?: (wasmPath: string) => Promise<void>;
13
+ sourceWasmPath: string;
14
+ outputWasmPath: string;
15
+ strip?: (wasmPath: string) => Promise<void>;
16
+ onWarning?: (message: string) => void;
17
+ }
18
+ declare function packageBrowserValidatedWasm(options: PackageBrowserValidatedWasmOptions): Promise<void>;
19
+ //#endregion
20
+ export { BuildAppWasmOptions, buildAppWasm, packageBrowserValidatedWasm };
21
+ //# sourceMappingURL=buildAppWasm.d.ts.map
@@ -0,0 +1,61 @@
1
+ import { optimizePackagedWasm } from "./optimizePackagedWasm.js";
2
+ import { resolveSwiftArtifacts } from "./resolveSwiftArtifacts.js";
3
+ import { stripPackagedWasm } from "./stripPackagedWasm.js";
4
+ import { formatWasmTypeDiagnostics } from "./wasmTypeDiagnostics.js";
5
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
6
+ import { join } from "node:path";
7
+ //#region src/build/buildAppWasm.ts
8
+ async function buildAppWasm(options) {
9
+ const artifacts = await resolveSwiftArtifacts(options);
10
+ const packagedWasmPath = join(options.outputDirectory, "assets", "app.wasm");
11
+ await mkdir(join(options.outputDirectory, "assets"), { recursive: true });
12
+ await rm(packagedWasmPath, { force: true });
13
+ await packageBrowserValidatedWasm({
14
+ sourceWasmPath: artifacts.wasmPath,
15
+ outputWasmPath: packagedWasmPath
16
+ });
17
+ return artifacts;
18
+ }
19
+ async function packageBrowserValidatedWasm(options) {
20
+ const sourceBytes = await readFile(options.sourceWasmPath);
21
+ await writeFile(options.outputWasmPath, sourceBytes);
22
+ const optimize = options.optimize ?? optimizePackagedWasm;
23
+ try {
24
+ await optimize(options.outputWasmPath);
25
+ await validateBrowserWasm(options.outputWasmPath, "optimized wasm");
26
+ } catch (error) {
27
+ await writeFile(options.outputWasmPath, sourceBytes);
28
+ const message = error instanceof Error ? error.message : String(error);
29
+ try {
30
+ await validateBrowserWasm(options.outputWasmPath, "generated wasm");
31
+ } catch (rawError) {
32
+ const rawMessage = rawError instanceof Error ? rawError.message : String(rawError);
33
+ throw new Error([rawMessage, `wasm optimization step failed: ${message}`].join("\n"));
34
+ }
35
+ const warning = [`warning: keeping unoptimized wasm at ${options.outputWasmPath}`, `wasm optimization step failed or did not produce browser-parseable output: ${message}`].join("\n");
36
+ (options.onWarning ?? console.warn)(warning);
37
+ }
38
+ const strip = options.strip ?? stripPackagedWasm;
39
+ try {
40
+ await strip(options.outputWasmPath);
41
+ await validateBrowserWasm(options.outputWasmPath, "stripped wasm");
42
+ } catch (error) {
43
+ await writeFile(options.outputWasmPath, sourceBytes);
44
+ const message = error instanceof Error ? error.message : String(error);
45
+ const warning = [`warning: keeping unstripped wasm at ${options.outputWasmPath}`, `strip step failed browser validation or tooling requirements: ${message}`].join("\n");
46
+ (options.onWarning ?? console.warn)(warning);
47
+ }
48
+ }
49
+ async function validateBrowserWasm(wasmPath, description) {
50
+ const bytes = await readFile(wasmPath);
51
+ try {
52
+ await WebAssembly.compile(bytes);
53
+ } catch (error) {
54
+ const message = error instanceof Error ? error.message : String(error);
55
+ throw new Error([`${description} does not parse in browser WebAssembly (${wasmPath}): ${message}`, formatWasmTypeDiagnostics(bytes)].join("\n"));
56
+ }
57
+ }
58
+ //#endregion
59
+ export { buildAppWasm, packageBrowserValidatedWasm };
60
+
61
+ //# sourceMappingURL=buildAppWasm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildAppWasm.js","names":[],"sources":["../../../src/build/buildAppWasm.ts"],"sourcesContent":["import { mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { optimizePackagedWasm } from \"./optimizePackagedWasm.ts\";\nimport {\n resolveSwiftArtifacts,\n type ResolveSwiftArtifactsOptions,\n type SwiftArtifactPaths,\n type WasmBuildConfiguration,\n} from \"./resolveSwiftArtifacts.ts\";\nimport { stripPackagedWasm } from \"./stripPackagedWasm.ts\";\nimport { formatWasmTypeDiagnostics } from \"./wasmTypeDiagnostics.ts\";\n\nexport interface BuildAppWasmOptions extends ResolveSwiftArtifactsOptions {\n configuration?: WasmBuildConfiguration;\n packagePath: string;\n outputDirectory: string;\n product: string;\n}\n\nexport async function buildAppWasm(\n options: BuildAppWasmOptions\n): Promise<SwiftArtifactPaths> {\n const artifacts = await resolveSwiftArtifacts(options);\n\n const packagedWasmPath = join(options.outputDirectory, \"assets\", \"app.wasm\");\n await mkdir(join(options.outputDirectory, \"assets\"), { recursive: true });\n await rm(packagedWasmPath, { force: true });\n await packageBrowserValidatedWasm({\n sourceWasmPath: artifacts.wasmPath,\n outputWasmPath: packagedWasmPath,\n });\n return artifacts;\n}\n\ninterface PackageBrowserValidatedWasmOptions {\n optimize?: (wasmPath: string) => Promise<void>;\n sourceWasmPath: string;\n outputWasmPath: string;\n strip?: (wasmPath: string) => Promise<void>;\n onWarning?: (message: string) => void;\n}\n\nexport async function packageBrowserValidatedWasm(\n options: PackageBrowserValidatedWasmOptions\n): Promise<void> {\n const sourceBytes = await readFile(options.sourceWasmPath);\n await writeFile(options.outputWasmPath, sourceBytes);\n\n const optimize = options.optimize ?? optimizePackagedWasm;\n try {\n await optimize(options.outputWasmPath);\n await validateBrowserWasm(options.outputWasmPath, \"optimized wasm\");\n } catch (error) {\n await writeFile(options.outputWasmPath, sourceBytes);\n const message = error instanceof Error ? error.message : String(error);\n\n try {\n await validateBrowserWasm(options.outputWasmPath, \"generated wasm\");\n } catch (rawError) {\n const rawMessage = rawError instanceof Error ? rawError.message : String(rawError);\n throw new Error([\n rawMessage,\n `wasm optimization step failed: ${message}`,\n ].join(\"\\n\"));\n }\n\n const warning = [\n `warning: keeping unoptimized wasm at ${options.outputWasmPath}`,\n `wasm optimization step failed or did not produce browser-parseable output: ${message}`,\n ].join(\"\\n\");\n (options.onWarning ?? console.warn)(warning);\n }\n\n const strip = options.strip ?? stripPackagedWasm;\n\n try {\n await strip(options.outputWasmPath);\n await validateBrowserWasm(options.outputWasmPath, \"stripped wasm\");\n } catch (error) {\n // Stripping is a size optimization only. Keep the known-good raw wasm\n // whenever toolchain-specific objcopy output fails browser validation.\n await writeFile(options.outputWasmPath, sourceBytes);\n const message = error instanceof Error ? error.message : String(error);\n const warning = [\n `warning: keeping unstripped wasm at ${options.outputWasmPath}`,\n `strip step failed browser validation or tooling requirements: ${message}`,\n ].join(\"\\n\");\n (options.onWarning ?? console.warn)(warning);\n }\n}\n\nasync function validateBrowserWasm(\n wasmPath: string,\n description: string\n): Promise<void> {\n const bytes = await readFile(wasmPath);\n try {\n // Validate against the same JS API the browser uses before we publish it.\n await WebAssembly.compile(bytes);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error([\n `${description} does not parse in browser WebAssembly (${wasmPath}): ${message}`,\n formatWasmTypeDiagnostics(bytes),\n ].join(\"\\n\"));\n }\n}\n"],"mappings":";;;;;;;AAmBA,eAAsB,aACpB,SAC6B;CAC7B,MAAM,YAAY,MAAM,sBAAsB,OAAO;CAErD,MAAM,mBAAmB,KAAK,QAAQ,iBAAiB,UAAU,UAAU;CAC3E,MAAM,MAAM,KAAK,QAAQ,iBAAiB,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;CACxE,MAAM,GAAG,kBAAkB,EAAE,OAAO,KAAK,CAAC;CAC1C,MAAM,4BAA4B;EAChC,gBAAgB,UAAU;EAC1B,gBAAgB;CAClB,CAAC;CACD,OAAO;AACT;AAUA,eAAsB,4BACpB,SACe;CACf,MAAM,cAAc,MAAM,SAAS,QAAQ,cAAc;CACzD,MAAM,UAAU,QAAQ,gBAAgB,WAAW;CAEnD,MAAM,WAAW,QAAQ,YAAY;CACrC,IAAI;EACF,MAAM,SAAS,QAAQ,cAAc;EACrC,MAAM,oBAAoB,QAAQ,gBAAgB,gBAAgB;CACpE,SAAS,OAAO;EACd,MAAM,UAAU,QAAQ,gBAAgB,WAAW;EACnD,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAErE,IAAI;GACF,MAAM,oBAAoB,QAAQ,gBAAgB,gBAAgB;EACpE,SAAS,UAAU;GACjB,MAAM,aAAa,oBAAoB,QAAQ,SAAS,UAAU,OAAO,QAAQ;GACjF,MAAM,IAAI,MAAM,CACd,YACA,kCAAkC,SACpC,CAAC,CAAC,KAAK,IAAI,CAAC;EACd;EAEA,MAAM,UAAU,CACd,wCAAwC,QAAQ,kBAChD,8EAA8E,SAChF,CAAC,CAAC,KAAK,IAAI;EACX,CAAC,QAAQ,aAAa,QAAQ,KAAA,CAAM,OAAO;CAC7C;CAEA,MAAM,QAAQ,QAAQ,SAAS;CAE/B,IAAI;EACF,MAAM,MAAM,QAAQ,cAAc;EAClC,MAAM,oBAAoB,QAAQ,gBAAgB,eAAe;CACnE,SAAS,OAAO;EAGd,MAAM,UAAU,QAAQ,gBAAgB,WAAW;EACnD,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACrE,MAAM,UAAU,CACd,uCAAuC,QAAQ,kBAC/C,iEAAiE,SACnE,CAAC,CAAC,KAAK,IAAI;EACX,CAAC,QAAQ,aAAa,QAAQ,KAAA,CAAM,OAAO;CAC7C;AACF;AAEA,eAAe,oBACb,UACA,aACe;CACf,MAAM,QAAQ,MAAM,SAAS,QAAQ;CACrC,IAAI;EAEF,MAAM,YAAY,QAAQ,KAAK;CACjC,SAAS,OAAO;EACd,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACrE,MAAM,IAAI,MAAM,CACd,GAAG,YAAY,0CAA0C,SAAS,KAAK,WACvE,0BAA0B,KAAK,CACjC,CAAC,CAAC,KAAK,IAAI,CAAC;CACd;AACF"}
@@ -0,0 +1,10 @@
1
+ import { BuildAppWasmOptions } from "./buildAppWasm.js";
2
+
3
+ //#region src/build/buildSwiftTUIWebApp.d.ts
4
+ interface BuildSwiftTUIWebAppOptions extends BuildAppWasmOptions {
5
+ appExecutable?: string;
6
+ }
7
+ declare function buildSwiftTUIWebApp(options: BuildSwiftTUIWebAppOptions): Promise<void>;
8
+ //#endregion
9
+ export { BuildSwiftTUIWebAppOptions, buildSwiftTUIWebApp };
10
+ //# sourceMappingURL=buildSwiftTUIWebApp.d.ts.map
@@ -0,0 +1,23 @@
1
+ import { buildAppWasm } from "./buildAppWasm.js";
2
+ import { generateSceneManifest } from "./generateSceneManifest.js";
3
+ import { mkdir, rm } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ //#region src/build/buildSwiftTUIWebApp.ts
6
+ async function buildSwiftTUIWebApp(options) {
7
+ await rm(options.outputDirectory, {
8
+ recursive: true,
9
+ force: true
10
+ });
11
+ await mkdir(options.outputDirectory, { recursive: true });
12
+ await generateSceneManifest({
13
+ packagePath: options.packagePath,
14
+ outputPath: join(options.outputDirectory, "scene-manifest.json"),
15
+ appExecutable: options.appExecutable ?? options.product,
16
+ swiftCommand: options.swiftCommand
17
+ });
18
+ await buildAppWasm(options);
19
+ }
20
+ //#endregion
21
+ export { buildSwiftTUIWebApp };
22
+
23
+ //# sourceMappingURL=buildSwiftTUIWebApp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildSwiftTUIWebApp.js","names":[],"sources":["../../../src/build/buildSwiftTUIWebApp.ts"],"sourcesContent":["import { mkdir, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { buildAppWasm, type BuildAppWasmOptions } from \"./buildAppWasm.ts\";\nimport { generateSceneManifest } from \"./generateSceneManifest.ts\";\n\nexport interface BuildSwiftTUIWebAppOptions extends BuildAppWasmOptions {\n appExecutable?: string;\n}\n\nexport async function buildSwiftTUIWebApp(\n options: BuildSwiftTUIWebAppOptions\n): Promise<void> {\n await rm(options.outputDirectory, { recursive: true, force: true });\n await mkdir(options.outputDirectory, { recursive: true });\n await generateSceneManifest({\n packagePath: options.packagePath,\n outputPath: join(options.outputDirectory, \"scene-manifest.json\"),\n appExecutable: options.appExecutable ?? options.product,\n swiftCommand: options.swiftCommand,\n });\n await buildAppWasm(options);\n}\n"],"mappings":";;;;;AASA,eAAsB,oBACpB,SACe;CACf,MAAM,GAAG,QAAQ,iBAAiB;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC;CAClE,MAAM,MAAM,QAAQ,iBAAiB,EAAE,WAAW,KAAK,CAAC;CACxD,MAAM,sBAAsB;EAC1B,aAAa,QAAQ;EACrB,YAAY,KAAK,QAAQ,iBAAiB,qBAAqB;EAC/D,eAAe,QAAQ,iBAAiB,QAAQ;EAChD,cAAc,QAAQ;CACxB,CAAC;CACD,MAAM,aAAa,OAAO;AAC5B"}
@@ -0,0 +1,13 @@
1
+ import { WebHostSceneManifest } from "@swifttui/web/manifest";
2
+
3
+ //#region src/build/generateSceneManifest.d.ts
4
+ interface GenerateSceneManifestOptions {
5
+ packagePath: string;
6
+ outputPath: string;
7
+ appExecutable: string;
8
+ swiftCommand?: readonly string[];
9
+ }
10
+ declare function generateSceneManifest(options: GenerateSceneManifestOptions): Promise<WebHostSceneManifest>;
11
+ //#endregion
12
+ export { GenerateSceneManifestOptions, generateSceneManifest };
13
+ //# sourceMappingURL=generateSceneManifest.d.ts.map
@@ -0,0 +1,28 @@
1
+ import { runCommand } from "./runCommand.js";
2
+ import { swiftCommandPrefix } from "./swiftCommandPrefix.js";
3
+ import { mkdir, writeFile } from "node:fs/promises";
4
+ import { dirname } from "node:path";
5
+ import { loadWebHostSceneManifest, webTUISceneManifestToJSON } from "@swifttui/web/manifest";
6
+ //#region src/build/generateSceneManifest.ts
7
+ async function generateSceneManifest(options) {
8
+ const manifest = await loadWebHostSceneManifest((await runManifestCommand(options)).trim());
9
+ await mkdir(dirname(options.outputPath), { recursive: true });
10
+ await writeFile(options.outputPath, webTUISceneManifestToJSON(manifest));
11
+ return manifest;
12
+ }
13
+ async function runManifestCommand(options) {
14
+ return await runCommand([
15
+ ...options.swiftCommand ?? swiftCommandPrefix(),
16
+ "run",
17
+ "--package-path",
18
+ options.packagePath,
19
+ options.appExecutable
20
+ ], { env: {
21
+ ...process.env,
22
+ TUIGUI_MODE: "manifest"
23
+ } });
24
+ }
25
+ //#endregion
26
+ export { generateSceneManifest };
27
+
28
+ //# sourceMappingURL=generateSceneManifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateSceneManifest.js","names":[],"sources":["../../../src/build/generateSceneManifest.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport {\n loadWebHostSceneManifest,\n webTUISceneManifestToJSON,\n type WebHostSceneManifest,\n} from \"@swifttui/web/manifest\";\nimport { runCommand } from \"./runCommand.ts\";\nimport { swiftCommandPrefix } from \"./swiftCommandPrefix.ts\";\n\nexport interface GenerateSceneManifestOptions {\n packagePath: string;\n outputPath: string;\n appExecutable: string;\n swiftCommand?: readonly string[];\n}\n\nexport async function generateSceneManifest(\n options: GenerateSceneManifestOptions\n): Promise<WebHostSceneManifest> {\n const output = await runManifestCommand(options);\n const manifest = await loadWebHostSceneManifest(output.trim());\n await mkdir(dirname(options.outputPath), { recursive: true });\n await writeFile(options.outputPath, webTUISceneManifestToJSON(manifest));\n return manifest;\n}\n\nasync function runManifestCommand(\n options: GenerateSceneManifestOptions\n): Promise<string> {\n return await runCommand(\n [\n ...(options.swiftCommand ?? swiftCommandPrefix()),\n \"run\",\n \"--package-path\",\n options.packagePath,\n options.appExecutable,\n ],\n {\n env: {\n ...process.env,\n TUIGUI_MODE: \"manifest\",\n },\n }\n );\n}\n"],"mappings":";;;;;;AAiBA,eAAsB,sBACpB,SAC+B;CAE/B,MAAM,WAAW,MAAM,0BAAyB,MAD3B,mBAAmB,OAAO,EAAA,CACQ,KAAK,CAAC;CAC7D,MAAM,MAAM,QAAQ,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;CAC5D,MAAM,UAAU,QAAQ,YAAY,0BAA0B,QAAQ,CAAC;CACvE,OAAO;AACT;AAEA,eAAe,mBACb,SACiB;CACjB,OAAO,MAAM,WACX;EACE,GAAI,QAAQ,gBAAgB,mBAAmB;EAC/C;EACA;EACA,QAAQ;EACR,QAAQ;CACV,GACA,EACE,KAAK;EACH,GAAG,QAAQ;EACX,aAAa;CACf,EACF,CACF;AACF"}
@@ -0,0 +1,17 @@
1
+ import { findExecutable, runCommand } from "./runCommand.js";
2
+ //#region src/build/optimizePackagedWasm.ts
3
+ async function optimizePackagedWasm(wasmPath) {
4
+ const wasmOptPath = findExecutable("wasm-opt");
5
+ if (!wasmOptPath) throw new Error("missing wasm-opt in PATH; install Binaryen so wasm packaging is deterministic across environments");
6
+ await runCommand([
7
+ wasmOptPath,
8
+ "-Os",
9
+ wasmPath,
10
+ "-o",
11
+ wasmPath
12
+ ]);
13
+ }
14
+ //#endregion
15
+ export { optimizePackagedWasm };
16
+
17
+ //# sourceMappingURL=optimizePackagedWasm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optimizePackagedWasm.js","names":[],"sources":["../../../src/build/optimizePackagedWasm.ts"],"sourcesContent":["import { findExecutable, runCommand } from \"./runCommand.ts\";\n\nexport async function optimizePackagedWasm(\n wasmPath: string\n): Promise<void> {\n const wasmOptPath = findExecutable(\"wasm-opt\");\n if (!wasmOptPath) {\n throw new Error(\n \"missing wasm-opt in PATH; install Binaryen so wasm packaging is deterministic across environments\"\n );\n }\n\n await runCommand([\n wasmOptPath,\n \"-Os\",\n wasmPath,\n \"-o\",\n wasmPath,\n ]);\n}\n"],"mappings":";;AAEA,eAAsB,qBACpB,UACe;CACf,MAAM,cAAc,eAAe,UAAU;CAC7C,IAAI,CAAC,aACH,MAAM,IAAI,MACR,mGACF;CAGF,MAAM,WAAW;EACf;EACA;EACA;EACA;EACA;CACF,CAAC;AACH"}
@@ -0,0 +1,39 @@
1
+ //#region src/build/resolveSwiftArtifacts.d.ts
2
+ interface ResolveSwiftArtifactsOptions {
3
+ configuration?: WasmBuildConfiguration;
4
+ extraLinkerFlags?: readonly string[];
5
+ extraSwiftBuildArgs?: readonly string[];
6
+ extraSwiftcFlags?: readonly string[];
7
+ initialMemory?: number | string;
8
+ maxMemory?: number | string;
9
+ packagePath: string;
10
+ product: string;
11
+ stackSize?: number | string;
12
+ swiftCommand?: readonly string[];
13
+ swiftSDK?: string;
14
+ }
15
+ type WasmBuildConfiguration = "debug" | "release";
16
+ interface SwiftArtifactPaths {
17
+ binPath: string;
18
+ wasmPath: string;
19
+ }
20
+ declare const requiredWasmSwiftFlags: readonly ["-Xswiftc", "-Osize", "-Xswiftc", "-Xfrontend", "-Xswiftc", "-disable-llvm-merge-functions-pass"];
21
+ declare const defaultWasmSwiftSDK = "swift-6.3.1-RELEASE_wasm";
22
+ declare const defaultInitialMemory = "536870912";
23
+ declare const defaultMaxMemory = "4294967296";
24
+ declare const defaultStackSize = "1048576";
25
+ declare function resolveSwiftArtifacts(options: ResolveSwiftArtifactsOptions): Promise<SwiftArtifactPaths>;
26
+ interface WasmBuildConfigurationLog {
27
+ configuration?: WasmBuildConfiguration;
28
+ packagePath: string;
29
+ product: string;
30
+ swiftlyWorkingDirectory: string;
31
+ buildCommand: string[];
32
+ showBinPathCommand: string[];
33
+ }
34
+ declare function hasRequiredWasmFlags(args: readonly string[]): boolean;
35
+ declare function wasmBuildConfigurationLogLines(config: WasmBuildConfigurationLog): string[];
36
+ declare function formatCommandForLogs(args: readonly string[]): string;
37
+ //#endregion
38
+ export { ResolveSwiftArtifactsOptions, SwiftArtifactPaths, WasmBuildConfiguration, defaultInitialMemory, defaultMaxMemory, defaultStackSize, defaultWasmSwiftSDK, formatCommandForLogs, hasRequiredWasmFlags, requiredWasmSwiftFlags, resolveSwiftArtifacts, wasmBuildConfigurationLogLines };
39
+ //# sourceMappingURL=resolveSwiftArtifacts.d.ts.map
@@ -0,0 +1,161 @@
1
+ import { runCommand } from "./runCommand.js";
2
+ import { swiftCommandPrefix } from "./swiftCommandPrefix.js";
3
+ import { access } from "node:fs/promises";
4
+ import { dirname, join, resolve } from "node:path";
5
+ //#region src/build/resolveSwiftArtifacts.ts
6
+ const requiredWasmSwiftFlags = [
7
+ "-Xswiftc",
8
+ "-Osize",
9
+ "-Xswiftc",
10
+ "-Xfrontend",
11
+ "-Xswiftc",
12
+ "-disable-llvm-merge-functions-pass"
13
+ ];
14
+ const defaultWasmSwiftSDK = "swift-6.3.1-RELEASE_wasm";
15
+ const defaultInitialMemory = "536870912";
16
+ const defaultMaxMemory = "4294967296";
17
+ const defaultStackSize = "1048576";
18
+ async function resolveSwiftArtifacts(options) {
19
+ const configuration = options.configuration ?? "release";
20
+ const swiftlyWorkingDirectory = await resolveSwiftlyWorkingDirectory(options.packagePath);
21
+ const swiftCommand = [...options.swiftCommand ?? swiftCommandPrefix()];
22
+ const environment = { ...process.env };
23
+ const swiftBuildArgs = [
24
+ "build",
25
+ "--package-path",
26
+ options.packagePath,
27
+ "--swift-sdk",
28
+ options.swiftSDK ?? "swift-6.3.1-RELEASE_wasm",
29
+ "-c",
30
+ configuration,
31
+ ...requiredSwiftFlags(configuration),
32
+ ...swiftcFlags(options.extraSwiftcFlags),
33
+ "-Xlinker",
34
+ `--initial-memory=${options.initialMemory ?? "536870912"}`,
35
+ "-Xlinker",
36
+ `--max-memory=${options.maxMemory ?? "4294967296"}`,
37
+ "-Xlinker",
38
+ "-z",
39
+ "-Xlinker",
40
+ `stack-size=${options.stackSize ?? "1048576"}`,
41
+ ...linkerFlags(options.extraLinkerFlags),
42
+ ...options.extraSwiftBuildArgs ?? []
43
+ ];
44
+ if (configuration === "release") confirmRequiredWasmFlags(swiftBuildArgs);
45
+ const buildCommand = [
46
+ ...swiftCommand,
47
+ ...swiftBuildArgs,
48
+ "--product",
49
+ options.product
50
+ ];
51
+ const showBinPathCommand = [
52
+ ...swiftCommand,
53
+ ...swiftBuildArgs,
54
+ "--show-bin-path"
55
+ ];
56
+ logWasmBuildConfiguration({
57
+ configuration,
58
+ packagePath: options.packagePath,
59
+ product: options.product,
60
+ swiftlyWorkingDirectory,
61
+ buildCommand,
62
+ showBinPathCommand
63
+ });
64
+ await runCommand(buildCommand, {
65
+ cwd: swiftlyWorkingDirectory,
66
+ env: environment
67
+ });
68
+ const binPath = await runCommand(showBinPathCommand, {
69
+ cwd: swiftlyWorkingDirectory,
70
+ env: environment
71
+ });
72
+ const wasmPath = join(binPath.trim(), `${options.product}.wasm`);
73
+ return {
74
+ binPath: binPath.trim(),
75
+ wasmPath
76
+ };
77
+ }
78
+ function hasRequiredWasmFlags(args) {
79
+ return containsSubsequence(args, requiredWasmSwiftFlags);
80
+ }
81
+ function confirmRequiredWasmFlags(args) {
82
+ if (hasRequiredWasmFlags(args)) return;
83
+ throw new Error(`missing required wasm Swift flags: ${requiredWasmSwiftFlags.join(" ")}`);
84
+ }
85
+ function requiredSwiftFlags(configuration) {
86
+ switch (configuration) {
87
+ case "debug": return [];
88
+ case "release": return requiredWasmSwiftFlags;
89
+ }
90
+ }
91
+ function swiftcFlags(flags) {
92
+ return (flags ?? []).flatMap((flag) => ["-Xswiftc", flag]);
93
+ }
94
+ function linkerFlags(flags) {
95
+ return (flags ?? []).flatMap((flag) => ["-Xlinker", flag]);
96
+ }
97
+ function logWasmBuildConfiguration(config) {
98
+ for (const line of wasmBuildConfigurationLogLines(config)) console.error(line);
99
+ }
100
+ function wasmBuildConfigurationLogLines(config) {
101
+ const configuration = config.configuration ?? "release";
102
+ return [
103
+ `WASM_BUILD_CONFIGURATION_NAME=${configuration}`,
104
+ `WASM_REQUIRED_FLAGS_CONFIRMED=${configuration === "release" ? "true" : "skipped"}`,
105
+ `WASM_REQUIRED_FLAGS=${requiredWasmSwiftFlags.join(" ")}`,
106
+ `WASM_REQUIRED_FLAGS_JSON=${JSON.stringify([...requiredWasmSwiftFlags])}`,
107
+ `WASM_BUILD_COMMAND=${formatCommandForLogs(config.buildCommand)}`,
108
+ `WASM_BUILD_COMMAND_ARGS_JSON=${JSON.stringify(config.buildCommand)}`,
109
+ `WASM_SHOW_BIN_PATH_COMMAND=${formatCommandForLogs(config.showBinPathCommand)}`,
110
+ `WASM_SHOW_BIN_PATH_COMMAND_ARGS_JSON=${JSON.stringify(config.showBinPathCommand)}`,
111
+ `WASM_BUILD_CONFIGURATION ${JSON.stringify({
112
+ packagePath: config.packagePath,
113
+ product: config.product,
114
+ configuration,
115
+ swiftlyWorkingDirectory: config.swiftlyWorkingDirectory,
116
+ requiredFlags: [...requiredWasmSwiftFlags],
117
+ buildCommand: formatCommandForLogs(config.buildCommand),
118
+ showBinPathCommand: formatCommandForLogs(config.showBinPathCommand)
119
+ })}`
120
+ ];
121
+ }
122
+ function formatCommandForLogs(args) {
123
+ return args.map(shellQuote).join(" ");
124
+ }
125
+ function containsSubsequence(args, expected) {
126
+ if (expected.length == 0) return true;
127
+ for (let index = 0; index <= args.length - expected.length; index += 1) {
128
+ let matches = true;
129
+ for (let expectedIndex = 0; expectedIndex < expected.length; expectedIndex += 1) if (args[index + expectedIndex] !== expected[expectedIndex]) {
130
+ matches = false;
131
+ break;
132
+ }
133
+ if (matches) return true;
134
+ }
135
+ return false;
136
+ }
137
+ function shellQuote(arg) {
138
+ if (/^[A-Za-z0-9_./:=+-]+$/.test(arg)) return arg;
139
+ return `'${arg.replaceAll("'", `'\\''`)}'`;
140
+ }
141
+ async function resolveSwiftlyWorkingDirectory(startPath) {
142
+ let currentPath = resolve(startPath);
143
+ while (true) {
144
+ if (await fileExists(join(currentPath, ".swift-version"))) return currentPath;
145
+ const parentPath = dirname(currentPath);
146
+ if (parentPath === currentPath) return resolve(startPath);
147
+ currentPath = parentPath;
148
+ }
149
+ }
150
+ async function fileExists(path) {
151
+ try {
152
+ await access(path);
153
+ return true;
154
+ } catch {
155
+ return false;
156
+ }
157
+ }
158
+ //#endregion
159
+ export { defaultInitialMemory, defaultMaxMemory, defaultStackSize, defaultWasmSwiftSDK, formatCommandForLogs, hasRequiredWasmFlags, requiredWasmSwiftFlags, resolveSwiftArtifacts, wasmBuildConfigurationLogLines };
160
+
161
+ //# sourceMappingURL=resolveSwiftArtifacts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveSwiftArtifacts.js","names":[],"sources":["../../../src/build/resolveSwiftArtifacts.ts"],"sourcesContent":["import { access } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { runCommand } from \"./runCommand.ts\";\nimport { swiftCommandPrefix } from \"./swiftCommandPrefix.ts\";\n\nexport interface ResolveSwiftArtifactsOptions {\n configuration?: WasmBuildConfiguration;\n extraLinkerFlags?: readonly string[];\n extraSwiftBuildArgs?: readonly string[];\n extraSwiftcFlags?: readonly string[];\n initialMemory?: number | string;\n maxMemory?: number | string;\n packagePath: string;\n product: string;\n stackSize?: number | string;\n swiftCommand?: readonly string[];\n swiftSDK?: string;\n}\n\nexport type WasmBuildConfiguration = \"debug\" | \"release\";\n\nexport interface SwiftArtifactPaths {\n binPath: string;\n wasmPath: string;\n}\n\nexport const requiredWasmSwiftFlags = [\n \"-Xswiftc\",\n \"-Osize\",\n \"-Xswiftc\",\n \"-Xfrontend\",\n \"-Xswiftc\",\n \"-disable-llvm-merge-functions-pass\",\n] as const;\n\nexport const defaultWasmSwiftSDK = \"swift-6.3.1-RELEASE_wasm\";\nexport const defaultInitialMemory = \"536870912\";\nexport const defaultMaxMemory = \"4294967296\";\nexport const defaultStackSize = \"1048576\";\n\nexport async function resolveSwiftArtifacts(\n options: ResolveSwiftArtifactsOptions\n): Promise<SwiftArtifactPaths> {\n const configuration = options.configuration ?? \"release\";\n const swiftlyWorkingDirectory = await resolveSwiftlyWorkingDirectory(options.packagePath);\n const swiftCommand = [...(options.swiftCommand ?? swiftCommandPrefix())];\n const environment = {\n ...process.env\n };\n\n // The browser WebAssembly API rejects function types with more than 1000\n // parameters. Swift's wasm release builds can trip that limit when LLVM's\n // merge-functions pass combines large outlined-copy helpers. `-Osize` helps,\n // but some Darwin CI runners still reproduce the failure unless we also\n // disable that merge pass explicitly.\n const swiftBuildArgs = [\n \"build\",\n \"--package-path\",\n options.packagePath,\n \"--swift-sdk\",\n options.swiftSDK ?? defaultWasmSwiftSDK,\n \"-c\",\n configuration,\n ...requiredSwiftFlags(configuration),\n ...swiftcFlags(options.extraSwiftcFlags),\n \"-Xlinker\",\n `--initial-memory=${options.initialMemory ?? defaultInitialMemory}`,\n \"-Xlinker\",\n `--max-memory=${options.maxMemory ?? defaultMaxMemory}`,\n \"-Xlinker\",\n \"-z\",\n \"-Xlinker\",\n `stack-size=${options.stackSize ?? defaultStackSize}`,\n ...linkerFlags(options.extraLinkerFlags),\n ...(options.extraSwiftBuildArgs ?? []),\n ];\n\n if (configuration === \"release\") {\n confirmRequiredWasmFlags(swiftBuildArgs);\n }\n\n const buildCommand = [\n ...swiftCommand,\n ...swiftBuildArgs,\n \"--product\",\n options.product,\n ];\n const showBinPathCommand = [\n ...swiftCommand,\n ...swiftBuildArgs,\n \"--show-bin-path\",\n ];\n\n logWasmBuildConfiguration({\n configuration,\n packagePath: options.packagePath,\n product: options.product,\n swiftlyWorkingDirectory,\n buildCommand,\n showBinPathCommand,\n });\n\n await runCommand(buildCommand, {\n cwd: swiftlyWorkingDirectory,\n env: environment,\n });\n\n const binPath = await runCommand(showBinPathCommand, {\n cwd: swiftlyWorkingDirectory,\n env: environment,\n });\n\n const wasmPath = join(binPath.trim(), `${options.product}.wasm`);\n return {\n binPath: binPath.trim(),\n wasmPath,\n };\n}\n\ninterface WasmBuildConfigurationLog {\n configuration?: WasmBuildConfiguration;\n packagePath: string;\n product: string;\n swiftlyWorkingDirectory: string;\n buildCommand: string[];\n showBinPathCommand: string[];\n}\n\nexport function hasRequiredWasmFlags(args: readonly string[]): boolean {\n return containsSubsequence(args, requiredWasmSwiftFlags);\n}\n\nfunction confirmRequiredWasmFlags(args: readonly string[]): void {\n if (hasRequiredWasmFlags(args)) {\n return;\n }\n\n throw new Error(\n `missing required wasm Swift flags: ${requiredWasmSwiftFlags.join(\" \")}`\n );\n}\n\nfunction requiredSwiftFlags(configuration: WasmBuildConfiguration): readonly string[] {\n switch (configuration) {\n case \"debug\":\n return [];\n case \"release\":\n return requiredWasmSwiftFlags;\n }\n}\n\nfunction swiftcFlags(flags: readonly string[] | undefined): string[] {\n return (flags ?? []).flatMap((flag) => [\"-Xswiftc\", flag]);\n}\n\nfunction linkerFlags(flags: readonly string[] | undefined): string[] {\n return (flags ?? []).flatMap((flag) => [\"-Xlinker\", flag]);\n}\n\nfunction logWasmBuildConfiguration(config: WasmBuildConfigurationLog): void {\n for (const line of wasmBuildConfigurationLogLines(config)) {\n console.error(line);\n }\n}\n\nexport function wasmBuildConfigurationLogLines(\n config: WasmBuildConfigurationLog\n): string[] {\n const configuration = config.configuration ?? \"release\";\n return [\n `WASM_BUILD_CONFIGURATION_NAME=${configuration}`,\n `WASM_REQUIRED_FLAGS_CONFIRMED=${configuration === \"release\" ? \"true\" : \"skipped\"}`,\n `WASM_REQUIRED_FLAGS=${requiredWasmSwiftFlags.join(\" \")}`,\n `WASM_REQUIRED_FLAGS_JSON=${JSON.stringify([...requiredWasmSwiftFlags])}`,\n `WASM_BUILD_COMMAND=${formatCommandForLogs(config.buildCommand)}`,\n `WASM_BUILD_COMMAND_ARGS_JSON=${JSON.stringify(config.buildCommand)}`,\n `WASM_SHOW_BIN_PATH_COMMAND=${formatCommandForLogs(config.showBinPathCommand)}`,\n `WASM_SHOW_BIN_PATH_COMMAND_ARGS_JSON=${JSON.stringify(config.showBinPathCommand)}`,\n `WASM_BUILD_CONFIGURATION ${JSON.stringify({\n packagePath: config.packagePath,\n product: config.product,\n configuration,\n swiftlyWorkingDirectory: config.swiftlyWorkingDirectory,\n requiredFlags: [...requiredWasmSwiftFlags],\n buildCommand: formatCommandForLogs(config.buildCommand),\n showBinPathCommand: formatCommandForLogs(config.showBinPathCommand),\n })}`,\n ];\n}\n\nexport function formatCommandForLogs(args: readonly string[]): string {\n return args.map(shellQuote).join(\" \");\n}\n\nfunction containsSubsequence(\n args: readonly string[],\n expected: readonly string[]\n): boolean {\n if (expected.length == 0) {\n return true;\n }\n\n for (let index = 0; index <= args.length - expected.length; index += 1) {\n let matches = true;\n for (let expectedIndex = 0; expectedIndex < expected.length; expectedIndex += 1) {\n if (args[index + expectedIndex] !== expected[expectedIndex]) {\n matches = false;\n break;\n }\n }\n if (matches) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction shellQuote(arg: string): string {\n if (/^[A-Za-z0-9_./:=+-]+$/.test(arg)) {\n return arg;\n }\n\n return `'${arg.replaceAll(\"'\", `'\\\\''`)}'`;\n}\n\nasync function resolveSwiftlyWorkingDirectory(\n startPath: string\n): Promise<string> {\n let currentPath = resolve(startPath);\n\n while (true) {\n if (await fileExists(join(currentPath, \".swift-version\"))) {\n return currentPath;\n }\n\n const parentPath = dirname(currentPath);\n if (parentPath === currentPath) {\n return resolve(startPath);\n }\n\n currentPath = parentPath;\n }\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import { delimiter, join } from "node:path";
2
+ import { accessSync } from "node:fs";
3
+ import { spawn } from "node:child_process";
4
+ //#region src/build/runCommand.ts
5
+ async function runCommand(cmd, options = {}) {
6
+ const executable = cmd[0];
7
+ if (!executable) throw new Error("cannot run an empty command");
8
+ const proc = spawn(executable, cmd.slice(1), {
9
+ cwd: options.cwd,
10
+ env: normalizeEnvironment(options.env),
11
+ stdio: [
12
+ "ignore",
13
+ "pipe",
14
+ "pipe"
15
+ ]
16
+ });
17
+ const stdoutChunks = [];
18
+ const stderrChunks = [];
19
+ proc.stdout.on("data", (chunk) => {
20
+ stdoutChunks.push(chunk);
21
+ });
22
+ proc.stderr.on("data", (chunk) => {
23
+ stderrChunks.push(chunk);
24
+ });
25
+ const exitCode = await new Promise((resolve, reject) => {
26
+ proc.on("error", reject);
27
+ proc.on("close", resolve);
28
+ });
29
+ const stdout = Buffer.concat(stdoutChunks).toString();
30
+ const stderr = Buffer.concat(stderrChunks).toString();
31
+ if (exitCode !== 0) throw new Error([stdout, stderr].filter(Boolean).join("\n").trim() || `command failed: ${cmd.join(" ")}`);
32
+ return stdout;
33
+ }
34
+ function findExecutable(name, pathValue = process.env.PATH) {
35
+ for (const directory of pathValue?.split(delimiter) ?? []) {
36
+ if (!directory) continue;
37
+ const candidate = join(directory, name);
38
+ try {
39
+ accessSync(candidate);
40
+ return candidate;
41
+ } catch {
42
+ continue;
43
+ }
44
+ }
45
+ }
46
+ function normalizeEnvironment(env) {
47
+ if (!env) return;
48
+ const normalized = {};
49
+ for (const [key, value] of Object.entries(env)) if (value !== void 0) normalized[key] = value;
50
+ return normalized;
51
+ }
52
+ //#endregion
53
+ export { findExecutable, runCommand };
54
+
55
+ //# sourceMappingURL=runCommand.js.map