Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/nuxt/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export default defineNuxtModule<ModuleOptions>({

if (serverConfigFile) {
if (!usesDeprecatedInjectMode) {
addServerConfigPlugin(nuxt, serverConfigFile);
addServerConfigPlugin(nuxt, serverConfigFile, !isNitroV3);
}

if (isNitroV3) {
Expand Down
28 changes: 17 additions & 11 deletions packages/nuxt/src/vite/addServerConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export function addSentryTopImport(moduleOptions: SentryNuxtModuleOptions, nitro
* Registers a Nitro plugin that statically imports the Sentry server config, so the SDK initializes
* at server startup without a `node --import` preload.
*/
export function addServerConfigPlugin(nuxt: Nuxt, serverConfigFile: string): void {
export function addServerConfigPlugin(nuxt: Nuxt, serverConfigFile: string, isLegacyNitro: boolean): void {
const configPath = createResolver(nuxt.options.rootDir).resolve(serverConfigFile);

// `Sentry.init` reads these flags, and a statement above the config import would not survive
Expand All @@ -134,13 +134,16 @@ export function addServerConfigPlugin(nuxt: Nuxt, serverConfigFile: string): voi

addServerPlugin(configPluginTemplate.dst);

// Nitro treeshakes side-effect-only imports outside its runtime dir, which would silently drop
// Nitro v2 treeshakes side-effect-only imports outside its runtime dir, which would silently drop
// the top-level `Sentry.init` and the flag assignments.
nuxt.options.nitro.moduleSideEffects = [
...(nuxt.options.nitro.moduleSideEffects ?? []),
configPath,
runtimeFlagsTemplate.dst,
];
// Nitro v3 dropped the option and keeps both.
if (isLegacyNitro) {
nuxt.options.nitro.moduleSideEffects = [
...(nuxt.options.nitro.moduleSideEffects ?? []),
configPath,
runtimeFlagsTemplate.dst,
];
}

nuxt.hook('nitro:config', nitroConfig => {
Comment thread
sentry[bot] marked this conversation as resolved.
// Early skip for explicitly configured Cloudflare presets. The authoritative check runs on
Expand All @@ -157,10 +160,13 @@ export function addServerConfigPlugin(nuxt: Nuxt, serverConfigFile: string): voi

// The Nitro v2 dev bundle would otherwise externalize these files, making Node load the raw
// `.ts` config — which needs type stripping (Node >= 22.18). Inlining keeps them transpiled.
const externals = (nitroConfig.externals ??= {});
const inline = externals.inline;
const existingInline = Array.isArray(inline) ? inline : inline ? [inline] : [];
externals.inline = [...existingInline, configPath, configPluginTemplate.dst, runtimeFlagsTemplate.dst];
// Nitro v3 has no `externals` option; its dev server runs the config through Vite's transform.
if (isLegacyNitro) {
const externals = (nitroConfig.externals ??= {});
const inline = externals.inline;
const existingInline = Array.isArray(inline) ? inline : inline ? [inline] : [];
externals.inline = [...existingInline, configPath, configPluginTemplate.dst, runtimeFlagsTemplate.dst];
}
});

// On Cloudflare the SDK is set up through `sentryCloudflareNitroPlugin`; the Node SDK config
Expand Down
37 changes: 29 additions & 8 deletions packages/nuxt/test/vite/addServerConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ describe('addServerConfigPlugin', () => {
it('registers a plugin that evaluates the runtime flags before the config', () => {
const { nuxt } = createFakeNuxt();

addServerConfigPlugin(nuxt, APP_CONFIG);
addServerConfigPlugin(nuxt, APP_CONFIG, true);

expect(templateContents('sentry-server-config-plugin.mjs')).toBe(
`import ${JSON.stringify(flagsDst)};\nimport ${JSON.stringify(APP_CONFIG)};\nexport default () => {};\n`,
Expand All @@ -173,7 +173,7 @@ describe('addServerConfigPlugin', () => {
it('derives the runtime flags from the build-time `import.meta` values', () => {
const { nuxt } = createFakeNuxt();

addServerConfigPlugin(nuxt, APP_CONFIG);
addServerConfigPlugin(nuxt, APP_CONFIG, true);

const contents = templateContents('sentry-runtime-flags.mjs');
expect(contents).toContain('globalThis.__SENTRY_NUXT_DEV_MODE__ = import.meta.dev === true;');
Expand All @@ -184,24 +184,45 @@ describe('addServerConfigPlugin', () => {
const { nuxt } = createFakeNuxt();
nuxt.options.nitro.moduleSideEffects = ['unenv/polyfill/'];

addServerConfigPlugin(nuxt, APP_CONFIG);
addServerConfigPlugin(nuxt, APP_CONFIG, true);

expect(nuxt.options.nitro.moduleSideEffects).toEqual(['unenv/polyfill/', APP_CONFIG, flagsDst]);
});

it('leaves `moduleSideEffects` alone on Nitro v3, which no longer supports the option', () => {
const { nuxt } = createFakeNuxt();
nuxt.options.nitro.moduleSideEffects = ['unenv/polyfill/'];

addServerConfigPlugin(nuxt, APP_CONFIG, false);

expect(nuxt.options.nitro.moduleSideEffects).toEqual(['unenv/polyfill/']);
});

it('inlines the config and both templates so the dev bundle transpiles them', () => {
const { nuxt, hooks } = createFakeNuxt();
addServerConfigPlugin(nuxt, APP_CONFIG);
addServerConfigPlugin(nuxt, APP_CONFIG, true);
const nitroConfig: NitroConfig = { externals: { inline: ['@sentry/'] } };

hooks['nitro:config']!(nitroConfig);

expect(nitroConfig.externals?.inline).toEqual(['@sentry/', APP_CONFIG, pluginDst, flagsDst]);
});

it('leaves `externals` alone on Nitro v3, which has no such option', () => {
const { nuxt, hooks } = createFakeNuxt();
addServerConfigPlugin(nuxt, APP_CONFIG, false);
const nitroConfig: NitroConfig = { plugins: ['other-module-plugin.mjs'] };

hooks['nitro:config']!(nitroConfig);

expect(nitroConfig.externals).toBeUndefined();
// Plugin ordering is not Nitro-version specific and still applies.
expect(nitroConfig.plugins).toEqual([pluginDst, 'other-module-plugin.mjs']);
});

it('moves its plugin to the front when other modules registered plugins first', () => {
const { nuxt, hooks } = createFakeNuxt();
addServerConfigPlugin(nuxt, APP_CONFIG);
addServerConfigPlugin(nuxt, APP_CONFIG, true);
const nitroConfig: NitroConfig = { plugins: ['other-module-plugin.mjs', pluginDst] };

hooks['nitro:config']!(nitroConfig);
Expand All @@ -211,7 +232,7 @@ describe('addServerConfigPlugin', () => {

it('removes the plugin for explicitly configured Cloudflare presets', () => {
const { nuxt, hooks } = createFakeNuxt();
addServerConfigPlugin(nuxt, APP_CONFIG);
addServerConfigPlugin(nuxt, APP_CONFIG, true);
const nitroConfig: NitroConfig = { preset: 'cloudflare_module', plugins: ['other-plugin.mjs', pluginDst] };

hooks['nitro:config']!(nitroConfig);
Expand All @@ -225,7 +246,7 @@ describe('addServerConfigPlugin', () => {
// after `nitro:config` ran, so the authoritative check uses `nitro:init`.
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const { nuxt, hooks } = createFakeNuxt();
addServerConfigPlugin(nuxt, APP_CONFIG);
addServerConfigPlugin(nuxt, APP_CONFIG, true);
const nitro = { options: { preset: 'cloudflare_pages', plugins: ['other-plugin.mjs', pluginDst] } };

hooks['nitro:init']!(nitro as never);
Expand All @@ -237,7 +258,7 @@ describe('addServerConfigPlugin', () => {

it('keeps the plugin when the resolved preset is not Cloudflare', () => {
const { nuxt, hooks } = createFakeNuxt();
addServerConfigPlugin(nuxt, APP_CONFIG);
addServerConfigPlugin(nuxt, APP_CONFIG, true);
const nitro = { options: { preset: 'node-server', plugins: ['other-plugin.mjs', pluginDst] } };

hooks['nitro:init']!(nitro as never);
Expand Down
Loading