Skip to content

Commit ef6dfac

Browse files
committed
refs and fixes
1 parent bec403e commit ef6dfac

4 files changed

Lines changed: 97 additions & 131 deletions

File tree

packages/bun/src/plugin.ts

Lines changed: 2 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -11,127 +11,8 @@
1111
* });
1212
* ```
1313
*
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.
2815
*
2916
* @module
3017
*/
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';

packages/server-utils/package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@
5656
"import": "./build/esm/orchestrion/bundler/esbuild.js",
5757
"require": "./build/cjs/orchestrion/bundler/esbuild.js"
5858
},
59-
"./orchestrion/bundler-transforms": {
60-
"types": "./build/types/orchestrion/bundler/moduleInjectedTransform.d.ts",
61-
"import": "./build/esm/orchestrion/bundler/moduleInjectedTransform.js",
62-
"require": "./build/cjs/orchestrion/bundler/moduleInjectedTransform.js"
59+
"./orchestrion/bun": {
60+
"types": "./build/types/orchestrion/bundler/bun.d.ts",
61+
"import": "./build/esm/orchestrion/bundler/bun.js",
62+
"require": "./build/cjs/orchestrion/bundler/bun.js"
6363
}
6464
},
6565
"typesVersions": {
@@ -79,8 +79,8 @@
7979
"orchestrion/esbuild": [
8080
"build/types/orchestrion/bundler/esbuild.d.ts"
8181
],
82-
"orchestrion/bundler-transforms": [
83-
"build/types/orchestrion/bundler/moduleInjectedTransform.d.ts"
82+
"orchestrion/bun": [
83+
"build/types/orchestrion/bundler/bun.d.ts"
8484
]
8585
}
8686
},

packages/server-utils/rollup.npm.config.mjs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,6 @@ export default [
8282
// `.../orchestrion/vite`, etc.) — none are reachable from `src/index.ts`, so
8383
// we list them as separate entrypoints to guarantee they end up in build/esm
8484
// and build/cjs.
85-
//
86-
// The runtime diagnostics-channel injection (`register`/`hook`/`import-hook` + the vendored
87-
// transformer chain) lives in `@sentry/server-runtime-injection` — it must stay external when
88-
// apps bundle, so it is a separate package rather than a subpath here.
8985
entrypoints: [
9086
'src/index.ts',
9187
'src/index.no-diagnostic-channels.ts',
@@ -95,6 +91,7 @@ export default [
9591
'src/orchestrion/bundler/webpack.ts',
9692
'src/orchestrion/bundler/webpack-loader.ts',
9793
'src/orchestrion/bundler/esbuild.ts',
94+
'src/orchestrion/bundler/bun.ts',
9895
],
9996
packageSpecificConfig: {
10097
plugins: [debugNodeAlias, commonJSPlugin, thirdPartyLicensePlugin],
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import codeTransformer from '@apm-js-collab/code-transformer-bundler-plugins/bun';
2+
import { INSTRUMENTED_MODULE_NAMES, SENTRY_INSTRUMENTATIONS, withoutInstrumentedExternals } from '../config';
3+
import { moduleInjectedTransforms, ORCHESTRION_BUNDLER_MARKER_BANNER } from './moduleInjectedTransform';
4+
5+
// oxlint-disable-next-line typescript/no-explicit-any
6+
type UnknownPlugin = any;
7+
8+
// Minimal shape of Bun's `PluginBuilder` that we touch. Typed locally instead
9+
// of depending on `bun-types`, which would pull Bun's globals into this build.
10+
interface BunPluginBuilder {
11+
config?: { banner?: string; external?: string[]; packages?: 'bundle' | 'external' };
12+
}
13+
14+
/**
15+
* Sentry orchestrion code-transform plugin for Bun's bundler (`bun build`), exposed to users via the
16+
* `@sentry/bun/plugin` subpath (which re-exports this as `sentryBunPlugin`).
17+
*
18+
* This is BUILD-ONLY. Runtime instrumentation (`bun run`) is intentionally not offered: a module
19+
* returned by a runtime `onLoad` plugin in Bun loses its CommonJS named exports. When
20+
* https://github.com/oven-sh/bun/pull/31770 lands, we can revisit. Until then, Bun apps must bundle
21+
* to get build-time instrumentation; in dev (`bun run`) there is simply no instrumentation, which is
22+
* clearer than partial/inconsistent coverage.
23+
*
24+
* The plugin injects `diagnostics_channel.tracingChannel` calls into the instrumented libraries as
25+
* `bun build` bundles them — plus, via the module-injected transform, the snippet that records each
26+
* module on `globalThis.__SENTRY_ORCHESTRION__` when it is evaluated — and injects the marker banner
27+
* so `bundler` is set (to an empty `Set`) from boot, which gates the SDK's channel-integration setup
28+
* at `init()`.
29+
*/
30+
export function sentryOrchestrionPlugin(): UnknownPlugin {
31+
// Typed upstream as an esbuild `Plugin`, but Bun passes its own `PluginBuilder` (which has the
32+
// `onLoad` the transform uses) to `setup`. Cast to the Bun-compatible shape so we can forward
33+
// Bun's builder to its `setup`.
34+
const transformer = codeTransformer({
35+
instrumentations: SENTRY_INSTRUMENTATIONS,
36+
customTransforms: moduleInjectedTransforms(),
37+
}) as unknown as {
38+
setup: (build: BunPluginBuilder) => void;
39+
};
40+
41+
return {
42+
name: 'sentry-orchestrion',
43+
setup(build: BunPluginBuilder): void {
44+
// Inject the marker banner via Bun's native `banner` config (unlike the upstream
45+
// `injectDiagnostics` path, it needs no `outdir`). `config` is the `Bun.build` config and is
46+
// present when this plugin is passed to `Bun.build({ plugins: [...] })`.
47+
if (build.config) {
48+
const existing = build.config.banner ?? '';
49+
build.config.banner = existing
50+
? `${existing}\n${ORCHESTRION_BUNDLER_MARKER_BANNER}`
51+
: ORCHESTRION_BUNDLER_MARKER_BANNER;
52+
53+
// Force-bundle every instrumented package. An externalized dependency is resolved from
54+
// `node_modules` at runtime and never passes through the transform's `onLoad`, so its
55+
// diagnostics_channel calls would be silently never injected. Bun has no runtime fallback
56+
// here, so bundling is the only injection path.
57+
build.config.external = withoutInstrumentedExternals(build.config.external);
58+
59+
// A blanket externalization strategy like `packages: 'external'` or `'*'` in `external`
60+
// externalizes instrumented packages too, and `withoutInstrumentedExternals` only strips
61+
// exact names/subpaths (not these), so those packages ship un-transformed with no runtime
62+
// fallback. Forcing them back in via `onResolve` is not an option: Bun ignores
63+
// `{ external: false }` against a blanket strategy, and returning a resolved `path` corrupts
64+
// the package's ESM/CJS interop. So warn instead. This runs in the user's build script,
65+
// where the Sentry debug logger isn't enabled, and `console` is the thing to use.
66+
const blanketExternal =
67+
build.config.packages === 'external'
68+
? "packages: 'external'"
69+
: build.config.external?.includes('*')
70+
? "'*' in external"
71+
: undefined;
72+
if (blanketExternal) {
73+
// oxlint-disable-next-line no-console
74+
console.warn(
75+
`[Sentry] This Bun build externalizes all dependencies (${blanketExternal}), so Sentry ` +
76+
'cannot instrument bundled libraries. Instrumentation will be missing for any of ' +
77+
`these packages your app uses: ${INSTRUMENTED_MODULE_NAMES.join(', ')}. To instrument them, ` +
78+
'externalize only the specific packages you need external instead of all of them.',
79+
);
80+
}
81+
}
82+
83+
// Delegate to the upstream code-transformer, which registers the `onLoad` hook that does the
84+
// actual channel injection.
85+
transformer.setup(build);
86+
},
87+
};
88+
}

0 commit comments

Comments
 (0)