Skip to content

Commit 65860d4

Browse files
committed
fix(nextjs): Handle Spotlight auto-enablement in Next.js client SDK
The browser SDK's Spotlight auto-detection is wrapped in rollup-include-development-only, which strips it from production builds. Since users install production builds from npm, the Next.js SDK needs to handle Spotlight auto-enablement explicitly. This moves the Spotlight env var detection from the browser SDK to the Next.js client SDK, which reads the injected NEXT_PUBLIC_SENTRY_SPOTLIGHT value from globalThis and adds the Spotlight integration when appropriate. This also avoids the issue where CI sets VITE_SENTRY_SPOTLIGHT=true globally, which was accidentally enabling Spotlight in all test apps and potentially causing timing issues.
1 parent 9a9ba91 commit 65860d4

File tree

2 files changed

+35
-12
lines changed

2 files changed

+35
-12
lines changed

packages/browser/src/sdk.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,8 @@ export function init(options: BrowserOptions = {}): Client | undefined {
9696
let defaultIntegrations =
9797
options.defaultIntegrations == null ? getDefaultIntegrations(options) : options.defaultIntegrations;
9898

99-
// Resolve Spotlight configuration with proper precedence.
100-
// This code is intentionally NOT wrapped in rollup-include-development-only because:
101-
// - Users install production builds of the SDK from npm
102-
// - They expect auto-enablement to work during their app's development
103-
// - The env var (NEXT_PUBLIC_SENTRY_SPOTLIGHT, etc.) won't be set in production deployments
104-
// - Users can explicitly disable with `spotlight: false` if needed
99+
/* rollup-include-development-only */
100+
// Resolve Spotlight configuration with proper precedence
105101
const envSpotlight = getSpotlightConfig();
106102
// resolveSpotlightOptions is the single source of truth that ensures empty strings are never used
107103
const spotlightValue = resolveSpotlightOptions(options.spotlight, envSpotlight);
@@ -113,6 +109,7 @@ export function init(options: BrowserOptions = {}): Client | undefined {
113109
const args = typeof spotlightValue === 'string' ? { sidecarUrl: spotlightValue } : undefined;
114110
defaultIntegrations.push(spotlightBrowserIntegration(args));
115111
}
112+
/* rollup-include-development-only-end */
116113

117114
const clientOptions: BrowserClientOptions = {
118115
...options,

packages/nextjs/src/client/index.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,21 @@
22
// can be removed once following issue is fixed: https://github.com/import-js/eslint-plugin-import/issues/703
33
/* eslint-disable import/export */
44
import type { Client, EventProcessor, Integration } from '@sentry/core';
5-
import { addEventProcessor, applySdkMetadata, consoleSandbox, getGlobalScope, GLOBAL_OBJ } from '@sentry/core';
5+
import {
6+
addEventProcessor,
7+
applySdkMetadata,
8+
consoleSandbox,
9+
envToBool,
10+
getGlobalScope,
11+
GLOBAL_OBJ,
12+
resolveSpotlightOptions,
13+
} from '@sentry/core';
614
import type { BrowserOptions } from '@sentry/react';
7-
import { getDefaultIntegrations as getReactDefaultIntegrations, init as reactInit } from '@sentry/react';
15+
import {
16+
getDefaultIntegrations as getReactDefaultIntegrations,
17+
init as reactInit,
18+
spotlightBrowserIntegration,
19+
} from '@sentry/react';
820
import { devErrorSymbolicationEventProcessor } from '../common/devErrorSymbolicationEventProcessor';
921
import { getVercelEnv } from '../common/getVercelEnv';
1022
import { isRedirectNavigationError } from '../common/nextNavigationErrorUtils';
@@ -31,6 +43,7 @@ const globalWithInjectedValues = GLOBAL_OBJ as typeof GLOBAL_OBJ & {
3143
_sentryBasePath?: string;
3244
_sentryRelease?: string;
3345
_experimentalThirdPartyOriginStackFrames?: string;
46+
NEXT_PUBLIC_SENTRY_SPOTLIGHT?: string;
3447
};
3548

3649
// Treeshakable guard to remove all code related to tracing
@@ -130,11 +143,24 @@ function getDefaultIntegrations(options: BrowserOptions): Integration[] {
130143
}),
131144
);
132145

133-
// Note: Spotlight auto-enablement from NEXT_PUBLIC_SENTRY_SPOTLIGHT is handled by the
134-
// browser SDK's init() function. The Next.js build config injects the env var value via:
135-
// - Webpack: DefinePlugin replaces process.env.NEXT_PUBLIC_SENTRY_SPOTLIGHT
146+
// Add Spotlight integration if enabled via NEXT_PUBLIC_SENTRY_SPOTLIGHT env var.
147+
// The env var is injected by the Next.js build config:
148+
// - Webpack: valueInjectionLoader sets globalThis.NEXT_PUBLIC_SENTRY_SPOTLIGHT
136149
// - Turbopack: valueInjectionLoader sets globalThis.NEXT_PUBLIC_SENTRY_SPOTLIGHT
137-
// The browser SDK's getEnvValue() checks both sources.
150+
// We handle this in the Next.js SDK rather than the browser SDK because the browser SDK's
151+
// auto-detection is development-only (stripped from production builds that users install).
152+
const spotlightEnvValue = globalWithInjectedValues.NEXT_PUBLIC_SENTRY_SPOTLIGHT;
153+
if (spotlightEnvValue !== undefined) {
154+
// Parse the env var value (could be 'true', 'false', or a URL)
155+
const boolValue = envToBool(spotlightEnvValue, { strict: true });
156+
const envSpotlight = boolValue !== null ? boolValue : spotlightEnvValue;
157+
const spotlightValue = resolveSpotlightOptions(options.spotlight, envSpotlight);
158+
159+
if (spotlightValue) {
160+
const args = typeof spotlightValue === 'string' ? { sidecarUrl: spotlightValue } : undefined;
161+
customDefaultIntegrations.push(spotlightBrowserIntegration(args));
162+
}
163+
}
138164

139165
return customDefaultIntegrations;
140166
}

0 commit comments

Comments
 (0)