| 1 | // @ts-check |
| 2 | // Node dev-mode hooks: run an alchemy checkout from source with no build. |
| 3 | // |
| 4 | // 1. A synchronous `resolve` hook (`module.registerHooks`) enables the |
| 5 | // `bun` export condition for the monorepo's own packages (`alchemy`, |
| 6 | // `@alchemy.run/*`, `@distilled.cloud/*`), whose exports map that |
| 7 | // condition to `src/*.ts`. Under plain node the `import` condition |
| 8 | // resolves built `lib/` output instead — requiring a `tsc -b` first, |
| 9 | // and splitting the process across a src copy (the CLI) and a lib copy |
| 10 | // (the user's stack, which imports `alchemy`) whenever lib is stale. |
| 11 | // Packages without a `bun` condition (e.g. the published sigil) are |
| 12 | // untouched — an enabled condition their exports never mention simply |
| 13 | // doesn't match. |
| 14 | // 2. node-utils handles the actual `.ts`/`.tsx` loading with Rolldown's Oxc |
| 15 | // transformer and source maps. Node's built-in TypeScript support does not |
| 16 | // cover TSX and cannot apply each package's nearest tsconfig. |
| 17 | // |
| 18 | // Dev-only: the launcher (`bin/cli.js`) and the dev supervisor pass this |
| 19 | // via `--import` when spawning node in a checkout. Published installs run |
| 20 | // bundled `.js` and never load it; this file is excluded from the tarball. |
| 21 | // In a checkout, the source loader resolves from the workspace package. |
| 22 | import { enableCompileCache, registerHooks } from "node:module"; |
| 23 | |
| 24 | // See register-oxc.js: enabled before anything else loads so the whole graph — |
| 25 | // the loader's rolldown dependency and every transformed workspace file — |
| 26 | // is code-cached across the CLI, the dev exec child and the sidecars. |
| 27 | enableCompileCache(); |
| 28 | |
| 29 | /** @param {string} specifier */ |
| 30 | const isMonorepoPackage = (specifier) => |
| 31 | specifier === "alchemy" || |
| 32 | specifier.startsWith("alchemy/") || |
| 33 | specifier.startsWith("@alchemy.run/") || |
| 34 | specifier.startsWith("@distilled.cloud/"); |
| 35 | |
| 36 | registerHooks({ |
| 37 | resolve(specifier, context, nextResolve) { |
| 38 | if (!isMonorepoPackage(specifier)) return nextResolve(specifier, context); |
| 39 | return nextResolve(specifier, { |
| 40 | ...context, |
| 41 | conditions: ["bun", ...context.conditions], |
| 42 | }); |
| 43 | }, |
| 44 | }); |
| 45 | |
| 46 | // This import must happen after the condition hook is installed so a clean |
| 47 | // checkout resolves node-utils to source without requiring a prior build. |
| 48 | const { registerOxc } = await import("@alchemy.run/node-utils/register-oxc"); |
| 49 | |
| 50 | // Oxc discovers the nearest tsconfig for each transformed file. Alchemy's |
| 51 | // TSX therefore uses Alchemy's React settings while the user's config and its |
| 52 | // dependencies retain their own compiler settings. |
| 53 | registerOxc({ conditions: ["bun"] }); |