|
| 1 | +/** |
| 2 | + * orchestrion code-transform plugin for Bun's bundler (`bun build`). |
| 3 | + * |
| 4 | + * Usage: |
| 5 | + * |
| 6 | + * ```ts |
| 7 | + * import { sentryBunPlugin } from '@sentry/bun/plugin'; |
| 8 | + * await Bun.build({ |
| 9 | + * entrypoints: ['./app.ts'], |
| 10 | + * plugins: [sentryBunPlugin()], |
| 11 | + * }); |
| 12 | + * ``` |
| 13 | + * |
| 14 | + * This is BUILD-ONLY. Runtime instrumentation (`bun run`) is intentionally not |
| 15 | + * offered: a module returned by a runtime `onLoad` plugin in Bun loses its |
| 16 | + * CommonJS named exports. |
| 17 | + * |
| 18 | + * When https://github.com/oven-sh/bun/pull/31770 lands, we can revisit. |
| 19 | + * |
| 20 | + * Until then, Bun apps must bundle to get orchestrion instrumentation. In dev |
| 21 | + * (ie, `bun run`) there is simply no instrumentation, which is clearer than |
| 22 | + * partial/inconsistent coverage. |
| 23 | + * |
| 24 | + * Shipped as both ESM and CJS (via the `@sentry/bun/plugin` subpath) so a user's |
| 25 | + * `bun build` script can be authored in either module system. It's a plain |
| 26 | + * library import here (not a `--import`/`--preload` hook), so CJS is fine; Bun |
| 27 | + * resolves the underlying ESM-only transformer in either module system. |
| 28 | + * |
| 29 | + * @module |
| 30 | + */ |
| 31 | + |
| 32 | +// eslint-disable-next-line @typescript-eslint/no-explicit-any |
| 33 | +type UnknownPlugin = any; |
| 34 | + |
| 35 | +// `@apm-js-collab/code-transformer-bundler-plugins/bun` is published ESM-only |
| 36 | +// (no `require` arm, unlike its `/vite` entry). The ESM build imports it; the |
| 37 | +// CJS build requires it. Bun resolves correctly for ESM modules in either |
| 38 | +// module system. |
| 39 | +import codeTransformer from '@apm-js-collab/code-transformer-bundler-plugins/bun'; |
| 40 | +import { SENTRY_INSTRUMENTATIONS } from '@sentry/server-utils/orchestrion/config'; |
| 41 | + |
| 42 | +const BUNDLER_MARKER_BANNER = |
| 43 | + ';(globalThis.__SENTRY_ORCHESTRION__=(globalThis.__SENTRY_ORCHESTRION__||{})).bundler=true;'; |
| 44 | + |
| 45 | +// Minimal shape of Bun's `PluginBuilder` that we touch. Typed locally instead |
| 46 | +// of depending on `bun-types`, which would pull Bun's globals. |
| 47 | +interface BunPluginBuilder { |
| 48 | + config?: { banner?: string }; |
| 49 | +} |
| 50 | + |
| 51 | +/** |
| 52 | + * Returns the orchestrion code-transform plugin for Bun's bundler, configured |
| 53 | + * with the central `SENTRY_INSTRUMENTATIONS`. The plugin injects |
| 54 | + * `diagnostics_channel.tracingChannel` calls into the instrumented libraries as |
| 55 | + * `bun build` bundles them, and injects a banner that sets |
| 56 | + * `globalThis.__SENTRY_ORCHESTRION__.bundler = true` when the bundle boots |
| 57 | + * |
| 58 | + * Pass the result to `Bun.build({ plugins: [...] })`. |
| 59 | + * |
| 60 | + * @example |
| 61 | + * ```ts |
| 62 | + * import { sentryBunPlugin } from '@sentry/bun/plugin'; |
| 63 | + * await Bun.build({ entrypoints: ['./app.ts'], plugins: [sentryBunPlugin()] }); |
| 64 | + * ``` |
| 65 | + */ |
| 66 | +export function sentryBunPlugin(): UnknownPlugin { |
| 67 | + // Typed upstream as an esbuild `Plugin`, but Bun passes its own |
| 68 | + // `PluginBuilder` (which has the `onLoad` the transform uses) to `setup`. |
| 69 | + // Cast to the Bun-compatible shape so we can forward Bun's builder to its |
| 70 | + // `setup`. |
| 71 | + const transformer = codeTransformer({ instrumentations: SENTRY_INSTRUMENTATIONS }) as unknown as { |
| 72 | + setup: (build: BunPluginBuilder) => void; |
| 73 | + }; |
| 74 | + |
| 75 | + return { |
| 76 | + name: 'sentry-orchestrion', |
| 77 | + setup(build: BunPluginBuilder): void { |
| 78 | + // Inject a banner so the bundled output sets `bundler: true` at boot. |
| 79 | + // `config` is the `Bun.build` config and is present when this plugin |
| 80 | + // is passed to `Bun.build({ plugins: [...] })`. |
| 81 | + if (build.config) { |
| 82 | + const existing = build.config.banner ?? ''; |
| 83 | + build.config.banner = existing ? `${existing}\n${BUNDLER_MARKER_BANNER}` : BUNDLER_MARKER_BANNER; |
| 84 | + } |
| 85 | + |
| 86 | + // Delegate to the upstream code-transformer, which registers the `onLoad` |
| 87 | + // hook that does the actual channel injection. |
| 88 | + transformer.setup(build); |
| 89 | + }, |
| 90 | + }; |
| 91 | +} |
0 commit comments