From b6af90009947ce78a941566e57b6fd617f5e36e4 Mon Sep 17 00:00:00 2001 From: Ivan Pesic Date: Fri, 23 May 2025 13:29:53 +0200 Subject: [PATCH 1/2] feat(nuxt): Added support for nuxt layers (#14345) --- packages/nuxt/src/module.ts | 4 +- packages/nuxt/src/vite/utils.ts | 36 ++++++++++--- packages/nuxt/test/vite/utils.test.ts | 73 +++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 10 deletions(-) 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..d88e1bbd7337 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..f1702ed28eb4 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,8 +70,80 @@ 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', () => { it('should return the filename from a simple path', () => { const path = 'node ./server/index.mjs'; From b99bea16df5c381640fc51b572fd25c3c0a63241 Mon Sep 17 00:00:00 2001 From: Ivan Pesic Date: Fri, 23 May 2025 15:55:48 +0200 Subject: [PATCH 2/2] Fix prettier --- packages/nuxt/src/vite/utils.ts | 2 +- packages/nuxt/test/vite/utils.test.ts | 77 +++++++++++++-------------- 2 files changed, 38 insertions(+), 41 deletions(-) diff --git a/packages/nuxt/src/vite/utils.ts b/packages/nuxt/src/vite/utils.ts index d88e1bbd7337..f1ef1c9e4cf2 100644 --- a/packages/nuxt/src/vite/utils.ts +++ b/packages/nuxt/src/vite/utils.ts @@ -23,7 +23,7 @@ export function findDefaultSdkInitFile(type: 'server' | 'client', nuxt?: Nuxt): } // Get layers from highest priority to lowest - const layers = [...nuxt?.options._layers ?? []].reverse(); + const layers = [...(nuxt?.options._layers ?? [])].reverse(); for (const layer of layers) { for (const relativePath of relativePaths) { diff --git a/packages/nuxt/test/vite/utils.test.ts b/packages/nuxt/test/vite/utils.test.ts index f1702ed28eb4..f19ec98b4b64 100644 --- a/packages/nuxt/test/vite/utils.test.ts +++ b/packages/nuxt/test/vite/utils.test.ts @@ -72,23 +72,22 @@ describe('findDefaultSdkInitFile', () => { }); 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 + 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'); @@ -96,7 +95,7 @@ describe('findDefaultSdkInitFile', () => { it('should return the latest layer config file path if server config exists', () => { vi.spyOn(fs, 'existsSync').mockImplementation(filePath => { - return ( + return ( !(filePath instanceof URL) && (filePath.includes('sentry.server.config.ts') || filePath.includes('instrument.server.ts')) ); @@ -106,44 +105,42 @@ describe('findDefaultSdkInitFile', () => { options: { _layers: [ { - cwd: 'packages/nuxt/module' + cwd: 'packages/nuxt/module', }, { - cwd: 'packages/nuxt' - } - ] - } - } as Nuxt + 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 + 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', () => { it('should return the filename from a simple path', () => { const path = 'node ./server/index.mjs';