Skip to content

Compiled binary: Playwright readiness check always fails (import('playwright') can't resolve in single-file bun --compile build) #17

Description

@RichardBray

Bug

Running any command via the installed compiled binary (~/.local/bin/x-dl, built with bun build --compile) always reports Playwright/Chromium as not ready, even when Chromium is correctly installed and launchable.

Repro

$ bunx playwright install chromium   # succeeds, browser downloads fine
$ x-dl install
🔍 Installing Playwright Chromium...
❌ Install failed: Playwright Chromium installation failed or browser is not launchable.

$ x-dl --url-only <tweet-url>
🔍 Checking for Playwright (Chromium)...
❌ Playwright Chromium is not ready

Meanwhile, launching Chromium manually via a plain bun script with node_modules/playwright present works fine (chromium.launch() succeeds, browser opens/closes normally).

Root cause (likely)

isPlaywrightChromiumReady() in src/installer.ts does:

const { chromium } = await import('playwright');
const browser = await chromium.launch({ headless: true });

When x-dl runs as a single-file compiled binary (bun build --compile), there's no node_modules/playwright directory on disk next to the executable. Playwright's launch logic needs its own package folder on disk (to locate the browser registry / driver scripts, not just the bundled JS), so the dynamic import/launch fails inside the compiled binary regardless of whether Chromium is actually installed and cached at ~/Library/Caches/ms-playwright.

This means x-dl install and the pre-flight check in normal usage are effectively broken for every user of the released compiled binaries (from install.sh / GitHub releases), since isPlaywrightChromiumReady() can never return true from that build target.

Workaround

Running from source instead of the compiled binary works, since a real node_modules/playwright exists to resolve against:

git clone https://github.com/RichardBray/x-dl.git
cd x-dl
bun install
bun run src/index.ts <url>

Suggested fix

Playwright's browser-launch and driver-resolution logic assumes a real package directory on disk. Options:

  • Don't bundle Playwright into the compiled binary; instead shell out to a locally-resolved Playwright (e.g. via bunx playwright subprocess) for both the readiness check and the actual extraction, rather than importing it in-process.
  • Or ship the compiled binary alongside a real node_modules/playwright directory (defeats the point of a single-file binary).
  • Or detect process.execPath/compiled-binary context and route Chromium launch through a subprocess wrapper that isn't compiled into the binary.

Confirmed on macOS arm64, x-dl v0.5.0, installed via the official install.sh one-liner.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions