A local pnpm workspace that marries flighthq/flight
and flighthq/flight-reference so that local
Flight source acts as the dependency for flight-reference — without editing either repo.
flight-reference-dev/
├── pnpm-workspace.yaml # declares the workspace members (the glue)
├── .npmrc # workspace-linking + npm-compat settings (the glue)
├── package.json # convenience scripts (the glue)
├── repos.json # pinned refs for the two upstream repos
├── scripts/bootstrap.ts # cross-platform provisioner (clone + link + build)
├── flight/ # provisioned clone — provides @flighthq/* packages
└── flight-reference/ # provisioned clone — depends on @flighthq/*
All glue lives at this root. flight/ and flight-reference/ are ordinary standalone
clones (git-ignored here) that the bootstrapper provisions — left pristine, on branches
you can edit and open PRs from.
From a bare machine that only has Node (18+), one command does everything — clone both repos at their pinned refs, install, and build Flight:
./bootstrap.sh # macOS / Linux
npx -y tsx scripts/bootstrap.ts # any platform (Windows included)bootstrap.sh is just a thin wrapper around scripts/bootstrap.ts (all the real,
cross-platform logic is in the TS). Re-run either any time to fast-forward the clones and
re-link; it's idempotent. Once the workspace has a toolchain you can also use pnpm setup.
Pinning: edit repos.json to set each repo's ref to a branch, tag, or commit SHA.
Override one repo for a single run with an env var named after its directory:
FLIGHT_REF=my-feature FLIGHT_REFERENCE_REF=v0.2.1 npx -y tsx scripts/bootstrap.tsWhy a clone script (not submodules or symlinks): for an ephemeral workspace it's the only approach that's fully self-contained and leaves each repo as a normal branch checkout. Symlinks depend on external dirs existing (the original
flight → ../flight/mainbroke for exactly this reason); submodules add detached-HEAD +submodule updateceremony. The pnpm globs work regardless, so you can still swap in a symlink if you keep a shared canonical checkout.
flight-reference (and Flight's own internal packages) declare their @flighthq/*
dependencies as "*". The root .npmrc sets link-workspace-packages=true and
prefer-workspace-packages=true, so pnpm satisfies those from the local flight/packages/*
instead of the published npm builds. Verify with:
node -e "console.log(require.resolve('@flighthq/sdk'))" # inside flight-reference/
# -> .../flight-reference-dev/flight/packages/sdk/dist/index.jsPackages that the checked-out Flight does not contain (e.g. @flighthq/filters*) fall back
to the npm registry automatically.
Both repos behave normally — run their own scripts from either directory (pnpm walks up to this workspace) or via the root shortcuts:
# Flight — its normal scripts all work
pnpm --filter flight build
pnpm --filter flight test
cd flight && pnpm build # equivalent
# flight-reference — two modes:
# (a) Built mode (default): resolves @flighthq/* from flight/packages/*/dist.
# Rebuild Flight (pnpm build:flight) after changing Flight source.
cd flight-reference && pnpm dev -- <case>
# (b) Source mode (live HMR of Flight source): the root `ref:*` scripts set
# FLIGHT_REPO=./flight, which flight-reference's vite.config reads to alias
# @flighthq/* straight to flight/packages/*/src — no rebuild needed.
pnpm ref:dev <case> # e.g. pnpm ref:dev spring
pnpm ref:build
pnpm ref:checkSource mode is flight-reference's built-in
FLIGHT_REPOmechanism.pnpm ref:*just points it at this workspace'sflight/. Prefer it when actively editing Flight and want instant reload.
pnpm-lock.yamlis the single source of truth; the per-repopackage-lock.jsonfiles are ignored.tsctype errors originating inflight-reference/content/**/flight/srcmean the checked-out Flight commit has drifted from what those sample cases expect — pin matchingrefs inrepos.jsonand re-run the bootstrapper to realign. flight-reference's ownsrc/is unaffected.- To include Flight's Electron example host, add
flight/examples/runners/electrontopnpm-workspace.yaml(left out by default to avoid the large native download).