Skip to content

fix(build): bundle both macOS architectures of esbuild's native binary#463

Open
devrosx wants to merge 1 commit into
SuperCmdLabs:mainfrom
devrosx:fix/cross-arch-esbuild-binaries
Open

fix(build): bundle both macOS architectures of esbuild's native binary#463
devrosx wants to merge 1 commit into
SuperCmdLabs:mainfrom
devrosx:fix/cross-arch-esbuild-binaries

Conversation

@devrosx

@devrosx devrosx commented Jun 6, 2026

Copy link
Copy Markdown

Closes #462

Problem

esbuild ships its native binary via optionalDependencies (@esbuild/darwin-x64, @esbuild/darwin-arm64, …) — npm only installs the one matching the build host's architecture. The published x64 SuperCmd DMG is built on an Apple Silicon machine, so node_modules/@esbuild/ contains only darwin-arm64. electron-builder packs that same node_modules/ into both DMGs, so the x64 DMG ships an arm64 binary that it can't execute.

Every extension whose on-demand build invokes esbuild then fails on Intel/Rosetta Macs with the misleading message:

You installed esbuild for another platform than the one you're currently using.
…
Specifically the "@esbuild/darwin-arm64" package is present but this platform
needs the "@esbuild/darwin-x64" package instead.

The same problem exists for the nested esbuild copy under @raycast/api (a different version: 0.25.12 vs the top-level 0.19.12).

Confirmed by inspecting a packaged 1.0.25 build:

$ file SuperCmd.app/Contents/MacOS/SuperCmd
… Mach-O 64-bit executable x86_64

$ ls app.asar.unpacked/node_modules/@esbuild
darwin-arm64

$ ls app.asar.unpacked/node_modules/@raycast/api/node_modules/@esbuild
darwin-arm64

Fix

Two-part, both small:

  1. Pin both platform packages explicitly in package.json so npm always installs them for the top-level esbuild regardless of the build host:

    "@esbuild/darwin-arm64": "0.19.12",
    "@esbuild/darwin-x64": "0.19.12",
  2. Add scripts/ensure-cross-arch-esbuild.mjs (run from postinstall) which walks every esbuild folder it finds in node_modules (handles nested copies like @raycast/api/node_modules/esbuild at their own version) and npm packs the missing @esbuild/darwin-* sibling at the matching version. Idempotent, runs only on macOS, no-op when nothing is missing.

The script handles future esbuild version drift (e.g. when @raycast/api bumps its esbuild dependency) without needing a corresponding package.json change.

Verification

Reproduced the original failure with SuperCmd 1.0.25 on macOS 13.6 x86_64 (issue #462).

Local smoke test of the script:

# top-level esbuild 0.19.12 + nested @raycast/api/node_modules/esbuild 0.25.12,
# host arch x64 → only darwin-x64 installed.

$ node scripts/ensure-cross-arch-esbuild.mjs
  → installing @esbuild/darwin-arm64@0.25.12 for …/@raycast/api/node_modules/esbuild
  → installing @esbuild/darwin-arm64@0.19.12 for …/node_modules/esbuild
ensure-cross-arch-esbuild: added 2 missing platform package(s).

$ file node_modules/@esbuild/darwin-x64/bin/esbuild
… Mach-O 64-bit executable x86_64
$ file node_modules/@esbuild/darwin-arm64/bin/esbuild
… Mach-O 64-bit executable arm64

# re-run is a no-op:
$ node scripts/ensure-cross-arch-esbuild.mjs
ensure-cross-arch-esbuild: all esbuild instances already have both macOS binaries.

Alternatives considered

  • Build x64 and arm64 in separate CI jobs (Intel + Apple Silicon runners). Cleanest but doubles CI cost and requires CI changes; this PR is contained to two files and doesn't touch the release pipeline.
  • Bumping the top-level esbuild to ^0.25.x to deduplicate with @raycast/api's copy. Reduces the number of esbuild instances but is a larger functional change and doesn't itself fix the cross-arch problem.

Closes SuperCmdLabs#462

esbuild's native binary is delivered via optional dependencies that npm
resolves to the build host's architecture only. Building on Apple Silicon
populates node_modules/@esbuild/ with darwin-arm64 alone; electron-builder
then packs the same node_modules into both the x64 and arm64 DMGs, so the
x64 DMG ships an unusable arm64 binary. Every extension whose on-demand
build invokes esbuild fails on Intel/Rosetta with:

  You installed esbuild for another platform than the one you're currently
  using. ... the "@esbuild/darwin-arm64" package is present but this
  platform needs the "@esbuild/darwin-x64" package instead.

The same issue affects the nested esbuild copy under @raycast/api.

Changes:
- Pin @esbuild/darwin-x64 and @esbuild/darwin-arm64 at 0.19.12 (matching
  the top-level esbuild) so npm install always brings them in.
- Add scripts/ensure-cross-arch-esbuild.mjs, run from postinstall, which
  walks every esbuild folder in node_modules (handles the nested
  @raycast/api copy at its own version) and drops in any missing macOS
  platform binary via npm pack. Idempotent and a no-op outside macOS.

Tested by reproducing the failure on macOS x86_64 with SuperCmd 1.0.25
and verifying both binaries land in node_modules after install.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

x64 builds ship arm64 esbuild binaries → all extensions fail to load on Intel/Rosetta Macs

1 participant