diff --git a/packages/nuxt/src/module.ts b/packages/nuxt/src/module.ts index a7e81a49d045..377508e860a2 100644 --- a/packages/nuxt/src/module.ts +++ b/packages/nuxt/src/module.ts @@ -35,7 +35,7 @@ export default defineNuxtModule({ const moduleDirResolver = createResolver(import.meta.url); const buildDirResolver = createResolver(nuxt.options.buildDir); - const clientConfigFile = findDefaultSdkInitFile('client'); + const clientConfigFile = findDefaultSdkInitFile('client', nuxt); if (clientConfigFile) { // Inject the client-side Sentry config file with a side effect import @@ -59,7 +59,7 @@ export default defineNuxtModule({ addPlugin({ src: moduleDirResolver.resolve('./runtime/plugins/sentry.client'), mode: 'client' }); } - const serverConfigFile = findDefaultSdkInitFile('server'); + const serverConfigFile = findDefaultSdkInitFile('server', nuxt); if (serverConfigFile) { addServerPlugin(moduleDirResolver.resolve('./runtime/plugins/sentry.server')); diff --git a/packages/nuxt/src/vite/utils.ts b/packages/nuxt/src/vite/utils.ts index ea2db2bc21b8..f1ef1c9e4cf2 100644 --- a/packages/nuxt/src/vite/utils.ts +++ b/packages/nuxt/src/vite/utils.ts @@ -1,29 +1,49 @@ import { consoleSandbox } from '@sentry/core'; import * as fs from 'fs'; +import type { Nuxt } from 'nuxt/schema'; import * as path from 'path'; /** * Find the default SDK init file for the given type (client or server). * The sentry.server.config file is prioritized over the instrument.server file. */ -export function findDefaultSdkInitFile(type: 'server' | 'client'): string | undefined { +export function findDefaultSdkInitFile(type: 'server' | 'client', nuxt?: Nuxt): string | undefined { const possibleFileExtensions = ['ts', 'js', 'mjs', 'cjs', 'mts', 'cts']; - const cwd = process.cwd(); + const relativePaths: string[] = []; - const filePaths: string[] = []; if (type === 'server') { for (const ext of possibleFileExtensions) { - // order is important here - we want to prioritize the server.config file - filePaths.push(path.join(cwd, `sentry.${type}.config.${ext}`)); - filePaths.push(path.join(cwd, 'public', `instrument.${type}.${ext}`)); + relativePaths.push(`sentry.${type}.config.${ext}`); + relativePaths.push(path.join('public', `instrument.${type}.${ext}`)); } } else { for (const ext of possibleFileExtensions) { - filePaths.push(path.join(cwd, `sentry.${type}.config.${ext}`)); + relativePaths.push(`sentry.${type}.config.${ext}`); + } + } + + // Get layers from highest priority to lowest + const layers = [...(nuxt?.options._layers ?? [])].reverse(); + + for (const layer of layers) { + for (const relativePath of relativePaths) { + const fullPath = path.resolve(layer.cwd, relativePath); + if (fs.existsSync(fullPath)) { + return fullPath; + } + } + } + + // As a fallback, also check CWD (left for pure compatibility) + const cwd = process.cwd(); + for (const relativePath of relativePaths) { + const fullPath = path.resolve(cwd, relativePath); + if (fs.existsSync(fullPath)) { + return fullPath; } } - return filePaths.find(filename => fs.existsSync(filename)); + return undefined; } /** diff --git a/packages/nuxt/test/vite/utils.test.ts b/packages/nuxt/test/vite/utils.test.ts index 7ffd7654549e..f19ec98b4b64 100644 --- a/packages/nuxt/test/vite/utils.test.ts +++ b/packages/nuxt/test/vite/utils.test.ts @@ -1,4 +1,5 @@ import * as fs from 'fs'; +import type { Nuxt } from 'nuxt/schema'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { constructFunctionReExport, @@ -69,6 +70,75 @@ describe('findDefaultSdkInitFile', () => { const result = findDefaultSdkInitFile('server'); expect(result).toMatch('packages/nuxt/sentry.server.config.js'); }); + + it('should return the latest layer config file path if client config exists', () => { + vi.spyOn(fs, 'existsSync').mockImplementation(filePath => { + return !(filePath instanceof URL) && filePath.includes('sentry.client.config.ts'); + }); + + const nuxtMock = { + options: { + _layers: [ + { + cwd: 'packages/nuxt/module', + }, + { + cwd: 'packages/nuxt', + }, + ], + }, + } as Nuxt; + + const result = findDefaultSdkInitFile('client', nuxtMock); + expect(result).toMatch('packages/nuxt/sentry.client.config.ts'); + }); + + it('should return the latest layer config file path if server config exists', () => { + vi.spyOn(fs, 'existsSync').mockImplementation(filePath => { + return ( + !(filePath instanceof URL) && + (filePath.includes('sentry.server.config.ts') || filePath.includes('instrument.server.ts')) + ); + }); + + const nuxtMock = { + options: { + _layers: [ + { + cwd: 'packages/nuxt/module', + }, + { + cwd: 'packages/nuxt', + }, + ], + }, + } as Nuxt; + + const result = findDefaultSdkInitFile('server', nuxtMock); + expect(result).toMatch('packages/nuxt/sentry.server.config.ts'); + }); + + it('should return the latest layer config file path if client config exists in former layer', () => { + vi.spyOn(fs, 'existsSync').mockImplementation(filePath => { + return !(filePath instanceof URL) && filePath.includes('nuxt/sentry.client.config.ts'); + }); + + const nuxtMock = { + options: { + _layers: [ + { + cwd: 'packages/nuxt/module', + }, + { + cwd: 'packages/nuxt', + }, + ], + }, + } as Nuxt; + + const result = findDefaultSdkInitFile('client', nuxtMock); + expect(result).toMatch('packages/nuxt/sentry.client.config.ts'); + }); }); describe('getFilenameFromPath', () => {