|
11 | 11 | * }); |
12 | 12 | * ``` |
13 | 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 build-time 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. |
| 14 | + * This is BUILD-ONLY. Runtime instrumentation (`bun run`) is currently not supported. |
28 | 15 | * |
29 | 16 | * @module |
30 | 17 | */ |
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 { |
41 | | - moduleInjectedTransforms, |
42 | | - ORCHESTRION_BUNDLER_MARKER_BANNER, |
43 | | -} from '@sentry/server-utils/orchestrion/bundler-transforms'; |
44 | | -import { |
45 | | - INSTRUMENTED_MODULE_NAMES, |
46 | | - SENTRY_INSTRUMENTATIONS, |
47 | | - withoutInstrumentedExternals, |
48 | | -} from '@sentry/server-utils/orchestrion/config'; |
49 | | - |
50 | | -// Minimal shape of Bun's `PluginBuilder` that we touch. Typed locally instead |
51 | | -// of depending on `bun-types`, which would pull Bun's globals. |
52 | | -interface BunPluginBuilder { |
53 | | - config?: { banner?: string; external?: string[]; packages?: 'bundle' | 'external' }; |
54 | | -} |
55 | | - |
56 | | -/** |
57 | | - * Returns the Sentry code-transform plugin for Bun's bundler, configured |
58 | | - * with the central `SENTRY_INSTRUMENTATIONS`. The plugin injects |
59 | | - * `diagnostics_channel.tracingChannel` calls into the instrumented libraries as |
60 | | - * `bun build` bundles them — plus, via the module-injected transform, the |
61 | | - * snippet that records each module on `globalThis.__SENTRY_ORCHESTRION__` when |
62 | | - * it is evaluated — and injects the marker banner so `bundler` is set (to an |
63 | | - * empty `Set`) from boot, which is what gates the SDK's channel-integration |
64 | | - * setup at `init()`. |
65 | | - * |
66 | | - * Pass the result to `Bun.build({ plugins: [...] })`. |
67 | | - * |
68 | | - * @example |
69 | | - * ```ts |
70 | | - * import { sentryBunPlugin } from '@sentry/bun/plugin'; |
71 | | - * await Bun.build({ entrypoints: ['./app.ts'], plugins: [sentryBunPlugin()] }); |
72 | | - * ``` |
73 | | - */ |
74 | | -export function sentryBunPlugin(): UnknownPlugin { |
75 | | - // Typed upstream as an esbuild `Plugin`, but Bun passes its own |
76 | | - // `PluginBuilder` (which has the `onLoad` the transform uses) to `setup`. |
77 | | - // Cast to the Bun-compatible shape so we can forward Bun's builder to its |
78 | | - // `setup`. |
79 | | - const transformer = codeTransformer({ |
80 | | - instrumentations: SENTRY_INSTRUMENTATIONS, |
81 | | - customTransforms: moduleInjectedTransforms(), |
82 | | - }) as unknown as { |
83 | | - setup: (build: BunPluginBuilder) => void; |
84 | | - }; |
85 | | - |
86 | | - return { |
87 | | - name: 'sentry-orchestrion', |
88 | | - setup(build: BunPluginBuilder): void { |
89 | | - // Inject the marker banner via Bun's native `banner` config (unlike the |
90 | | - // upstream `injectDiagnostics` path, it needs no `outdir`). `config` is |
91 | | - // the `Bun.build` config and is present when this plugin is passed to |
92 | | - // `Bun.build({ plugins: [...] })`. |
93 | | - if (build.config) { |
94 | | - const existing = build.config.banner ?? ''; |
95 | | - build.config.banner = existing |
96 | | - ? `${existing}\n${ORCHESTRION_BUNDLER_MARKER_BANNER}` |
97 | | - : ORCHESTRION_BUNDLER_MARKER_BANNER; |
98 | | - |
99 | | - // Force-bundle every instrumented package. An externalized dependency |
100 | | - // is resolved from `node_modules` at runtime and never passes throug |
101 | | - // the transform's `onLoad`, so its diagnostics_channel calls would |
102 | | - // be silently never injected. Bun has no runtime fallback here, so |
103 | | - // bundling is the only injection path. |
104 | | - build.config.external = withoutInstrumentedExternals(build.config.external); |
105 | | - |
106 | | - // A blanket externalization strategy like `packages: 'external'` or |
107 | | - // `'*'` in `external` externalizes instrumented packages too, and |
108 | | - // `withoutInstrumentedExternals` only strips exact names/subpaths (not |
109 | | - // these), so those packages ship un-transformed with no runtime |
110 | | - // fallback. Forcing them back in via `onResolve` is not an option: Bun |
111 | | - // ignores `{ external: false }` against a blanket strategy, and |
112 | | - // returning a resolved `path` corrupts the package's ESM/CJS interop. |
113 | | - // So warn instead. This runs in the user's build script, where the |
114 | | - // Sentry debug logger isn't enabled, and `console` is the thing to use. |
115 | | - const blanketExternal = |
116 | | - build.config.packages === 'external' |
117 | | - ? "packages: 'external'" |
118 | | - : build.config.external?.includes('*') |
119 | | - ? "'*' in external" |
120 | | - : undefined; |
121 | | - if (blanketExternal) { |
122 | | - // eslint-disable-next-line no-console |
123 | | - console.warn( |
124 | | - `[Sentry] This Bun build externalizes all dependencies (${blanketExternal}), so Sentry ` + |
125 | | - 'cannot instrument bundled libraries. Instrumentation will be missing for any of ' + |
126 | | - `these packages your app uses: ${INSTRUMENTED_MODULE_NAMES.join(', ')}. To instrument them, ` + |
127 | | - 'externalize only the specific packages you need external instead of all of them.', |
128 | | - ); |
129 | | - } |
130 | | - } |
131 | | - |
132 | | - // Delegate to the upstream code-transformer, which registers the `onLoad` |
133 | | - // hook that does the actual channel injection. |
134 | | - transformer.setup(build); |
135 | | - }, |
136 | | - }; |
137 | | -} |
| 18 | +export { sentryOrchestrionPlugin as sentryBunPlugin } from '@sentry/server-utils/orchestrion/bun'; |
0 commit comments