Skip to content

Commit f72aa25

Browse files
committed
sdk
1 parent 7b65f27 commit f72aa25

4 files changed

Lines changed: 139 additions & 9 deletions

File tree

packages/astro/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"@sentry/core": "10.67.0",
6161
"@sentry/conventions": "^0.16.0",
6262
"@sentry/node": "10.67.0",
63+
"@sentry/server-utils": "10.67.0",
6364
"@sentry/bundler-plugins": "10.67.0"
6465
},
6566
"devDependencies": {

packages/astro/src/integration/cloudflare.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { builtinModules } from 'module';
44
// Derived from Astro's own config type rather than imported from `vite` directly: Astro bundles its
55
// own Vite version, which differs across the Astro majors we support. A plugin typed against any
66
// single Vite version is not assignable to `updateConfig({ vite: { plugins } })` for the others.
7-
type VitePlugin = Extract<NonNullable<NonNullable<AstroConfig['vite']>['plugins']>[number], { name: string }>;
7+
export type VitePlugin = Extract<NonNullable<NonNullable<AstroConfig['vite']>['plugins']>[number], { name: string }>;
88

99
// Build a set of all Node.js built-in module names, including both
1010
// bare names (e.g. "fs") and "node:" prefixed names (e.g. "node:fs").

packages/astro/src/integration/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { sentryVitePlugin } from '@sentry/bundler-plugins/vite';
2+
import { sentryOrchestrionPlugin } from '@sentry/server-utils/orchestrion/vite';
23
import type { AstroConfig, AstroIntegration, AstroIntegrationLogger } from 'astro';
34
import * as fs from 'fs';
45
import { createRequire } from 'module';
56
import * as path from 'path';
7+
import type { VitePlugin } from './cloudflare';
68
import { sentryCloudflareNodeWarningPlugin, sentryCloudflareVitePlugin } from './cloudflare';
79
import { buildClientSnippet, buildSdkInitFileImportSnippet, buildServerSnippet } from './snippets';
810
import type { SentryOptions } from './types';
@@ -34,6 +36,7 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => {
3436
sourcemaps,
3537
// todo(v11): Extract `release` build time option here - cannot be done currently, because it conflicts with the `DeprecatedRuntimeOptions` type
3638
// release,
39+
buildTimeInstrumentation,
3740
bundleSizeOptimizations,
3841
applicationKey,
3942
unstable_sentryVitePluginOptions,
@@ -167,6 +170,15 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => {
167170
const isCloudflare = config?.adapter?.name?.startsWith('@astrojs/cloudflare');
168171
const isCloudflareWorkers = isCloudflare && !isCloudflarePages();
169172

173+
// TODO: Cloudflare/workerd needs different wiring — skipped for now.
174+
if (sdkEnabled.server && !isCloudflare) {
175+
updateConfig({
176+
vite: {
177+
plugins: [sentryOrchestrionPlugin({ buildTimeInstrumentation }) as VitePlugin],
178+
},
179+
});
180+
}
181+
170182
if (isCloudflare) {
171183
try {
172184
const _require = createRequire(`${process.cwd()}/`);

packages/astro/test/integration/index.test.ts

Lines changed: 125 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,26 @@ vi.mock('@sentry/bundler-plugins/vite', () => ({
1010
sentryVitePlugin: vi.fn(args => sentryVitePluginSpy(args)),
1111
}));
1212

13+
// Stub the orchestrion plugin so these stay pure wiring tests (no apm code transformer pulled in).
14+
// Mirror the real plugin's contract: `buildTimeInstrumentation: false` yields the inert variant.
15+
const orchestrionVite = vi.fn((options?: { buildTimeInstrumentation?: boolean }) => ({
16+
name: options?.buildTimeInstrumentation === false ? 'sentry-orchestrion-disabled' : 'sentry-orchestrion-vite',
17+
}));
18+
vi.mock('@sentry/server-utils/orchestrion/vite', () => ({
19+
sentryOrchestrionPlugin: (options?: { buildTimeInstrumentation?: boolean }) => orchestrionVite(options),
20+
}));
21+
22+
// The cloudflare adapter path resolves `@sentry/cloudflare` via `createRequire` and calls
23+
// `process.exit(1)` when it's missing. Stub the resolver so it always "finds" the package,
24+
// keeping these tests hermetic regardless of what's installed in `node_modules`.
25+
vi.mock('module', async requireActual => {
26+
const actual = await requireActual<any>();
27+
return {
28+
...actual,
29+
createRequire: () => ({ resolve: () => '@sentry/cloudflare' }),
30+
};
31+
});
32+
1333
process.env = {
1434
...process.env,
1535
SENTRY_AUTH_TOKEN: 'my-token',
@@ -23,7 +43,7 @@ const config = {
2343
} as AstroConfig;
2444

2545
const baseConfigHookObject = {
26-
logger: { warn: vi.fn(), info: vi.fn() },
46+
logger: { warn: vi.fn(), info: vi.fn(), error: vi.fn() },
2747
addMiddleware: vi.fn(),
2848
};
2949

@@ -46,7 +66,8 @@ describe('sentryAstro integration', () => {
4666
// @ts-expect-error - the hook exists and we only need to pass what we actually use
4767
await integration.hooks['astro:config:setup']({ ...baseConfigHookObject, updateConfig, injectScript, config });
4868

49-
expect(updateConfig).toHaveBeenCalledTimes(1);
69+
// one call for the sourcemaps vite plugin, one for the orchestrion plugin
70+
expect(updateConfig).toHaveBeenCalledTimes(2);
5071
expect(updateConfig).toHaveBeenCalledWith({
5172
vite: {
5273
build: {
@@ -55,6 +76,11 @@ describe('sentryAstro integration', () => {
5576
plugins: ['sentryVitePlugin'],
5677
},
5778
});
79+
expect(updateConfig).toHaveBeenCalledWith({
80+
vite: {
81+
plugins: [{ name: 'sentry-orchestrion-vite' }],
82+
},
83+
});
5884

5985
expect(sentryVitePluginSpy).toHaveBeenCalledTimes(1);
6086
expect(sentryVitePluginSpy).toHaveBeenCalledWith(
@@ -294,7 +320,13 @@ describe('sentryAstro integration', () => {
294320
// @ts-expect-error - the hook exists and we only need to pass what we actually use
295321
await integration.hooks['astro:config:setup']({ ...baseConfigHookObject, updateConfig, injectScript, config });
296322

297-
expect(updateConfig).toHaveBeenCalledTimes(0);
323+
// only the orchestrion plugin is wired, no sourcemaps plugin
324+
expect(updateConfig).toHaveBeenCalledTimes(1);
325+
expect(updateConfig).toHaveBeenCalledWith({
326+
vite: {
327+
plugins: [{ name: 'sentry-orchestrion-vite' }],
328+
},
329+
});
298330
expect(sentryVitePluginSpy).toHaveBeenCalledTimes(0);
299331
});
300332

@@ -307,7 +339,13 @@ describe('sentryAstro integration', () => {
307339
// @ts-expect-error - the hook exists and we only need to pass what we actually use
308340
await integration.hooks['astro:config:setup']({ ...baseConfigHookObject, updateConfig, injectScript, config });
309341

310-
expect(updateConfig).toHaveBeenCalledTimes(0);
342+
// only the orchestrion plugin is wired, no sourcemaps plugin
343+
expect(updateConfig).toHaveBeenCalledTimes(1);
344+
expect(updateConfig).toHaveBeenCalledWith({
345+
vite: {
346+
plugins: [{ name: 'sentry-orchestrion-vite' }],
347+
},
348+
});
311349
expect(sentryVitePluginSpy).toHaveBeenCalledTimes(0);
312350
});
313351

@@ -318,11 +356,12 @@ describe('sentryAstro integration', () => {
318356
// @ts-expect-error - the hook exists and we only need to pass what we actually use
319357
await integration.hooks['astro:config:setup']({ ...baseConfigHookObject, updateConfig, injectScript, config });
320358

321-
expect(updateConfig).toHaveBeenCalledTimes(1);
359+
// one call for the sourcemaps vite plugin, one for the orchestrion plugin
360+
expect(updateConfig).toHaveBeenCalledTimes(2);
322361
expect(sentryVitePluginSpy).toHaveBeenCalledTimes(1);
323362
});
324363

325-
it("doesn't add the Vite plugin in dev mode", async () => {
364+
it("doesn't add the sourcemaps Vite plugin in dev mode", async () => {
326365
const integration = sentryAstro({
327366
sourceMapsUploadOptions: { enabled: true },
328367
});
@@ -337,7 +376,13 @@ describe('sentryAstro integration', () => {
337376
command: 'dev',
338377
});
339378

340-
expect(updateConfig).toHaveBeenCalledTimes(0);
379+
// the sourcemaps plugin is skipped in dev, but the orchestrion plugin is still wired
380+
expect(updateConfig).toHaveBeenCalledTimes(1);
381+
expect(updateConfig).toHaveBeenCalledWith({
382+
vite: {
383+
plugins: [{ name: 'sentry-orchestrion-vite' }],
384+
},
385+
});
341386
expect(sentryVitePluginSpy).toHaveBeenCalledTimes(0);
342387
});
343388

@@ -348,12 +393,84 @@ describe('sentryAstro integration', () => {
348393

349394
expect(integration.hooks['astro:config:setup']).toBeDefined();
350395
// @ts-expect-error - the hook exists and we only need to pass what we actually use
351-
await integration.hooks['astro:config:setup']({ updateConfig, injectScript, config });
396+
await integration.hooks['astro:config:setup']({ ...baseConfigHookObject, updateConfig, injectScript, config });
352397

398+
// neither the sourcemaps nor the orchestrion plugin should be wired
353399
expect(updateConfig).toHaveBeenCalledTimes(0);
400+
expect(orchestrionVite).not.toHaveBeenCalled();
354401
expect(sentryVitePluginSpy).toHaveBeenCalledTimes(0);
355402
});
356403

404+
it('adds the orchestrion plugin by default', async () => {
405+
const integration = sentryAstro({});
406+
407+
expect(integration.hooks['astro:config:setup']).toBeDefined();
408+
// @ts-expect-error - the hook exists and we only need to pass what we actually use
409+
await integration.hooks['astro:config:setup']({ ...baseConfigHookObject, updateConfig, injectScript, config });
410+
411+
expect(orchestrionVite).toHaveBeenCalledWith({ buildTimeInstrumentation: undefined });
412+
expect(updateConfig).toHaveBeenCalledWith({
413+
vite: {
414+
plugins: [{ name: 'sentry-orchestrion-vite' }],
415+
},
416+
});
417+
});
418+
419+
it('adds an inert orchestrion plugin when `buildTimeInstrumentation` is `false`', async () => {
420+
const integration = sentryAstro({ buildTimeInstrumentation: false });
421+
422+
expect(integration.hooks['astro:config:setup']).toBeDefined();
423+
// @ts-expect-error - the hook exists and we only need to pass what we actually use
424+
await integration.hooks['astro:config:setup']({ ...baseConfigHookObject, updateConfig, injectScript, config });
425+
426+
expect(orchestrionVite).toHaveBeenCalledWith({ buildTimeInstrumentation: false });
427+
expect(updateConfig).toHaveBeenCalledWith({
428+
vite: {
429+
plugins: [{ name: 'sentry-orchestrion-disabled' }],
430+
},
431+
});
432+
});
433+
434+
it("doesn't add the orchestrion plugin for the cloudflare adapter", async () => {
435+
const integration = sentryAstro({});
436+
437+
const cloudflareConfig = { ...config, adapter: { name: '@astrojs/cloudflare' } } as AstroConfig;
438+
439+
expect(integration.hooks['astro:config:setup']).toBeDefined();
440+
// @ts-expect-error - the hook exists and we only need to pass what we actually use
441+
await integration.hooks['astro:config:setup']({
442+
...baseConfigHookObject,
443+
updateConfig,
444+
injectScript,
445+
config: cloudflareConfig,
446+
});
447+
448+
expect(orchestrionVite).not.toHaveBeenCalled();
449+
expect(updateConfig).not.toHaveBeenCalledWith({
450+
vite: {
451+
plugins: [{ name: 'sentry-orchestrion-vite' }],
452+
},
453+
});
454+
});
455+
456+
it("doesn't warn about deprecated options when `buildTimeInstrumentation` is set", async () => {
457+
const integration = sentryAstro({ buildTimeInstrumentation: false });
458+
459+
const logger = { warn: vi.fn(), info: vi.fn() };
460+
461+
expect(integration.hooks['astro:config:setup']).toBeDefined();
462+
// @ts-expect-error - the hook exists and we only need to pass what we actually use
463+
await integration.hooks['astro:config:setup']({
464+
...baseConfigHookObject,
465+
updateConfig,
466+
injectScript,
467+
config,
468+
logger,
469+
});
470+
471+
expect(logger.warn).not.toHaveBeenCalledWith(expect.stringContaining('buildTimeInstrumentation'));
472+
});
473+
357474
it.each([{}, { enabled: true }])('injects client and server init scripts', async options => {
358475
const integration = sentryAstro(options);
359476

0 commit comments

Comments
 (0)