Skip to content

Commit b99f437

Browse files
committed
feat(v10/cloudflare): Add wranglerConfigPath to Vite options
Backport of: #22800
1 parent 6a84103 commit b99f437

3 files changed

Lines changed: 87 additions & 4 deletions

File tree

packages/cloudflare/src/vite/autoInstrument.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { basename } from 'node:path';
12
import { collectAgentCandidates, detectAgentClasses, type ModuleResolver } from './agentClass';
23
import { buildOptionsImport, ENV_FALLBACK_OPTIONS_FN, resolveInstrumentFile } from './instrumentFile';
34
import { applyAutoInstrumentTransforms, type ClassWrapperKind, type ProgramBody } from './transform';
@@ -14,7 +15,7 @@ function normalizePath(path: string): string {
1415
// `.html`, … — sharing the entry's basename must never be treated as the entry.
1516
const JS_EXTENSION_REGEX = /\.[cm]?[jt]sx?$/;
1617

17-
export function sentryCloudflareAutoInstrumentPlugin() {
18+
export function sentryCloudflareAutoInstrumentPlugin(options: { wranglerConfigPath?: string } = {}) {
1819
let wranglerConfig: WranglerConfig | undefined;
1920
let entryFilePath: string | undefined;
2021

@@ -25,9 +26,18 @@ export function sentryCloudflareAutoInstrumentPlugin() {
2526
name: 'sentry-cloudflare-auto-instrument',
2627

2728
configResolved(config: { root: string; logger?: { warn(msg: string): void } }): void {
28-
const result = resolveWranglerConfig(config.root);
29+
const result = resolveWranglerConfig(config.root, options.wranglerConfigPath);
2930
if (!result) {
30-
config.logger?.warn('[sentry] No parseable wrangler config found — auto-instrumentation disabled.');
31+
// An explicit path that fails is a misconfiguration worth naming;
32+
// without one, hint at the option so custom-named configs (e.g. a
33+
// `configPath` handed to @cloudflare/vite-plugin) are discoverable.
34+
config.logger?.warn(
35+
options.wranglerConfigPath
36+
? `[sentry] Could not find or parse the wrangler config "${basename(options.wranglerConfigPath)}" ` +
37+
'(resolved against the Vite root) — auto-instrumentation disabled.'
38+
: '[sentry] No parseable wrangler config found — auto-instrumentation disabled. ' +
39+
'Set `wranglerConfigPath` if your config uses a custom name.',
40+
);
3141
return;
3242
}
3343

packages/cloudflare/src/vite/index.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ import { sentryCloudflareAutoInstrumentPlugin } from './autoInstrument';
1010
* Options for {@link sentryCloudflareVitePlugin}.
1111
*/
1212
export interface SentryCloudflareVitePluginOptions {
13+
/**
14+
* Path to the wrangler config, relative to the Vite root (or absolute).
15+
* Set this when your config doesn't use a default name — e.g. when you
16+
* pass `configPath: './wrangler.agent.jsonc'` to `@cloudflare/vite-plugin`,
17+
* which the Sentry plugin cannot see. When set, only this file is read
18+
* (no default-name probing), and a warning is emitted if it is missing or
19+
* unparseable.
20+
*
21+
* @default undefined (probes `wrangler.json`, `wrangler.jsonc`, `wrangler.toml` at the Vite root)
22+
*/
23+
wranglerConfigPath?: string;
1324
/**
1425
* Experimental options that may change or be removed without notice.
1526
*/
@@ -80,6 +91,8 @@ export function sentryCloudflareVitePlugin(options: SentryCloudflareVitePluginOp
8091
...(options._experimental?.useDiagnosticsChannelInjection
8192
? [sentryOrchestrionPlugin({ injectChannelSubscribers: true })]
8293
: []),
83-
...(options._experimental?.autoInstrumentation ? [sentryCloudflareAutoInstrumentPlugin()] : []),
94+
...(options._experimental?.autoInstrumentation
95+
? [sentryCloudflareAutoInstrumentPlugin({ wranglerConfigPath: options.wranglerConfigPath })]
96+
: []),
8497
];
8598
}

packages/cloudflare/test/vite/autoInstrument.test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,66 @@ describe('sentryCloudflareAutoInstrumentPlugin', () => {
354354
});
355355
});
356356

357+
describe('wranglerConfigPath option', () => {
358+
it('reads a custom-named wrangler config (e.g. wrangler.agent.jsonc)', async () => {
359+
const dir = writeTempDir({ 'wrangler.agent.jsonc': '{ "main": "src/agent.ts" }' });
360+
const plugin = sentryCloudflareAutoInstrumentPlugin({ wranglerConfigPath: './wrangler.agent.jsonc' });
361+
plugin.configResolved({ root: dir });
362+
363+
const code = 'export default { fetch() { return new Response("ok"); } };';
364+
const result = await plugin.transform.call({ parse: (c: string) => parseJS(c) }, code, join(dir, 'src/agent.ts'));
365+
366+
expect(result).toBeDefined();
367+
expect(result.code).toBe(
368+
[
369+
"import * as __SENTRY__ from '@sentry/cloudflare';",
370+
'const __SENTRY_DEFAULT_EXPORT__ = { fetch() { return new Response("ok"); } };',
371+
'export default __SENTRY__.withSentry(() => undefined, __SENTRY_DEFAULT_EXPORT__);',
372+
'',
373+
].join('\n'),
374+
);
375+
});
376+
377+
it('prefers the explicit path over default-name configs', async () => {
378+
const dir = writeTempDir({
379+
'wrangler.toml': 'main = "src/default.ts"',
380+
'wrangler.agent.jsonc': '{ "main": "src/agent.ts" }',
381+
});
382+
const plugin = sentryCloudflareAutoInstrumentPlugin({ wranglerConfigPath: 'wrangler.agent.jsonc' });
383+
plugin.configResolved({ root: dir });
384+
385+
const code = 'export default { fetch() { return new Response("ok"); } };';
386+
const tx = (id: string) => plugin.transform.call({ parse: (c: string) => parseJS(c) }, code, id);
387+
388+
// The probed default config's entry must not be treated as the worker entry…
389+
expect(await tx(join(dir, 'src/default.ts'))).toBeUndefined();
390+
// …while the explicit config's entry is.
391+
expect(await tx(join(dir, 'src/agent.ts'))).toBeDefined();
392+
});
393+
394+
it('warns with only the basename when the explicit path cannot be read', () => {
395+
const dir = writeTempDir({});
396+
const warnings: string[] = [];
397+
const plugin = sentryCloudflareAutoInstrumentPlugin({ wranglerConfigPath: 'nested/dir/wrangler.agent.jsonc' });
398+
plugin.configResolved({ root: dir, logger: { warn: msg => warnings.push(msg) } });
399+
400+
expect(warnings).toHaveLength(1);
401+
expect(warnings[0]).toContain('wrangler.agent.jsonc');
402+
// The full path may leak a location the user doesn't want in build logs.
403+
expect(warnings[0]).not.toContain('nested/dir');
404+
});
405+
406+
it('hints at the option when no default-named config is found', () => {
407+
const dir = writeTempDir({});
408+
const warnings: string[] = [];
409+
const plugin = sentryCloudflareAutoInstrumentPlugin();
410+
plugin.configResolved({ root: dir, logger: { warn: msg => warnings.push(msg) } });
411+
412+
expect(warnings).toHaveLength(1);
413+
expect(warnings[0]).toContain('`wranglerConfigPath`');
414+
});
415+
});
416+
357417
// ---------------------------------------------------------------------------
358418
// instrument.server.* auto-detection (config from a conventional module)
359419
// ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)