| author | |
| committer | |
| log | 683aa87096899aa191e150429415ecc78966f538 |
| tree | 7c9414892116ee106b0f6c44e025590d3dc9afca |
| parent | d81278aa6b272bb3d3314266c6d17f22504d19d6 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>4 files changed, 142 insertions(+), 45 deletions(-)
apps/desktop/vite.config.ts+12-1| ... | @@ -1,9 +1,18 @@ | ... | @@ -1,9 +1,18 @@ |
| 1 | import "vite-plus/test/config"; | 1 | import "vite-plus/test/config"; |
| 2 | import { defineConfig } from "vite-plus"; | 2 | import { defineConfig } from "vite-plus"; |
| 3 | 3 | ||
| 4 | import { isDesktopRuntimeExternalDependency } from "../../scripts/lib/desktop-external-packages.ts"; | ||
| 4 | import { loadRepoEnv } from "../../scripts/lib/public-config.ts"; | 5 | import { loadRepoEnv } from "../../scripts/lib/public-config.ts"; |
| 5 | 6 | ||
| 6 | const repoEnv = loadRepoEnv(); | 7 | const repoEnv = loadRepoEnv(); |
| 8 | |||
| 9 | // The main process is bundled the same way the server CLI is: every JS | ||
| 10 | // dependency is inlined and only packages Node must load from disk stay | ||
| 11 | // external. The packaged app then installs just those externals, instead of a | ||
| 12 | // full production install of apps/desktop's dependency tree next to a server | ||
| 13 | // bundle that already carries its own copy of the same libraries. | ||
| 14 | const isMainProcessExternal = (id: string) => | ||
| 15 | id === "electron" || id.startsWith("electron/") || isDesktopRuntimeExternalDependency(id); | ||
| 7 | const shouldLaunchElectronAfterPack = process.env.T3CODE_DESKTOP_DEV === "1"; | 16 | const shouldLaunchElectronAfterPack = process.env.T3CODE_DESKTOP_DEV === "1"; |
| 8 | const publicConfigDefine = { | 17 | const publicConfigDefine = { |
| 9 | __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__: JSON.stringify( | 18 | __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__: JSON.stringify( |
| ... | @@ -55,7 +64,9 @@ export default defineConfig({ | ... | @@ -55,7 +64,9 @@ export default defineConfig({ |
| 55 | ], | 64 | ], |
| 56 | clean: true, | 65 | clean: true, |
| 57 | deps: { | 66 | deps: { |
| 58 | alwaysBundle: (id) => id.startsWith("@t3tools/"), | 67 | alwaysBundle: (id) => !id.startsWith("node:") && !isMainProcessExternal(id), |
| 68 | neverBundle: isMainProcessExternal, | ||
| 69 | onlyBundle: false, | ||
| 59 | }, | 70 | }, |
| 60 | ...(shouldLaunchElectronAfterPack ? { onSuccess: "node scripts/dev-electron.mjs" } : {}), | 71 | ...(shouldLaunchElectronAfterPack ? { onSuccess: "node scripts/dev-electron.mjs" } : {}), |
| 61 | }, | 72 | }, |
scripts/build-desktop-artifact.test.ts+51-14| ... | @@ -28,6 +28,7 @@ import { | ... | @@ -28,6 +28,7 @@ import { |
| 28 | DESKTOP_EXTRA_RESOURCES, | 28 | DESKTOP_EXTRA_RESOURCES, |
| 29 | LINUX_CAPTURE_EXTRA_RESOURCES, | 29 | LINUX_CAPTURE_EXTRA_RESOURCES, |
| 30 | LINUX_BROWSER_SECRET_EXTRA_RESOURCES, | 30 | LINUX_BROWSER_SECRET_EXTRA_RESOURCES, |
| 31 | LINUX_FILE_EXCLUSIONS, | ||
| 31 | MAC_FILE_EXCLUSIONS, | 32 | MAC_FILE_EXCLUSIONS, |
| 32 | InvalidMacPasskeyRpDomainError, | 33 | InvalidMacPasskeyRpDomainError, |
| 33 | InvalidMacPasskeyPublishableKeyError, | 34 | InvalidMacPasskeyPublishableKeyError, |
| ... | @@ -47,7 +48,7 @@ import { | ... | @@ -47,7 +48,7 @@ import { |
| 47 | resolveClerkPasskeyNativeArtifacts, | 48 | resolveClerkPasskeyNativeArtifacts, |
| 48 | resolveMacPasskeySigningConfiguration, | 49 | resolveMacPasskeySigningConfiguration, |
| 49 | resolveDesktopRuntimeDependencies, | 50 | resolveDesktopRuntimeDependencies, |
| 50 | resolveMacStageDependencies, | 51 | resolveMergedStageDependencies, |
| 51 | resolveFffNativeDependencies, | 52 | resolveFffNativeDependencies, |
| 52 | resolveBuildOptions, | 53 | resolveBuildOptions, |
| 53 | resolveDesktopBuildIconAssets, | 54 | resolveDesktopBuildIconAssets, |
| ... | @@ -367,26 +368,37 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { | ... | @@ -367,26 +368,37 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 367 | ), | 368 | ), |
| 368 | ); | 369 | ); |
| 369 | 370 | ||
| 370 | it("omits bundled workspace packages from staged desktop dependencies", () => { | 371 | it("stages only the desktop main-process externals", () => { |
| 371 | assert.deepStrictEqual( | 372 | assert.deepStrictEqual( |
| 372 | resolveDesktopRuntimeDependencies( | 373 | resolveDesktopRuntimeDependencies( |
| 373 | { | 374 | { |
| 375 | "@clerk/electron": "catalog:", | ||
| 376 | "@clerk/electron-passkeys": "catalog:", | ||
| 377 | "@crowecawcaw/xa11y": "0.13.0", | ||
| 374 | "@effect/platform-node": "catalog:", | 378 | "@effect/platform-node": "catalog:", |
| 379 | "@napi-rs/keyring": "^1.3.0", | ||
| 375 | "@t3tools/contracts": "workspace:*", | 380 | "@t3tools/contracts": "workspace:*", |
| 376 | "@t3tools/shared": "workspace:*", | 381 | "@t3tools/shared": "workspace:*", |
| 377 | "@t3tools/ssh": "workspace:*", | 382 | "dbus-next": "0.10.2", |
| 378 | "@t3tools/tailscale": "workspace:*", | ||
| 379 | effect: "catalog:", | 383 | effect: "catalog:", |
| 380 | electron: "41.5.0", | 384 | electron: "41.5.0", |
| 385 | "electron-updater": "^6.6.2", | ||
| 386 | "ffi-rs": "1.3.2", | ||
| 387 | "playwright-core": "1.60.0", | ||
| 381 | }, | 388 | }, |
| 382 | { | 389 | { |
| 390 | "@clerk/electron": "0.0.37", | ||
| 391 | "@clerk/electron-passkeys": "0.0.3", | ||
| 383 | "@effect/platform-node": "4.0.0-beta.59", | 392 | "@effect/platform-node": "4.0.0-beta.59", |
| 384 | effect: "4.0.0-beta.59", | 393 | effect: "4.0.0-beta.59", |
| 385 | }, | 394 | }, |
| 386 | ), | 395 | ), |
| 387 | { | 396 | { |
| 388 | "@effect/platform-node": "4.0.0-beta.59", | 397 | "@clerk/electron-passkeys": "0.0.3", |
| 389 | effect: "4.0.0-beta.59", | 398 | "@crowecawcaw/xa11y": "0.13.0", |
| 399 | "@napi-rs/keyring": "^1.3.0", | ||
| 400 | "ffi-rs": "1.3.2", | ||
| 401 | "playwright-core": "1.60.0", | ||
| 390 | }, | 402 | }, |
| 391 | ); | 403 | ); |
| 392 | }); | 404 | }); |
| ... | @@ -559,6 +571,8 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { | ... | @@ -559,6 +571,8 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 559 | 571 | ||
| 560 | assert.deepStrictEqual(DESKTOP_FILE_EXCLUSIONS, [ | 572 | assert.deepStrictEqual(DESKTOP_FILE_EXCLUSIONS, [ |
| 561 | "!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*", | 573 | "!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*", |
| 574 | "!**/*.map", | ||
| 575 | "!**/*.d.cts", | ||
| 562 | "!apps/desktop/resources/browser-secret", | 576 | "!apps/desktop/resources/browser-secret", |
| 563 | "!apps/desktop/resources/browser-secret/**/*", | 577 | "!apps/desktop/resources/browser-secret/**/*", |
| 564 | "!apps/desktop/prod-resources/browser-secret", | 578 | "!apps/desktop/prod-resources/browser-secret", |
| ... | @@ -661,6 +675,7 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { | ... | @@ -661,6 +675,7 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 661 | "**/node_modules/@anthropic-ai/claude-agent-sdk-*/**", | 675 | "**/node_modules/@anthropic-ai/claude-agent-sdk-*/**", |
| 662 | "**/node_modules/.bin", | 676 | "**/node_modules/.bin", |
| 663 | "**/node_modules/.bin/**", | 677 | "**/node_modules/.bin/**", |
| 678 | "**/*.map", | ||
| 664 | ]); | 679 | ]); |
| 665 | assert.deepStrictEqual(mac.dmg, { | 680 | assert.deepStrictEqual(mac.dmg, { |
| 666 | title: "T3 Code (Alpha) 1.2.3 Installer", | 681 | title: "T3 Code (Alpha) 1.2.3 Installer", |
| ... | @@ -679,7 +694,7 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { | ... | @@ -679,7 +694,7 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 679 | { name: "T3 Code", schemes: ["t3code", "t3code-dev"] }, | 694 | { name: "T3 Code", schemes: ["t3code", "t3code-dev"] }, |
| 680 | ]); | 695 | ]); |
| 681 | assert.deepStrictEqual(mac.files, [...DESKTOP_FILE_EXCLUSIONS, ...MAC_FILE_EXCLUSIONS]); | 696 | assert.deepStrictEqual(mac.files, [...DESKTOP_FILE_EXCLUSIONS, ...MAC_FILE_EXCLUSIONS]); |
| 682 | assert.deepStrictEqual(linux.files, DESKTOP_FILE_EXCLUSIONS); | 697 | assert.deepStrictEqual(linux.files, [...DESKTOP_FILE_EXCLUSIONS, ...LINUX_FILE_EXCLUSIONS]); |
| 683 | assert.deepStrictEqual(win.files, DESKTOP_FILE_EXCLUSIONS); | 698 | assert.deepStrictEqual(win.files, DESKTOP_FILE_EXCLUSIONS); |
| 684 | assert.deepStrictEqual(winWithoutWslPrebuild.files, win.files); | 699 | assert.deepStrictEqual(winWithoutWslPrebuild.files, win.files); |
| 685 | assert.notProperty(mac.mac as Record<string, unknown>, "sign"); | 700 | assert.notProperty(mac.mac as Record<string, unknown>, "sign"); |
| ... | @@ -690,11 +705,15 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { | ... | @@ -690,11 +705,15 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 690 | }).pipe(Effect.provide(ConfigProvider.layer(ConfigProvider.fromEnv({ env: {} })))), | 705 | }).pipe(Effect.provide(ConfigProvider.layer(ConfigProvider.fromEnv({ env: {} })))), |
| 691 | ); | 706 | ); |
| 692 | 707 | ||
| 693 | it("excludes Windows terminal binaries only from macOS packages", () => { | 708 | it("excludes foreign node-pty prebuilds from macOS and Linux packages", () => { |
| 694 | assert.deepStrictEqual(MAC_FILE_EXCLUSIONS, [ | 709 | assert.deepStrictEqual(MAC_FILE_EXCLUSIONS, [ |
| 695 | "!**/node_modules/node-pty/prebuilds/win32-*/**/*", | 710 | "!**/node_modules/node-pty/prebuilds/win32-*/**/*", |
| 696 | "!**/node_modules/node-pty/third_party/conpty/**/*", | 711 | "!**/node_modules/node-pty/third_party/conpty/**/*", |
| 697 | ]); | 712 | ]); |
| 713 | assert.deepStrictEqual(LINUX_FILE_EXCLUSIONS, [ | ||
| 714 | ...MAC_FILE_EXCLUSIONS, | ||
| 715 | "!**/node_modules/node-pty/prebuilds/darwin-*/**/*", | ||
| 716 | ]); | ||
| 698 | }); | 717 | }); |
| 699 | 718 | ||
| 700 | it("unpacks native binaries while keeping their JavaScript and metadata archived", () => { | 719 | it("unpacks native binaries while keeping their JavaScript and metadata archived", () => { |
| ... | @@ -725,9 +744,10 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { | ... | @@ -725,9 +744,10 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 725 | } | 744 | } |
| 726 | }); | 745 | }); |
| 727 | 746 | ||
| 728 | it("stages only server runtime externals in macOS packages", () => { | 747 | it("stages only the externals of both bundles in merged packages", () => { |
| 729 | assert.deepStrictEqual( | 748 | assert.deepStrictEqual( |
| 730 | resolveMacStageDependencies({ | 749 | resolveMergedStageDependencies({ |
| 750 | platform: "mac", | ||
| 731 | serverDependencies: { | 751 | serverDependencies: { |
| 732 | "@anthropic-ai/claude-agent-sdk": "^0.3.170", | 752 | "@anthropic-ai/claude-agent-sdk": "^0.3.170", |
| 733 | "@ff-labs/fff-node": "0.9.4", | 753 | "@ff-labs/fff-node": "0.9.4", |
| ... | @@ -737,8 +757,8 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { | ... | @@ -737,8 +757,8 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 737 | "node-pty": "1.1.0", | 757 | "node-pty": "1.1.0", |
| 738 | }, | 758 | }, |
| 739 | desktopDependencies: { | 759 | desktopDependencies: { |
| 740 | "@clerk/electron": "0.0.34", | 760 | "@napi-rs/keyring": "1.3.0", |
| 741 | effect: "4.0.0-beta.103", | 761 | "playwright-core": "1.60.0", |
| 742 | }, | 762 | }, |
| 743 | arch: "arm64", | 763 | arch: "arm64", |
| 744 | fffNodeVersion: "0.9.4", | 764 | fffNodeVersion: "0.9.4", |
| ... | @@ -747,11 +767,28 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { | ... | @@ -747,11 +767,28 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 747 | "@ff-labs/fff-node": "0.9.4", | 767 | "@ff-labs/fff-node": "0.9.4", |
| 748 | "msgpackr-extract": "3.0.4", | 768 | "msgpackr-extract": "3.0.4", |
| 749 | "node-pty": "1.1.0", | 769 | "node-pty": "1.1.0", |
| 750 | "@clerk/electron": "0.0.34", | 770 | "@napi-rs/keyring": "1.3.0", |
| 751 | effect: "4.0.0-beta.103", | 771 | "playwright-core": "1.60.0", |
| 752 | "@ff-labs/fff-bin-darwin-arm64": "0.9.4", | 772 | "@ff-labs/fff-bin-darwin-arm64": "0.9.4", |
| 753 | }, | 773 | }, |
| 754 | ); | 774 | ); |
| 775 | |||
| 776 | assert.deepStrictEqual( | ||
| 777 | resolveMergedStageDependencies({ | ||
| 778 | platform: "linux", | ||
| 779 | serverDependencies: { "@ff-labs/fff-node": "0.9.4", "node-pty": "1.1.0", effect: "4.0.0" }, | ||
| 780 | desktopDependencies: { "@crowecawcaw/xa11y": "0.13.0" }, | ||
| 781 | arch: "x64", | ||
| 782 | fffNodeVersion: "0.9.4", | ||
| 783 | }), | ||
| 784 | { | ||
| 785 | "@ff-labs/fff-node": "0.9.4", | ||
| 786 | "node-pty": "1.1.0", | ||
| 787 | "@crowecawcaw/xa11y": "0.13.0", | ||
| 788 | "@ff-labs/fff-bin-linux-x64-gnu": "0.9.4", | ||
| 789 | "@ff-labs/fff-bin-linux-x64-musl": "0.9.4", | ||
| 790 | }, | ||
| 791 | ); | ||
| 755 | }); | 792 | }); |
| 756 | 793 | ||
| 757 | it("excludes node-pty binaries for the other Windows architecture", () => { | 794 | it("excludes node-pty binaries for the other Windows architecture", () => { |
scripts/build-desktop-artifact.ts+41-30| ... | @@ -34,6 +34,7 @@ import { | ... | @@ -34,6 +34,7 @@ import { |
| 34 | selectCliRuntimeExternalDependencies, | 34 | selectCliRuntimeExternalDependencies, |
| 35 | } from "./lib/cli-external-packages.ts"; | 35 | } from "./lib/cli-external-packages.ts"; |
| 36 | import { loadRepoEnv } from "./lib/public-config.ts"; | 36 | import { loadRepoEnv } from "./lib/public-config.ts"; |
| 37 | import { selectDesktopRuntimeExternalDependencies } from "./lib/desktop-external-packages.ts"; | ||
| 37 | import { resolveCatalogDependencies } from "./lib/resolve-catalog.ts"; | 38 | import { resolveCatalogDependencies } from "./lib/resolve-catalog.ts"; |
| 38 | 39 | ||
| 39 | import * as NodeRuntime from "@effect/platform-node/NodeRuntime"; | 40 | import * as NodeRuntime from "@effect/platform-node/NodeRuntime"; |
| ... | @@ -959,6 +960,10 @@ export const DESKTOP_FILE_EXCLUSIONS = [ | ... | @@ -959,6 +960,10 @@ export const DESKTOP_FILE_EXCLUSIONS = [ |
| 959 | // so the SDK's optional platform packages (each a ~200MB bundled executable) | 960 | // so the SDK's optional platform packages (each a ~200MB bundled executable) |
| 960 | // are dead weight. The trailing dash keeps the SDK's own JS package. | 961 | // are dead weight. The trailing dash keeps the SDK's own JS package. |
| 961 | "!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*", | 962 | "!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*", |
| 963 | // Nothing in the packaged app enables source maps or serves them: the web | ||
| 964 | // client's maps alone were 50 MB of app.asar that no request ever read. | ||
| 965 | "!**/*.map", | ||
| 966 | "!**/*.d.cts", | ||
| 962 | "!apps/desktop/resources/browser-secret", | 967 | "!apps/desktop/resources/browser-secret", |
| 963 | "!apps/desktop/resources/browser-secret/**/*", | 968 | "!apps/desktop/resources/browser-secret/**/*", |
| 964 | "!apps/desktop/prod-resources/browser-secret", | 969 | "!apps/desktop/prod-resources/browser-secret", |
| ... | @@ -978,6 +983,12 @@ export const MAC_FILE_EXCLUSIONS = [ | ... | @@ -978,6 +983,12 @@ export const MAC_FILE_EXCLUSIONS = [ |
| 978 | "!**/node_modules/node-pty/prebuilds/win32-*/**/*", | 983 | "!**/node_modules/node-pty/prebuilds/win32-*/**/*", |
| 979 | "!**/node_modules/node-pty/third_party/conpty/**/*", | 984 | "!**/node_modules/node-pty/third_party/conpty/**/*", |
| 980 | ] as const; | 985 | ] as const; |
| 986 | // Linux builds node-pty from source, so every prebuild in the package is for | ||
| 987 | // another platform (58 MB of it Windows debug symbols). | ||
| 988 | export const LINUX_FILE_EXCLUSIONS = [ | ||
| 989 | ...MAC_FILE_EXCLUSIONS, | ||
| 990 | "!**/node_modules/node-pty/prebuilds/darwin-*/**/*", | ||
| 991 | ] as const; | ||
| 981 | 992 | ||
| 982 | // node-pty publishes both Darwin prebuilds in one package. Single-architecture | 993 | // node-pty publishes both Darwin prebuilds in one package. Single-architecture |
| 983 | // apps only need the native target; universal apps need both. An omitted arch | 994 | // apps only need the native target; universal apps need both. An omitted arch |
| ... | @@ -1013,6 +1024,7 @@ export const WINDOWS_SERVER_ASAR_IGNORE_GLOBS = [ | ... | @@ -1013,6 +1024,7 @@ export const WINDOWS_SERVER_ASAR_IGNORE_GLOBS = [ |
| 1013 | "**/node_modules/@anthropic-ai/claude-agent-sdk-*/**", | 1024 | "**/node_modules/@anthropic-ai/claude-agent-sdk-*/**", |
| 1014 | "**/node_modules/.bin", | 1025 | "**/node_modules/.bin", |
| 1015 | "**/node_modules/.bin/**", | 1026 | "**/node_modules/.bin/**", |
| 1027 | "**/*.map", | ||
| 1016 | ] as const; | 1028 | ] as const; |
| 1017 | 1029 | ||
| 1018 | export function resolveWindowsServerAsarIgnoreGlobs(arch: typeof BuildArch.Type) { | 1030 | export function resolveWindowsServerAsarIgnoreGlobs(arch: typeof BuildArch.Type) { |
| ... | @@ -1357,7 +1369,10 @@ export function resolveFffNativeDependencies( | ... | @@ -1357,7 +1369,10 @@ export function resolveFffNativeDependencies( |
| 1357 | ); | 1369 | ); |
| 1358 | } | 1370 | } |
| 1359 | 1371 | ||
| 1360 | export function resolveMacStageDependencies(input: { | 1372 | // macOS and Linux run both processes from one app.asar, so the stage installs |
| 1373 | // the union of what each bundle leaves external and nothing else. | ||
| 1374 | export function resolveMergedStageDependencies(input: { | ||
| 1375 | readonly platform: "mac" | "linux"; | ||
| 1361 | readonly serverDependencies: Record<string, string>; | 1376 | readonly serverDependencies: Record<string, string>; |
| 1362 | readonly desktopDependencies: Record<string, string>; | 1377 | readonly desktopDependencies: Record<string, string>; |
| 1363 | readonly arch: typeof BuildArch.Type; | 1378 | readonly arch: typeof BuildArch.Type; |
| ... | @@ -1366,7 +1381,7 @@ export function resolveMacStageDependencies(input: { | ... | @@ -1366,7 +1381,7 @@ export function resolveMacStageDependencies(input: { |
| 1366 | return { | 1381 | return { |
| 1367 | ...selectCliRuntimeExternalDependencies(input.serverDependencies), | 1382 | ...selectCliRuntimeExternalDependencies(input.serverDependencies), |
| 1368 | ...input.desktopDependencies, | 1383 | ...input.desktopDependencies, |
| 1369 | ...resolveFffNativeDependencies("mac", input.arch, input.fffNodeVersion), | 1384 | ...resolveFffNativeDependencies(input.platform, input.arch, input.fffNodeVersion), |
| 1370 | }; | 1385 | }; |
| 1371 | } | 1386 | } |
| 1372 | 1387 | ||
| ... | @@ -2551,6 +2566,11 @@ function validateBundledClientAssets(clientDir: string) { | ... | @@ -2551,6 +2566,11 @@ function validateBundledClientAssets(clientDir: string) { |
| 2551 | }); | 2566 | }); |
| 2552 | } | 2567 | } |
| 2553 | 2568 | ||
| 2569 | // The main-process bundle inlines every JS dependency (see | ||
| 2570 | // apps/desktop/vite.config.ts), so the packaged app only installs the packages | ||
| 2571 | // that bundle leaves external: native addons and playwright-core. Everything | ||
| 2572 | // else already lives inside dist-electron and would only duplicate what the | ||
| 2573 | // server bundle carries too. | ||
| 2554 | export function resolveDesktopRuntimeDependencies( | 2574 | export function resolveDesktopRuntimeDependencies( |
| 2555 | dependencies: Record<string, string> | undefined, | 2575 | dependencies: Record<string, string> | undefined, |
| 2556 | catalog: Record<string, string>, | 2576 | catalog: Record<string, string>, |
| ... | @@ -2559,14 +2579,11 @@ export function resolveDesktopRuntimeDependencies( | ... | @@ -2559,14 +2579,11 @@ export function resolveDesktopRuntimeDependencies( |
| 2559 | return {}; | 2579 | return {}; |
| 2560 | } | 2580 | } |
| 2561 | 2581 | ||
| 2562 | const runtimeDependencies = Object.fromEntries( | 2582 | return resolveCatalogDependencies( |
| 2563 | Object.entries(dependencies).filter( | 2583 | selectDesktopRuntimeExternalDependencies(dependencies), |
| 2564 | ([dependencyName, dependencySpec]) => | 2584 | catalog, |
| 2565 | dependencyName !== "electron" && !dependencySpec.startsWith("workspace:"), | 2585 | "apps/desktop", |
| 2566 | ), | ||
| 2567 | ); | 2586 | ); |
| 2568 | |||
| 2569 | return resolveCatalogDependencies(runtimeDependencies, catalog, "apps/desktop"); | ||
| 2570 | } | 2587 | } |
| 2571 | 2588 | ||
| 2572 | export const resolveGitHubPublishConfig = Effect.fn("resolveGitHubPublishConfig")(function* ( | 2589 | export const resolveGitHubPublishConfig = Effect.fn("resolveGitHubPublishConfig")(function* ( |
| ... | @@ -2672,7 +2689,11 @@ export const createBuildConfig = Effect.fn("createBuildConfig")(function* ( | ... | @@ -2672,7 +2689,11 @@ export const createBuildConfig = Effect.fn("createBuildConfig")(function* ( |
| 2672 | electronLanguages: [...DESKTOP_ELECTRON_LANGUAGES], | 2689 | electronLanguages: [...DESKTOP_ELECTRON_LANGUAGES], |
| 2673 | files: [ | 2690 | files: [ |
| 2674 | ...DESKTOP_FILE_EXCLUSIONS, | 2691 | ...DESKTOP_FILE_EXCLUSIONS, |
| 2675 | ...(platform === "mac" ? resolveMacFileExclusions(arch) : []), | 2692 | ...(platform === "mac" |
| 2693 | ? resolveMacFileExclusions(arch) | ||
| 2694 | : platform === "linux" | ||
| 2695 | ? LINUX_FILE_EXCLUSIONS | ||
| 2696 | : []), | ||
| 2676 | ], | 2697 | ], |
| 2677 | directories: { | 2698 | directories: { |
| 2678 | buildResources: "apps/desktop/resources", | 2699 | buildResources: "apps/desktop/resources", |
| ... | @@ -3735,29 +3756,19 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( | ... | @@ -3735,29 +3756,19 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( |
| 3735 | } | 3756 | } |
| 3736 | 3757 | ||
| 3737 | // Windows splits dependencies per process: app.asar carries only the | 3758 | // Windows splits dependencies per process: app.asar carries only the |
| 3738 | // desktop main-process runtime deps, while the server bundle's deps live in | 3759 | // desktop main-process externals, while the server bundle's externals live |
| 3739 | // the server.asar sidecar (see stageWindowsServerSidecar). macOS adds only | 3760 | // in the server.asar sidecar (see stageWindowsServerSidecar). macOS and |
| 3740 | // server packages that remain external to its merged app.asar. Linux retains | 3761 | // Linux merge both sets into one app.asar. |
| 3741 | // its existing full dependency tree. | ||
| 3742 | const stageDependencies = | 3762 | const stageDependencies = |
| 3743 | options.platform === "win" | 3763 | options.platform === "win" |
| 3744 | ? { ...resolvedDesktopRuntimeDependencies } | 3764 | ? { ...resolvedDesktopRuntimeDependencies } |
| 3745 | : options.platform === "mac" | 3765 | : resolveMergedStageDependencies({ |
| 3746 | ? resolveMacStageDependencies({ | 3766 | platform: options.platform, |
| 3747 | serverDependencies: resolvedServerDependencies, | 3767 | serverDependencies: resolvedServerDependencies, |
| 3748 | desktopDependencies: resolvedDesktopRuntimeDependencies, | 3768 | desktopDependencies: resolvedDesktopRuntimeDependencies, |
| 3749 | arch: options.arch, | 3769 | arch: options.arch, |
| 3750 | fffNodeVersion: serverPackageJson.dependencies["@ff-labs/fff-node"], | 3770 | fffNodeVersion: serverPackageJson.dependencies["@ff-labs/fff-node"], |
| 3751 | }) | 3771 | }); |
| 3752 | : { | ||
| 3753 | ...resolvedServerDependencies, | ||
| 3754 | ...resolvedDesktopRuntimeDependencies, | ||
| 3755 | ...resolveFffNativeDependencies( | ||
| 3756 | options.platform, | ||
| 3757 | options.arch, | ||
| 3758 | serverPackageJson.dependencies["@ff-labs/fff-node"], | ||
| 3759 | ), | ||
| 3760 | }; | ||
| 3761 | const stagePatchedDependencies = createStagePatchedDependencies( | 3772 | const stagePatchedDependencies = createStagePatchedDependencies( |
| 3762 | workspacePatchedDependencies, | 3773 | workspacePatchedDependencies, |
| 3763 | stageDependencies, | 3774 | stageDependencies, |
scripts/lib/desktop-external-packages.ts created+38| ... | @@ -0,0 +1,38 @@ | ||
| 1 | /** | ||
| 2 | * Packages the desktop main-process bundle must NOT inline. | ||
| 3 | * | ||
| 4 | * The desktop bundle follows the same policy as the server CLI bundle (see | ||
| 5 | * cli-external-packages.ts): everything is inlined except what Node has to | ||
| 6 | * load from the real filesystem. Both `apps/desktop/vite.config.ts` and the | ||
| 7 | * artifact stage in scripts/build-desktop-artifact.ts derive from this list, | ||
| 8 | * so a package that is external is also the only kind of package the staged | ||
| 9 | * production install carries. Anything not listed here ships inside | ||
| 10 | * `dist-electron/*.cjs` and has no `node_modules` presence at all. | ||
| 11 | * | ||
| 12 | * Entries are matched as prefixes so platform-specific siblings are covered. | ||
| 13 | */ | ||
| 14 | export const DESKTOP_RUNTIME_EXTERNAL_PREFIXES = [ | ||
| 15 | // Native addons and the wrappers that dlopen them by real path. | ||
| 16 | "@napi-rs/keyring", | ||
| 17 | "@crowecawcaw/xa11y", | ||
| 18 | "@clerk/electron-passkeys", | ||
| 19 | "ffi-rs", | ||
| 20 | "@yuuang/", | ||
| 21 | // Reads its own bundle from disk by resolving `playwright-core/package.json` | ||
| 22 | // at runtime and ships the browser driver alongside; there is nothing to | ||
| 23 | // gain from inlining a 10 MB file the code re-reads as text. | ||
| 24 | "playwright-core", | ||
| 25 | ] as const; | ||
| 26 | |||
| 27 | export function isDesktopRuntimeExternalDependency(id: string): boolean { | ||
| 28 | return DESKTOP_RUNTIME_EXTERNAL_PREFIXES.some((prefix) => id.startsWith(prefix)); | ||
| 29 | } | ||
| 30 | |||
| 31 | /** Select the desktop dependency roots whose runtime closure the stage must install. */ | ||
| 32 | export function selectDesktopRuntimeExternalDependencies( | ||
| 33 | dependencies: Readonly<Record<string, string>>, | ||
| 34 | ): Record<string, string> { | ||
| 35 | return Object.fromEntries( | ||
| 36 | Object.entries(dependencies).filter(([name]) => isDesktopRuntimeExternalDependency(name)), | ||
| 37 | ); | ||
| 38 | } | ||