Repository navigation
fix(desktop): make macOS universal and x64 builds actually work - #193
Merged
Merged
Conversation
Three things had to change together before `--universal` produced a
working app.
The vite build rewrote `@lydell/node-pty` to
`@lydell/node-pty-${process.platform}-${process.arch}` — the build
host's arch. `electron-vite build` runs before `electron-builder` and
emits one bundle for whatever is packaged afterwards, so on Apple
Silicon that baked darwin-arm64 in and an x64 or universal build shipped
a shell that starts and then cannot load its terminal backend. The
wrapper does the same lookup itself at run time, so it is externalised
instead.
That alone would have been worse than the bug: the wrapper was a
devDependency and never shipped. `asar list` on a packaged build showed
the platform packages present and `@lydell/node-pty/index.js` absent, so
the require could not resolve. The bundled server already required the
bare wrapper, so packaged builds were carrying that break already. It
moves to dependencies.
Finally, both halves of a universal build carry all six platform
packages, leaving eight byte-identical Mach-O files — including
node-pty's extension-less spawn-helper — which @electron/universal
rejects. `mac.x64ArchFiles` covers the unpacked node_modules, where
every binary is a prebuilt chosen at run time.
Verified on a packaged universal app run from outside the repo: main,
framework and helper are all x86_64+arm64, both darwin prebuilds ship,
and pty.node loads from inside the bundle. Run from inside the repo,
Node's module walk-up finds the repo node_modules and hides a missing
wrapper — which is how the second bug nearly shipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
electron-builder --mac --universaldid not produce a working app. Three separate problems, all found by building and then inspecting the packaged result.1. The terminal backend was pinned to the build host's arch
electron.vite.config.tsrewrote@lydell/node-ptyto@lydell/node-pty-${process.platform}-${process.arch}. That is the build host's arch, resolved once — andelectron-vite buildruns beforeelectron-builder, emitting a single bundle for whatever is packaged afterwards. On Apple Silicon it bakeddarwin-arm64in, so an--x64or--universalbuild shipped an Electron shell that starts and then cannot load its terminal backend. The failure reads as an app bug, not a build one.The wrapper's own entry already does the same
process.platform/process.archlookup at run time, so the import is externalised and left to dispatch.2. …but the wrapper was never shipped
That change alone would have been worse than the bug.
@lydell/node-ptywas adevDependency, so electron-builder never packed it — the old narrowing worked only because it pointed at the platform packages, which areoptionalDependencies.So
require("@lydell/node-pty")could not resolve. Worth noting the bundled server (packages/opencode/dist/node/node.js) already required the bare wrapper, so packaged builds were carrying this break before any of my changes. It moves todependencies.3. @electron/universal refused to merge
Both halves of a universal build carry all six platform packages, so eight Mach-O files end up byte-identical across x64 and arm64 — node-pty's
pty.nodeand its extension-lessspawn-helper, plus@parcel/watcherandmsgpackr-extract.@electron/universaltreats that as a mistake and aborts. They are prebuilt binaries chosen at run time, not per-arch build output, somac.x64ArchFilesnow covers the unpackednode_modules.I enumerated all eight by diffing the two temp app trees rather than discovering them one failed build at a time — my first pattern only covered
*.nodeand missedspawn-helper.Verification
On a packaged universal app, copied outside the repo and launched:
Running it from inside the repo is not a valid test: Node's module walk-up finds
codev-code/node_modulesand masks a missing wrapper. That is exactly how problem 2 nearly shipped — the app looked fine until I moved it to/tmp.Intel itself is unverified — I have no x64 hardware here. What is verified is that the x64 prebuild ships and that the selection is made at run time from
process.arch.bun typecheckclean;bun lint4741 + 1 error againstmain's 4746 + 1.Not included
No CHANGELOG entry:
release.ymlbuilds no desktop artifact, so no shipped build changes behaviour for a user today. Worth adding if desktop builds ever start shipping — at which point problem 2 would have made the terminal dead on arrival.🤖 Generated with Claude Code