From 8b1b2ec4aad7f0544771d00e68d0b686bca78db9 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Mon, 4 Nov 2024 16:51:51 +0100 Subject: [PATCH 01/18] feat(nitro-utils): Export Rollup Plugin `wrapServerEntryWithDynamicImport` --- package.json | 1 + packages/nitro-utils/.eslintrc.js | 21 ++ packages/nitro-utils/LICENSE | 21 ++ packages/nitro-utils/README.md | 23 ++ packages/nitro-utils/package.json | 71 ++++++ packages/nitro-utils/rollup.npm.config.mjs | 17 ++ packages/nitro-utils/src/index.ts | 1 + .../wrapServerEntryWithDynamicImport.ts | 219 ++++++++++++++++++ .../wrapServerEntryWithDynamicImport.test.ts | 193 +++++++++++++++ packages/nitro-utils/test/tsconfig.json | 3 + packages/nitro-utils/test/vitest.setup.ts | 8 + packages/nitro-utils/tsconfig.json | 9 + packages/nitro-utils/tsconfig.test.json | 10 + packages/nitro-utils/tsconfig.types.json | 10 + packages/nitro-utils/vite.config.ts | 9 + packages/nuxt/package.json | 1 + packages/nuxt/src/vite/addServerConfig.ts | 99 +------- packages/nuxt/src/vite/utils.ts | 130 ----------- packages/nuxt/test/vite/utils.test.ts | 194 +--------------- 19 files changed, 624 insertions(+), 416 deletions(-) create mode 100644 packages/nitro-utils/.eslintrc.js create mode 100644 packages/nitro-utils/LICENSE create mode 100644 packages/nitro-utils/README.md create mode 100644 packages/nitro-utils/package.json create mode 100644 packages/nitro-utils/rollup.npm.config.mjs create mode 100644 packages/nitro-utils/src/index.ts create mode 100644 packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts create mode 100644 packages/nitro-utils/test/rollupPlugins/wrapServerEntryWithDynamicImport.test.ts create mode 100644 packages/nitro-utils/test/tsconfig.json create mode 100644 packages/nitro-utils/test/vitest.setup.ts create mode 100644 packages/nitro-utils/tsconfig.json create mode 100644 packages/nitro-utils/tsconfig.test.json create mode 100644 packages/nitro-utils/tsconfig.types.json create mode 100644 packages/nitro-utils/vite.config.ts diff --git a/package.json b/package.json index 39139d5faba0..88f3725bf29f 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "packages/integration-shims", "packages/nestjs", "packages/nextjs", + "packages/nitro-utils", "packages/node", "packages/nuxt", "packages/opentelemetry", diff --git a/packages/nitro-utils/.eslintrc.js b/packages/nitro-utils/.eslintrc.js new file mode 100644 index 000000000000..3849c1ee28a6 --- /dev/null +++ b/packages/nitro-utils/.eslintrc.js @@ -0,0 +1,21 @@ +module.exports = { + extends: ['../../.eslintrc.js'], + env: { + node: true, + }, + overrides: [ + { + files: ['src/**'], + rules: { + '@sentry-internal/sdk/no-optional-chaining': 'off', + }, + }, + { + files: ['src/metrics/**'], + rules: { + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + }, + }, + ], +}; diff --git a/packages/nitro-utils/LICENSE b/packages/nitro-utils/LICENSE new file mode 100644 index 000000000000..5af93a5bdae5 --- /dev/null +++ b/packages/nitro-utils/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2024 Functional Software, Inc. dba Sentry + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/nitro-utils/README.md b/packages/nitro-utils/README.md new file mode 100644 index 000000000000..304aa0961836 --- /dev/null +++ b/packages/nitro-utils/README.md @@ -0,0 +1,23 @@ +

+ + Sentry + +

+ +# Sentry Utilities for Nitro-based SDKs + +[![npm version](https://img.shields.io/npm/v/@sentry-internal/nitro-utils.svg)](https://www.npmjs.com/package/@sentry-internal/nitro-utils) +[![npm dm](https://img.shields.io/npm/dm/@sentry-internal/nitro-utils.svg)](https://www.npmjs.com/package/@sentry-internal/nitro-utils) +[![npm dt](https://img.shields.io/npm/dt/@sentry-internal/nitro-utils.svg)](https://www.npmjs.com/package/@sentry-internal/nitro-utils) + +## Links + +- [Official SDK Docs](https://docs.sentry.io/quickstart/) +- [TypeDoc](http://getsentry.github.io/sentry-node/) + +## General + +Common utilities used by Sentry SDKs that use Nitro on the server-side. + +Note: This package is only meant to be used internally, and as such is not part of our public API contract and does not +follow semver. diff --git a/packages/nitro-utils/package.json b/packages/nitro-utils/package.json new file mode 100644 index 000000000000..d6bed1ea224e --- /dev/null +++ b/packages/nitro-utils/package.json @@ -0,0 +1,71 @@ +{ + "name": "@sentry-internal/nitro-utils", + "version": "8.36.0", + "description": "Utilities for all Sentry SDKs with Nitro on the server-side", + "repository": "git://github.com/getsentry/sentry-javascript.git", + "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nitro-utils", + "author": "Sentry", + "license": "MIT", + "private": true, + "engines": { + "node": ">=14.18" + }, + "files": [ + "/build" + ], + "main": "build/cjs/index.js", + "module": "build/esm/index.js", + "types": "build/types/index.d.ts", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./build/types/index.d.ts", + "default": "./build/esm/index.js" + }, + "require": { + "types": "./build/types/index.d.ts", + "default": "./build/cjs/index.js" + } + } + }, + "typesVersions": { + "<4.9": { + "build/types/index.d.ts": [ + "build/types-ts3.8/index.d.ts" + ] + } + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@sentry/core": "8.36.0", + "@sentry/types": "8.36.0", + "@sentry/utils": "8.36.0" + }, + "scripts": { + "build": "run-p build:transpile build:types", + "build:dev": "yarn build", + "build:transpile": "rollup -c rollup.npm.config.mjs", + "build:types": "run-s build:types:core build:types:downlevel", + "build:types:core": "tsc -p tsconfig.types.json", + "build:types:downlevel": "yarn downlevel-dts build/types build/types-ts3.8 --to ts3.8", + "build:watch": "run-p build:transpile:watch build:types:watch", + "build:dev:watch": "run-p build:transpile:watch build:types:watch", + "build:transpile:watch": "rollup -c rollup.npm.config.mjs --watch", + "build:types:watch": "tsc -p tsconfig.types.json --watch", + "build:tarball": "npm pack", + "clean": "rimraf build coverage sentry-internal-nitro-utils-*.tgz", + "fix": "eslint . --format stylish --fix", + "lint": "eslint . --format stylish", + "test": "yarn test:unit", + "test:unit": "vitest run", + "test:watch": "vitest --watch", + "yalc:publish": "yalc publish --push --sig" + }, + "volta": { + "extends": "../../package.json" + }, + "sideEffects": false +} diff --git a/packages/nitro-utils/rollup.npm.config.mjs b/packages/nitro-utils/rollup.npm.config.mjs new file mode 100644 index 000000000000..d28a7a6f54a0 --- /dev/null +++ b/packages/nitro-utils/rollup.npm.config.mjs @@ -0,0 +1,17 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollup-utils'; + +export default makeNPMConfigVariants( + makeBaseNPMConfig({ + packageSpecificConfig: { + output: { + // set exports to 'named' or 'auto' so that rollup doesn't warn + exports: 'named', + // set preserveModules to true because we don't want to bundle everything into one file. + preserveModules: + process.env.SENTRY_BUILD_PRESERVE_MODULES === undefined + ? true + : Boolean(process.env.SENTRY_BUILD_PRESERVE_MODULES), + }, + }, + }), +); diff --git a/packages/nitro-utils/src/index.ts b/packages/nitro-utils/src/index.ts new file mode 100644 index 000000000000..ae57db463f33 --- /dev/null +++ b/packages/nitro-utils/src/index.ts @@ -0,0 +1 @@ +export { wrapServerEntryWithDynamicImport } from './rollupPlugins/wrapServerEntryWithDynamicImport'; diff --git a/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts b/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts new file mode 100644 index 000000000000..cf855fc0b68e --- /dev/null +++ b/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts @@ -0,0 +1,219 @@ +import { consoleSandbox, flatten } from '@sentry/utils'; +import type { InputPluginOption } from 'rollup'; + +export const SENTRY_WRAPPED_ENTRY = '?sentry-query-wrapped-entry'; +export const SENTRY_WRAPPED_FUNCTIONS = '?sentry-query-wrapped-functions='; +export const SENTRY_REEXPORTED_FUNCTIONS = '?sentry-query-reexported-functions='; +export const QUERY_END_INDICATOR = 'SENTRY-QUERY-END'; + +/** + * A Rollup plugin which wraps the server entry with a dynamic `import()`. This makes it possible to initialize Sentry first + * by using a regular `import` and load the server after that. + * This also works with serverless `handler` functions, as it re-exports the `handler`. + * + * @param config Configuration options for the Rollup Plugin + * @param config.serverConfigFileName Name of the Sentry server config (without file extension). E.g. 'sentry.server.config' + * @param config.resolvedServerConfigPath Resolved path of the Sentry server config (based on `src` directory) + * @param config.entryPointWrappedFunctions Exported bindings of the server entry file, which are wrapped as async function. E.g. ['default', 'handler', 'server'] + * @param config.additionalImports Adds additional imports to the entry file. Can be e.g. 'import-in-the-middle/hook.mjs' + * @param config.debug Whether debug logs are enabled in the build time environment + */ +export function wrapServerEntryWithDynamicImport(config: { + serverConfigFileName: string; + resolvedServerConfigPath: string; + entrypointWrappedFunctions: string[]; + additionalImports?: string[]; + debug?: boolean; +}): InputPluginOption { + const { serverConfigFileName, resolvedServerConfigPath, entrypointWrappedFunctions, additionalImports, debug } = + config; + + return { + name: 'sentry-wrap-server-entry-with-dynamic-import', + async resolveId(source, importer, options) { + if (source.includes(`/${serverConfigFileName}`)) { + return { id: source, moduleSideEffects: true }; + } + + if (additionalImports && additionalImports.includes(source)) { + // When importing additional imports like "import-in-the-middle/hook.mjs" in the returned code of the `load()` function below: + // By setting `moduleSideEffects` to `true`, the import is added to the bundle, although nothing is imported from it + // By importing "import-in-the-middle/hook.mjs", we can make sure this file is included, as not all node builders are including files imported with `module.register()`. + // Prevents the error "Failed to register ESM hook Error: Cannot find module 'import-in-the-middle/hook.mjs'" + return { id: source, moduleSideEffects: true, external: true }; + } + + if (options.isEntry && source.includes('.mjs') && !source.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`)) { + const resolution = await this.resolve(source, importer, options); + + // If it cannot be resolved or is external, just return it so that Rollup can display an error + if (!resolution || (resolution && resolution.external)) return resolution; + + const moduleInfo = await this.load(resolution); + + moduleInfo.moduleSideEffects = true; + + // The enclosing `if` already checks for the suffix in `source`, but a check in `resolution.id` is needed as well to prevent multiple attachment of the suffix + return resolution.id.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`) + ? resolution.id + : resolution.id + // Concatenates the query params to mark the file (also attaches names of re-exports - this is needed for serverless functions to re-export the handler) + .concat(SENTRY_WRAPPED_ENTRY) + .concat( + constructWrappedFunctionExportQuery(moduleInfo.exportedBindings, entrypointWrappedFunctions, debug), + ) + .concat(QUERY_END_INDICATOR); + } + return null; + }, + load(id: string) { + if (id.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`)) { + const entryId = removeSentryQueryFromPath(id); + + // Mostly useful for serverless `handler` functions + const reExportedFunctions = + id.includes(SENTRY_WRAPPED_FUNCTIONS) || id.includes(SENTRY_REEXPORTED_FUNCTIONS) + ? constructFunctionReExport(id, entryId) + : ''; + + return ( + // Regular `import` of the Sentry config + `import ${JSON.stringify(resolvedServerConfigPath)};\n` + + // Dynamic `import()` for the previous, actual entry point. + // `import()` can be used for any code that should be run after the hooks are registered (https://nodejs.org/api/module.html#enabling) + `import(${JSON.stringify(entryId)});\n` + + // By importing additional imports like "import-in-the-middle/hook.mjs", we can make sure this file wil be included, as not all node builders are including files imported with `module.register()`. + `${additionalImports ? additionalImports.map(importPath => `import "${importPath}";\n`) : ''}` + + `${reExportedFunctions}\n` + ); + } + + return null; + }, + }; +} + +/** + * Strips the Sentry query part from a path. + * Example: example/path?sentry-query-wrapped-entry?sentry-query-functions-reexport=foo,SENTRY-QUERY-END -> /example/path + * + * **Only exported for testing** + */ +export function removeSentryQueryFromPath(url: string): string { + // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor + const regex = new RegExp(`\\${SENTRY_WRAPPED_ENTRY}.*?\\${QUERY_END_INDICATOR}`); + return url.replace(regex, ''); +} + +/** + * Extracts and sanitizes function re-export and function wrap query parameters from a query string. + * If it is a default export, it is not considered for re-exporting. + * + * **Only exported for testing** + */ +export function extractFunctionReexportQueryParameters(query: string): { wrap: string[]; reexport: string[] } { + // Regex matches the comma-separated params between the functions query + // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor + const wrapRegex = new RegExp( + `\\${SENTRY_WRAPPED_FUNCTIONS}(.*?)(\\${QUERY_END_INDICATOR}|\\${SENTRY_REEXPORTED_FUNCTIONS})`, + ); + // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor + const reexportRegex = new RegExp(`\\${SENTRY_REEXPORTED_FUNCTIONS}(.*?)(\\${QUERY_END_INDICATOR})`); + + const wrapMatch = query.match(wrapRegex); + const reexportMatch = query.match(reexportRegex); + + const wrap = + wrapMatch && wrapMatch[1] + ? wrapMatch[1] + .split(',') + .filter(param => param !== '') + // Sanitize, as code could be injected with another rollup plugin + .map((str: string) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) + : []; + + const reexport = + reexportMatch && reexportMatch[1] + ? reexportMatch[1] + .split(',') + .filter(param => param !== '' && param !== 'default') + // Sanitize, as code could be injected with another rollup plugin + .map((str: string) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) + : []; + + return { wrap, reexport }; +} + +/** + * Constructs a comma-separated string with all functions that need to be re-exported later from the server entry. + * It uses Rollup's `exportedBindings` to determine the functions to re-export. Functions which should be wrapped + * (e.g. serverless handlers) are wrapped by Sentry. + * + * **Only exported for testing** + */ +export function constructWrappedFunctionExportQuery( + exportedBindings: Record | null, + entrypointWrappedFunctions: string[], + debug?: boolean, +): string { + // `exportedBindings` can look like this: `{ '.': [ 'handler' ] }` or `{ '.': [], './firebase-gen-1.mjs': [ 'server' ] }` + // The key `.` refers to exports within the current file, while other keys show from where exports were imported first. + const functionsToExport = flatten(Object.values(exportedBindings || {})).reduce( + (functions, currFunctionName) => { + if (entrypointWrappedFunctions.includes(currFunctionName)) { + functions.wrap.push(currFunctionName); + } else { + functions.reexport.push(currFunctionName); + } + return functions; + }, + { wrap: [], reexport: [] } as { wrap: string[]; reexport: string[] }, + ); + + if (debug && functionsToExport.wrap.length === 0) { + consoleSandbox(() => + // eslint-disable-next-line no-console + console.warn( + "[Sentry] No functions found to wrap. In case the server needs to export async functions other than `handler` or `server`, consider adding the name(s) to Sentry's build options `sentry.entrypointWrappedFunctions` in `nuxt.config.ts`.", + ), + ); + } + + const wrapQuery = functionsToExport.wrap.length + ? `${SENTRY_WRAPPED_FUNCTIONS}${functionsToExport.wrap.join(',')}` + : ''; + const reexportQuery = functionsToExport.reexport.length + ? `${SENTRY_REEXPORTED_FUNCTIONS}${functionsToExport.reexport.join(',')}` + : ''; + + return [wrapQuery, reexportQuery].join(''); +} + +/** + * Constructs a code snippet with function reexports (can be used in Rollup plugins as a return value for `load()`) + * + * **Only exported for testing** + */ +export function constructFunctionReExport(pathWithQuery: string, entryId: string): string { + const { wrap: wrapFunctions, reexport: reexportFunctions } = extractFunctionReexportQueryParameters(pathWithQuery); + + return wrapFunctions + .reduce( + (functionsCode, currFunctionName) => + functionsCode.concat( + `async function ${currFunctionName}_sentryWrapped(...args) {\n` + + ` const res = await import(${JSON.stringify(entryId)});\n` + + ` return res.${currFunctionName}.call(this, ...args);\n` + + '}\n' + + `export { ${currFunctionName}_sentryWrapped as ${currFunctionName} };\n`, + ), + '', + ) + .concat( + reexportFunctions.reduce( + (functionsCode, currFunctionName) => + functionsCode.concat(`export { ${currFunctionName} } from ${JSON.stringify(entryId)};`), + '', + ), + ); +} diff --git a/packages/nitro-utils/test/rollupPlugins/wrapServerEntryWithDynamicImport.test.ts b/packages/nitro-utils/test/rollupPlugins/wrapServerEntryWithDynamicImport.test.ts new file mode 100644 index 000000000000..1ef8b8d0224c --- /dev/null +++ b/packages/nitro-utils/test/rollupPlugins/wrapServerEntryWithDynamicImport.test.ts @@ -0,0 +1,193 @@ +import { describe, expect, it, vi } from 'vitest'; +import { + QUERY_END_INDICATOR, + SENTRY_REEXPORTED_FUNCTIONS, + SENTRY_WRAPPED_ENTRY, + SENTRY_WRAPPED_FUNCTIONS, + constructFunctionReExport, + constructWrappedFunctionExportQuery, + extractFunctionReexportQueryParameters, + removeSentryQueryFromPath, +} from '../../src/rollupPlugins/wrapServerEntryWithDynamicImport'; + +describe('removeSentryQueryFromPath', () => { + it('strips the Sentry query part from the path', () => { + const url = `/example/path${SENTRY_WRAPPED_ENTRY}${SENTRY_WRAPPED_FUNCTIONS}foo,${QUERY_END_INDICATOR}`; + const url2 = `/example/path${SENTRY_WRAPPED_ENTRY}${QUERY_END_INDICATOR}`; + const result = removeSentryQueryFromPath(url); + const result2 = removeSentryQueryFromPath(url2); + expect(result).toBe('/example/path'); + expect(result2).toBe('/example/path'); + }); + + it('returns the same path if the specific query part is not present', () => { + const url = '/example/path?other-query=param'; + const result = removeSentryQueryFromPath(url); + expect(result).toBe(url); + }); +}); + +describe('extractFunctionReexportQueryParameters', () => { + it.each([ + [`${SENTRY_WRAPPED_FUNCTIONS}foo,bar,${QUERY_END_INDICATOR}`, { wrap: ['foo', 'bar'], reexport: [] }], + [ + `${SENTRY_WRAPPED_FUNCTIONS}foo,bar,default${QUERY_END_INDICATOR}`, + { wrap: ['foo', 'bar', 'default'], reexport: [] }, + ], + [ + `${SENTRY_WRAPPED_FUNCTIONS}foo,a.b*c?d[e]f(g)h|i\\\\j(){hello},${QUERY_END_INDICATOR}`, + { wrap: ['foo', 'a\\.b\\*c\\?d\\[e\\]f\\(g\\)h\\|i\\\\\\\\j\\(\\)\\{hello\\}'], reexport: [] }, + ], + [`/example/path/${SENTRY_WRAPPED_FUNCTIONS}foo,bar${QUERY_END_INDICATOR}`, { wrap: ['foo', 'bar'], reexport: [] }], + [ + `${SENTRY_WRAPPED_FUNCTIONS}foo,bar,${SENTRY_REEXPORTED_FUNCTIONS}${QUERY_END_INDICATOR}`, + { wrap: ['foo', 'bar'], reexport: [] }, + ], + [`${SENTRY_REEXPORTED_FUNCTIONS}${QUERY_END_INDICATOR}`, { wrap: [], reexport: [] }], + [ + `/path${SENTRY_WRAPPED_FUNCTIONS}foo,bar${SENTRY_REEXPORTED_FUNCTIONS}bar${QUERY_END_INDICATOR}`, + { wrap: ['foo', 'bar'], reexport: ['bar'] }, + ], + ['?other-query=param', { wrap: [], reexport: [] }], + ])('extracts parameters from the query string: %s', (query, expected) => { + const result = extractFunctionReexportQueryParameters(query); + expect(result).toEqual(expected); + }); +}); + +describe('constructWrappedFunctionExportQuery', () => { + it.each([ + [{ '.': ['handler'] }, ['handler'], `${SENTRY_WRAPPED_FUNCTIONS}handler`], + [{ '.': ['handler'], './module': ['server'] }, [], `${SENTRY_REEXPORTED_FUNCTIONS}handler,server`], + [ + { '.': ['handler'], './module': ['server'] }, + ['server'], + `${SENTRY_WRAPPED_FUNCTIONS}server${SENTRY_REEXPORTED_FUNCTIONS}handler`, + ], + [ + { '.': ['handler', 'otherFunction'] }, + ['handler'], + `${SENTRY_WRAPPED_FUNCTIONS}handler${SENTRY_REEXPORTED_FUNCTIONS}otherFunction`, + ], + [{ '.': ['handler', 'otherFn'] }, ['handler', 'otherFn'], `${SENTRY_WRAPPED_FUNCTIONS}handler,otherFn`], + [{ '.': ['bar'], './module': ['foo'] }, ['bar', 'foo'], `${SENTRY_WRAPPED_FUNCTIONS}bar,foo`], + [{ '.': ['foo', 'bar'] }, ['foo'], `${SENTRY_WRAPPED_FUNCTIONS}foo${SENTRY_REEXPORTED_FUNCTIONS}bar`], + [{ '.': ['foo', 'bar'] }, ['bar'], `${SENTRY_WRAPPED_FUNCTIONS}bar${SENTRY_REEXPORTED_FUNCTIONS}foo`], + [{ '.': ['foo', 'bar'] }, ['foo', 'bar'], `${SENTRY_WRAPPED_FUNCTIONS}foo,bar`], + [{ '.': ['foo', 'bar'] }, [], `${SENTRY_REEXPORTED_FUNCTIONS}foo,bar`], + ])( + 'constructs re-export query for exportedBindings: %j and entrypointWrappedFunctions: %j', + (exportedBindings, entrypointWrappedFunctions, expected) => { + const result = constructWrappedFunctionExportQuery(exportedBindings, entrypointWrappedFunctions); + expect(result).toBe(expected); + }, + ); + + it('logs a warning if no functions are found for re-export and debug is true', () => { + const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); + const exportedBindings = { '.': ['handler'] }; + const entrypointWrappedFunctions = ['nonExistentFunction']; + const debug = true; + + const result = constructWrappedFunctionExportQuery(exportedBindings, entrypointWrappedFunctions, debug); + expect(result).toBe('?sentry-query-reexported-functions=handler'); + expect(consoleWarnSpy).toHaveBeenCalledWith( + "[Sentry] No functions found to wrap. In case the server needs to export async functions other than `handler` or `server`, consider adding the name(s) to Sentry's build options `sentry.entrypointWrappedFunctions` in `nuxt.config.ts`.", + ); + + consoleWarnSpy.mockRestore(); + }); +}); + +describe('constructFunctionReExport', () => { + it('constructs re-export code for given query parameters and entry ID', () => { + const query = `${SENTRY_WRAPPED_FUNCTIONS}foo,bar,${QUERY_END_INDICATOR}}`; + const query2 = `${SENTRY_WRAPPED_FUNCTIONS}foo,bar${QUERY_END_INDICATOR}}`; + const entryId = './module'; + const result = constructFunctionReExport(query, entryId); + const result2 = constructFunctionReExport(query2, entryId); + + const expected = ` +async function foo_sentryWrapped(...args) { + const res = await import("./module"); + return res.foo.call(this, ...args); +} +export { foo_sentryWrapped as foo }; +async function bar_sentryWrapped(...args) { + const res = await import("./module"); + return res.bar.call(this, ...args); +} +export { bar_sentryWrapped as bar }; +`; + expect(result.trim()).toBe(expected.trim()); + expect(result2.trim()).toBe(expected.trim()); + }); + + it('constructs re-export code for a "default" query parameters and entry ID', () => { + const query = `${SENTRY_WRAPPED_FUNCTIONS}default${QUERY_END_INDICATOR}}`; + const entryId = './index'; + const result = constructFunctionReExport(query, entryId); + + const expected = ` +async function default_sentryWrapped(...args) { + const res = await import("./index"); + return res.default.call(this, ...args); +} +export { default_sentryWrapped as default }; +`; + expect(result.trim()).toBe(expected.trim()); + }); + + it('constructs re-export code for a "default" query parameters and entry ID', () => { + const query = `${SENTRY_WRAPPED_FUNCTIONS}default${QUERY_END_INDICATOR}}`; + const entryId = './index'; + const result = constructFunctionReExport(query, entryId); + + const expected = ` +async function default_sentryWrapped(...args) { + const res = await import("./index"); + return res.default.call(this, ...args); +} +export { default_sentryWrapped as default }; +`; + expect(result.trim()).toBe(expected.trim()); + }); + + it('constructs re-export code for a mix of wrapped and re-exported functions', () => { + const query = `${SENTRY_WRAPPED_FUNCTIONS}foo,${SENTRY_REEXPORTED_FUNCTIONS}bar${QUERY_END_INDICATOR}`; + const entryId = './module'; + const result = constructFunctionReExport(query, entryId); + + const expected = ` +async function foo_sentryWrapped(...args) { + const res = await import("./module"); + return res.foo.call(this, ...args); +} +export { foo_sentryWrapped as foo }; +export { bar } from "./module"; +`; + expect(result.trim()).toBe(expected.trim()); + }); + + it('does not re-export a default export for regular re-exported functions', () => { + const query = `${SENTRY_WRAPPED_FUNCTIONS}foo${SENTRY_REEXPORTED_FUNCTIONS}default${QUERY_END_INDICATOR}`; + const entryId = './module'; + const result = constructFunctionReExport(query, entryId); + + const expected = ` +async function foo_sentryWrapped(...args) { + const res = await import("./module"); + return res.foo.call(this, ...args); +} +export { foo_sentryWrapped as foo }; +`; + expect(result.trim()).toBe(expected.trim()); + }); + + it('returns an empty string if the query string is empty', () => { + const query = ''; + const entryId = './module'; + const result = constructFunctionReExport(query, entryId); + expect(result).toBe(''); + }); +}); diff --git a/packages/nitro-utils/test/tsconfig.json b/packages/nitro-utils/test/tsconfig.json new file mode 100644 index 000000000000..38ca0b13bcdd --- /dev/null +++ b/packages/nitro-utils/test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.test.json" +} diff --git a/packages/nitro-utils/test/vitest.setup.ts b/packages/nitro-utils/test/vitest.setup.ts new file mode 100644 index 000000000000..7676ce96afef --- /dev/null +++ b/packages/nitro-utils/test/vitest.setup.ts @@ -0,0 +1,8 @@ +export function setup() {} + +if (!globalThis.fetch) { + // @ts-expect-error - Needed for vitest to work with our fetch instrumentation + globalThis.Request = class Request {}; + // @ts-expect-error - Needed for vitest to work with our fetch instrumentation + globalThis.Response = class Response {}; +} diff --git a/packages/nitro-utils/tsconfig.json b/packages/nitro-utils/tsconfig.json new file mode 100644 index 000000000000..425f0657515d --- /dev/null +++ b/packages/nitro-utils/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + + "include": ["src/**/*"], + + "compilerOptions": { + "lib": ["ES2018"], + } +} diff --git a/packages/nitro-utils/tsconfig.test.json b/packages/nitro-utils/tsconfig.test.json new file mode 100644 index 000000000000..3fbe012384ee --- /dev/null +++ b/packages/nitro-utils/tsconfig.test.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + + "include": ["test/**/*", "vite.config.ts"], + + "compilerOptions": { + // should include all types from `./tsconfig.json` plus types for all test frameworks used + "types": ["node", "vitest/globals"] + } +} diff --git a/packages/nitro-utils/tsconfig.types.json b/packages/nitro-utils/tsconfig.types.json new file mode 100644 index 000000000000..65455f66bd75 --- /dev/null +++ b/packages/nitro-utils/tsconfig.types.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "build/types" + } +} diff --git a/packages/nitro-utils/vite.config.ts b/packages/nitro-utils/vite.config.ts new file mode 100644 index 000000000000..0229ec105e04 --- /dev/null +++ b/packages/nitro-utils/vite.config.ts @@ -0,0 +1,9 @@ +import baseConfig from '../../vite/vite.config'; + +export default { + ...baseConfig, + test: { + environment: 'jsdom', + setupFiles: ['./test/vitest.setup.ts'], + }, +}; diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index df5f2b285ddd..2697e61466e7 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -54,6 +54,7 @@ }, "devDependencies": { "@nuxt/module-builder": "^0.8.4", + "@sentry-internal/nitro-utils": "8.36.0", "nuxt": "^3.13.2" }, "scripts": { diff --git a/packages/nuxt/src/vite/addServerConfig.ts b/packages/nuxt/src/vite/addServerConfig.ts index 5ac673b3dd20..03fbb29f1653 100644 --- a/packages/nuxt/src/vite/addServerConfig.ts +++ b/packages/nuxt/src/vite/addServerConfig.ts @@ -1,19 +1,10 @@ import * as fs from 'fs'; import { createResolver } from '@nuxt/kit'; import type { Nuxt } from '@nuxt/schema'; +import { wrapServerEntryWithDynamicImport } from '@sentry-internal/nitro-utils'; import { consoleSandbox } from '@sentry/core'; import type { Nitro } from 'nitropack'; -import type { InputPluginOption } from 'rollup'; import type { SentryNuxtModuleOptions } from '../common/types'; -import { - QUERY_END_INDICATOR, - SENTRY_REEXPORTED_FUNCTIONS, - SENTRY_WRAPPED_ENTRY, - SENTRY_WRAPPED_FUNCTIONS, - constructFunctionReExport, - constructWrappedFunctionExportQuery, - removeSentryQueryFromPath, -} from './utils'; const SERVER_CONFIG_FILENAME = 'sentry.server.config'; @@ -101,90 +92,12 @@ export function addDynamicImportEntryFileWrapper( } nitro.options.rollupConfig.plugins.push( - wrapEntryWithDynamicImport({ - resolvedSentryConfigPath: createResolver(nitro.options.srcDir).resolve(`/${serverConfigFile}`), + wrapServerEntryWithDynamicImport({ + serverConfigFileName: SERVER_CONFIG_FILENAME, + resolvedServerConfigPath: createResolver(nitro.options.srcDir).resolve(`/${serverConfigFile}`), entrypointWrappedFunctions: moduleOptions.entrypointWrappedFunctions, + additionalImports: ['import-in-the-middle/hook.mjs'], + debug: moduleOptions.debug, }), ); } - -/** - * A Rollup plugin which wraps the server entry with a dynamic `import()`. This makes it possible to initialize Sentry first - * by using a regular `import` and load the server after that. - * This also works with serverless `handler` functions, as it re-exports the `handler`. - */ -function wrapEntryWithDynamicImport({ - resolvedSentryConfigPath, - entrypointWrappedFunctions, - debug, -}: { resolvedSentryConfigPath: string; entrypointWrappedFunctions: string[]; debug?: boolean }): InputPluginOption { - // In order to correctly import the server config file - // and dynamically import the nitro runtime, we need to - // mark the resolutionId with '\0raw' to fall into the - // raw chunk group, c.f. https://github.com/nitrojs/nitro/commit/8b4a408231bdc222569a32ce109796a41eac4aa6#diff-e58102d2230f95ddeef2662957b48d847a6e891e354cfd0ae6e2e03ce848d1a2R142 - const resolutionIdPrefix = '\0raw'; - - return { - name: 'sentry-wrap-entry-with-dynamic-import', - async resolveId(source, importer, options) { - if (source.includes(`/${SERVER_CONFIG_FILENAME}`)) { - return { id: source, moduleSideEffects: true }; - } - - if (source === 'import-in-the-middle/hook.mjs') { - // We are importing "import-in-the-middle" in the returned code of the `load()` function below - // By setting `moduleSideEffects` to `true`, the import is added to the bundle, although nothing is imported from it - // By importing "import-in-the-middle/hook.mjs", we can make sure this file is included, as not all node builders are including files imported with `module.register()`. - // Prevents the error "Failed to register ESM hook Error: Cannot find module 'import-in-the-middle/hook.mjs'" - return { id: source, moduleSideEffects: true, external: true }; - } - - if (options.isEntry && source.includes('.mjs') && !source.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`)) { - const resolution = await this.resolve(source, importer, options); - - // If it cannot be resolved or is external, just return it so that Rollup can display an error - if (!resolution || resolution?.external) return resolution; - - const moduleInfo = await this.load(resolution); - - moduleInfo.moduleSideEffects = true; - - // The enclosing `if` already checks for the suffix in `source`, but a check in `resolution.id` is needed as well to prevent multiple attachment of the suffix - return resolution.id.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`) - ? resolution.id - : `${resolutionIdPrefix}${resolution.id - // Concatenates the query params to mark the file (also attaches names of re-exports - this is needed for serverless functions to re-export the handler) - .concat(SENTRY_WRAPPED_ENTRY) - .concat( - constructWrappedFunctionExportQuery(moduleInfo.exportedBindings, entrypointWrappedFunctions, debug), - ) - .concat(QUERY_END_INDICATOR)}`; - } - return null; - }, - load(id: string) { - if (id.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`)) { - const entryId = removeSentryQueryFromPath(id).slice(resolutionIdPrefix.length); - - // Mostly useful for serverless `handler` functions - const reExportedFunctions = - id.includes(SENTRY_WRAPPED_FUNCTIONS) || id.includes(SENTRY_REEXPORTED_FUNCTIONS) - ? constructFunctionReExport(id, entryId) - : ''; - - return ( - // Regular `import` of the Sentry config - `import ${JSON.stringify(resolvedSentryConfigPath)};\n` + - // Dynamic `import()` for the previous, actual entry point. - // `import()` can be used for any code that should be run after the hooks are registered (https://nodejs.org/api/module.html#enabling) - `import(${JSON.stringify(entryId)});\n` + - // By importing "import-in-the-middle/hook.mjs", we can make sure this file wil be included, as not all node builders are including files imported with `module.register()`. - "import 'import-in-the-middle/hook.mjs';\n" + - `${reExportedFunctions}\n` - ); - } - - return null; - }, - }; -} diff --git a/packages/nuxt/src/vite/utils.ts b/packages/nuxt/src/vite/utils.ts index fff676a6ede1..e41d3fb06cab 100644 --- a/packages/nuxt/src/vite/utils.ts +++ b/packages/nuxt/src/vite/utils.ts @@ -1,6 +1,5 @@ import * as fs from 'fs'; import * as path from 'path'; -import { consoleSandbox } from '@sentry/core'; /** * Find the default SDK init file for the given type (client or server). @@ -25,132 +24,3 @@ export function findDefaultSdkInitFile(type: 'server' | 'client'): string | unde return filePaths.find(filename => fs.existsSync(filename)); } - -export const SENTRY_WRAPPED_ENTRY = '?sentry-query-wrapped-entry'; -export const SENTRY_WRAPPED_FUNCTIONS = '?sentry-query-wrapped-functions='; -export const SENTRY_REEXPORTED_FUNCTIONS = '?sentry-query-reexported-functions='; -export const QUERY_END_INDICATOR = 'SENTRY-QUERY-END'; - -/** - * Strips the Sentry query part from a path. - * Example: example/path?sentry-query-wrapped-entry?sentry-query-functions-reexport=foo,SENTRY-QUERY-END -> /example/path - * - * Only exported for testing. - */ -export function removeSentryQueryFromPath(url: string): string { - // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor - const regex = new RegExp(`\\${SENTRY_WRAPPED_ENTRY}.*?\\${QUERY_END_INDICATOR}`); - return url.replace(regex, ''); -} - -/** - * Extracts and sanitizes function re-export and function wrap query parameters from a query string. - * If it is a default export, it is not considered for re-exporting. - * - * Only exported for testing. - */ -export function extractFunctionReexportQueryParameters(query: string): { wrap: string[]; reexport: string[] } { - // Regex matches the comma-separated params between the functions query - // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor - const wrapRegex = new RegExp( - `\\${SENTRY_WRAPPED_FUNCTIONS}(.*?)(\\${QUERY_END_INDICATOR}|\\${SENTRY_REEXPORTED_FUNCTIONS})`, - ); - // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor - const reexportRegex = new RegExp(`\\${SENTRY_REEXPORTED_FUNCTIONS}(.*?)(\\${QUERY_END_INDICATOR})`); - - const wrapMatch = query.match(wrapRegex); - const reexportMatch = query.match(reexportRegex); - - const wrap = - wrapMatch && wrapMatch[1] - ? wrapMatch[1] - .split(',') - .filter(param => param !== '') - // Sanitize, as code could be injected with another rollup plugin - .map((str: string) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) - : []; - - const reexport = - reexportMatch && reexportMatch[1] - ? reexportMatch[1] - .split(',') - .filter(param => param !== '' && param !== 'default') - // Sanitize, as code could be injected with another rollup plugin - .map((str: string) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) - : []; - - return { wrap, reexport }; -} - -/** - * Constructs a comma-separated string with all functions that need to be re-exported later from the server entry. - * It uses Rollup's `exportedBindings` to determine the functions to re-export. Functions which should be wrapped - * (e.g. serverless handlers) are wrapped by Sentry. - */ -export function constructWrappedFunctionExportQuery( - exportedBindings: Record | null, - entrypointWrappedFunctions: string[], - debug?: boolean, -): string { - const functionsToExport: { wrap: string[]; reexport: string[] } = { - wrap: [], - reexport: [], - }; - - // `exportedBindings` can look like this: `{ '.': [ 'handler' ] }` or `{ '.': [], './firebase-gen-1.mjs': [ 'server' ] }` - // The key `.` refers to exports within the current file, while other keys show from where exports were imported first. - Object.values(exportedBindings || {}).forEach(functions => - functions.forEach(fn => { - if (entrypointWrappedFunctions.includes(fn)) { - functionsToExport.wrap.push(fn); - } else { - functionsToExport.reexport.push(fn); - } - }), - ); - - if (debug && functionsToExport.wrap.length === 0) { - consoleSandbox(() => - // eslint-disable-next-line no-console - console.warn( - "[Sentry] No functions found to wrap. In case the server needs to export async functions other than `handler` or `server`, consider adding the name(s) to Sentry's build options `sentry.entrypointWrappedFunctions` in `nuxt.config.ts`.", - ), - ); - } - - const wrapQuery = functionsToExport.wrap.length - ? `${SENTRY_WRAPPED_FUNCTIONS}${functionsToExport.wrap.join(',')}` - : ''; - const reexportQuery = functionsToExport.reexport.length - ? `${SENTRY_REEXPORTED_FUNCTIONS}${functionsToExport.reexport.join(',')}` - : ''; - - return [wrapQuery, reexportQuery].join(''); -} - -/** - * Constructs a code snippet with function reexports (can be used in Rollup plugins as a return value for `load()`) - */ -export function constructFunctionReExport(pathWithQuery: string, entryId: string): string { - const { wrap: wrapFunctions, reexport: reexportFunctions } = extractFunctionReexportQueryParameters(pathWithQuery); - - return wrapFunctions - .reduce( - (functionsCode, currFunctionName) => - functionsCode.concat( - `async function ${currFunctionName}_sentryWrapped(...args) {\n` + - ` const res = await import(${JSON.stringify(entryId)});\n` + - ` return res.${currFunctionName}.call(this, ...args);\n` + - '}\n' + - `export { ${currFunctionName}_sentryWrapped as ${currFunctionName} };\n`, - ), - '', - ) - .concat( - reexportFunctions.reduce( - (functionsCode, currFunctionName) => - functionsCode.concat(`export { ${currFunctionName} } from ${JSON.stringify(entryId)};`), - '', - ), - ); -} diff --git a/packages/nuxt/test/vite/utils.test.ts b/packages/nuxt/test/vite/utils.test.ts index a35f9cf8ca34..5115742be0f0 100644 --- a/packages/nuxt/test/vite/utils.test.ts +++ b/packages/nuxt/test/vite/utils.test.ts @@ -1,16 +1,6 @@ import * as fs from 'fs'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import { - QUERY_END_INDICATOR, - SENTRY_REEXPORTED_FUNCTIONS, - SENTRY_WRAPPED_ENTRY, - SENTRY_WRAPPED_FUNCTIONS, - constructFunctionReExport, - constructWrappedFunctionExportQuery, - extractFunctionReexportQueryParameters, - findDefaultSdkInitFile, - removeSentryQueryFromPath, -} from '../../src/vite/utils'; +import { findDefaultSdkInitFile } from '../../src/vite/utils'; vi.mock('fs'); @@ -69,185 +59,3 @@ describe('findDefaultSdkInitFile', () => { expect(result).toMatch('packages/nuxt/sentry.server.config.js'); }); }); - -describe('removeSentryQueryFromPath', () => { - it('strips the Sentry query part from the path', () => { - const url = `/example/path${SENTRY_WRAPPED_ENTRY}${SENTRY_WRAPPED_FUNCTIONS}foo,${QUERY_END_INDICATOR}`; - const url2 = `/example/path${SENTRY_WRAPPED_ENTRY}${QUERY_END_INDICATOR}`; - const result = removeSentryQueryFromPath(url); - const result2 = removeSentryQueryFromPath(url2); - expect(result).toBe('/example/path'); - expect(result2).toBe('/example/path'); - }); - - it('returns the same path if the specific query part is not present', () => { - const url = '/example/path?other-query=param'; - const result = removeSentryQueryFromPath(url); - expect(result).toBe(url); - }); -}); - -describe('extractFunctionReexportQueryParameters', () => { - it.each([ - [`${SENTRY_WRAPPED_FUNCTIONS}foo,bar,${QUERY_END_INDICATOR}`, { wrap: ['foo', 'bar'], reexport: [] }], - [ - `${SENTRY_WRAPPED_FUNCTIONS}foo,bar,default${QUERY_END_INDICATOR}`, - { wrap: ['foo', 'bar', 'default'], reexport: [] }, - ], - [ - `${SENTRY_WRAPPED_FUNCTIONS}foo,a.b*c?d[e]f(g)h|i\\\\j(){hello},${QUERY_END_INDICATOR}`, - { wrap: ['foo', 'a\\.b\\*c\\?d\\[e\\]f\\(g\\)h\\|i\\\\\\\\j\\(\\)\\{hello\\}'], reexport: [] }, - ], - [`/example/path/${SENTRY_WRAPPED_FUNCTIONS}foo,bar${QUERY_END_INDICATOR}`, { wrap: ['foo', 'bar'], reexport: [] }], - [ - `${SENTRY_WRAPPED_FUNCTIONS}foo,bar,${SENTRY_REEXPORTED_FUNCTIONS}${QUERY_END_INDICATOR}`, - { wrap: ['foo', 'bar'], reexport: [] }, - ], - [`${SENTRY_REEXPORTED_FUNCTIONS}${QUERY_END_INDICATOR}`, { wrap: [], reexport: [] }], - [ - `/path${SENTRY_WRAPPED_FUNCTIONS}foo,bar${SENTRY_REEXPORTED_FUNCTIONS}bar${QUERY_END_INDICATOR}`, - { wrap: ['foo', 'bar'], reexport: ['bar'] }, - ], - ['?other-query=param', { wrap: [], reexport: [] }], - ])('extracts parameters from the query string: %s', (query, expected) => { - const result = extractFunctionReexportQueryParameters(query); - expect(result).toEqual(expected); - }); -}); - -describe('constructWrappedFunctionExportQuery', () => { - it.each([ - [{ '.': ['handler'] }, ['handler'], `${SENTRY_WRAPPED_FUNCTIONS}handler`], - [{ '.': ['handler'], './module': ['server'] }, [], `${SENTRY_REEXPORTED_FUNCTIONS}handler,server`], - [ - { '.': ['handler'], './module': ['server'] }, - ['server'], - `${SENTRY_WRAPPED_FUNCTIONS}server${SENTRY_REEXPORTED_FUNCTIONS}handler`, - ], - [ - { '.': ['handler', 'otherFunction'] }, - ['handler'], - `${SENTRY_WRAPPED_FUNCTIONS}handler${SENTRY_REEXPORTED_FUNCTIONS}otherFunction`, - ], - [{ '.': ['handler', 'otherFn'] }, ['handler', 'otherFn'], `${SENTRY_WRAPPED_FUNCTIONS}handler,otherFn`], - [{ '.': ['bar'], './module': ['foo'] }, ['bar', 'foo'], `${SENTRY_WRAPPED_FUNCTIONS}bar,foo`], - [{ '.': ['foo', 'bar'] }, ['foo'], `${SENTRY_WRAPPED_FUNCTIONS}foo${SENTRY_REEXPORTED_FUNCTIONS}bar`], - [{ '.': ['foo', 'bar'] }, ['bar'], `${SENTRY_WRAPPED_FUNCTIONS}bar${SENTRY_REEXPORTED_FUNCTIONS}foo`], - [{ '.': ['foo', 'bar'] }, ['foo', 'bar'], `${SENTRY_WRAPPED_FUNCTIONS}foo,bar`], - [{ '.': ['foo', 'bar'] }, [], `${SENTRY_REEXPORTED_FUNCTIONS}foo,bar`], - ])( - 'constructs re-export query for exportedBindings: %j and entrypointWrappedFunctions: %j', - (exportedBindings, entrypointWrappedFunctions, expected) => { - const result = constructWrappedFunctionExportQuery(exportedBindings, entrypointWrappedFunctions); - expect(result).toBe(expected); - }, - ); - - it('logs a warning if no functions are found for re-export and debug is true', () => { - const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); - const exportedBindings = { '.': ['handler'] }; - const entrypointWrappedFunctions = ['nonExistentFunction']; - const debug = true; - - const result = constructWrappedFunctionExportQuery(exportedBindings, entrypointWrappedFunctions, debug); - expect(result).toBe('?sentry-query-reexported-functions=handler'); - expect(consoleWarnSpy).toHaveBeenCalledWith( - "[Sentry] No functions found to wrap. In case the server needs to export async functions other than `handler` or `server`, consider adding the name(s) to Sentry's build options `sentry.entrypointWrappedFunctions` in `nuxt.config.ts`.", - ); - - consoleWarnSpy.mockRestore(); - }); -}); - -describe('constructFunctionReExport', () => { - it('constructs re-export code for given query parameters and entry ID', () => { - const query = `${SENTRY_WRAPPED_FUNCTIONS}foo,bar,${QUERY_END_INDICATOR}}`; - const query2 = `${SENTRY_WRAPPED_FUNCTIONS}foo,bar${QUERY_END_INDICATOR}}`; - const entryId = './module'; - const result = constructFunctionReExport(query, entryId); - const result2 = constructFunctionReExport(query2, entryId); - - const expected = ` -async function foo_sentryWrapped(...args) { - const res = await import("./module"); - return res.foo.call(this, ...args); -} -export { foo_sentryWrapped as foo }; -async function bar_sentryWrapped(...args) { - const res = await import("./module"); - return res.bar.call(this, ...args); -} -export { bar_sentryWrapped as bar }; -`; - expect(result.trim()).toBe(expected.trim()); - expect(result2.trim()).toBe(expected.trim()); - }); - - it('constructs re-export code for a "default" query parameters and entry ID', () => { - const query = `${SENTRY_WRAPPED_FUNCTIONS}default${QUERY_END_INDICATOR}}`; - const entryId = './index'; - const result = constructFunctionReExport(query, entryId); - - const expected = ` -async function default_sentryWrapped(...args) { - const res = await import("./index"); - return res.default.call(this, ...args); -} -export { default_sentryWrapped as default }; -`; - expect(result.trim()).toBe(expected.trim()); - }); - - it('constructs re-export code for a "default" query parameters and entry ID', () => { - const query = `${SENTRY_WRAPPED_FUNCTIONS}default${QUERY_END_INDICATOR}}`; - const entryId = './index'; - const result = constructFunctionReExport(query, entryId); - - const expected = ` -async function default_sentryWrapped(...args) { - const res = await import("./index"); - return res.default.call(this, ...args); -} -export { default_sentryWrapped as default }; -`; - expect(result.trim()).toBe(expected.trim()); - }); - - it('constructs re-export code for a mix of wrapped and re-exported functions', () => { - const query = `${SENTRY_WRAPPED_FUNCTIONS}foo,${SENTRY_REEXPORTED_FUNCTIONS}bar${QUERY_END_INDICATOR}`; - const entryId = './module'; - const result = constructFunctionReExport(query, entryId); - - const expected = ` -async function foo_sentryWrapped(...args) { - const res = await import("./module"); - return res.foo.call(this, ...args); -} -export { foo_sentryWrapped as foo }; -export { bar } from "./module"; -`; - expect(result.trim()).toBe(expected.trim()); - }); - - it('does not re-export a default export for regular re-exported functions', () => { - const query = `${SENTRY_WRAPPED_FUNCTIONS}foo${SENTRY_REEXPORTED_FUNCTIONS}default${QUERY_END_INDICATOR}`; - const entryId = './module'; - const result = constructFunctionReExport(query, entryId); - - const expected = ` -async function foo_sentryWrapped(...args) { - const res = await import("./module"); - return res.foo.call(this, ...args); -} -export { foo_sentryWrapped as foo }; -`; - expect(result.trim()).toBe(expected.trim()); - }); - - it('returns an empty string if the query string is empty', () => { - const query = ''; - const entryId = './module'; - const result = constructFunctionReExport(query, entryId); - expect(result).toBe(''); - }); -}); From 3970390ea5e290c0ba29ff33ee5fbf04f4721f77 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Thu, 7 Nov 2024 11:26:16 +0100 Subject: [PATCH 02/18] review suggestions --- dev-packages/rollup-utils/npmHelpers.mjs | 10 +- packages/nitro-utils/package.json | 12 +- packages/nitro-utils/rollup.npm.config.mjs | 1 + packages/nitro-utils/src/index.ts | 5 +- .../wrapServerEntryWithDynamicImport.ts | 34 +++-- packages/nuxt/build.config.ts | 7 + packages/nuxt/src/common/types.ts | 9 ++ packages/nuxt/src/vite/addServerConfig.ts | 1 + packages/nuxt/tsconfig.json | 2 +- yarn.lock | 129 ++++++++++++++++++ 10 files changed, 189 insertions(+), 21 deletions(-) create mode 100644 packages/nuxt/build.config.ts diff --git a/dev-packages/rollup-utils/npmHelpers.mjs b/dev-packages/rollup-utils/npmHelpers.mjs index 4e6483364ee4..682350bf2d1e 100644 --- a/dev-packages/rollup-utils/npmHelpers.mjs +++ b/dev-packages/rollup-utils/npmHelpers.mjs @@ -132,9 +132,15 @@ export function makeBaseNPMConfig(options = {}) { } export function makeNPMConfigVariants(baseConfig, options = {}) { - const { emitEsm = true } = options; + const { emitEsm = true, emitCjs = true } = options; - const variantSpecificConfigs = [{ output: { format: 'cjs', dir: path.join(baseConfig.output.dir, 'cjs') } }]; + const variantSpecificConfigs = []; + + if (emitCjs) { + variantSpecificConfigs.push({ + output: { format: 'cjs', dir: path.join(baseConfig.output.dir, 'cjs') }, + }); + } if (emitEsm) { variantSpecificConfigs.push({ diff --git a/packages/nitro-utils/package.json b/packages/nitro-utils/package.json index d6bed1ea224e..11b859431c63 100644 --- a/packages/nitro-utils/package.json +++ b/packages/nitro-utils/package.json @@ -7,14 +7,14 @@ "author": "Sentry", "license": "MIT", "private": true, + "type": "module", "engines": { "node": ">=14.18" }, "files": [ "/build" ], - "main": "build/cjs/index.js", - "module": "build/esm/index.js", + "main": "build/esm/index.js", "types": "build/types/index.d.ts", "exports": { "./package.json": "./package.json", @@ -22,10 +22,6 @@ "import": { "types": "./build/types/index.d.ts", "default": "./build/esm/index.js" - }, - "require": { - "types": "./build/types/index.d.ts", - "default": "./build/cjs/index.js" } } }, @@ -40,10 +36,12 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.36.0", "@sentry/types": "8.36.0", "@sentry/utils": "8.36.0" }, + "devDependencies": { + "rollup": "^4.24.4" + }, "scripts": { "build": "run-p build:transpile build:types", "build:dev": "yarn build", diff --git a/packages/nitro-utils/rollup.npm.config.mjs b/packages/nitro-utils/rollup.npm.config.mjs index d28a7a6f54a0..648dd9b794f4 100644 --- a/packages/nitro-utils/rollup.npm.config.mjs +++ b/packages/nitro-utils/rollup.npm.config.mjs @@ -14,4 +14,5 @@ export default makeNPMConfigVariants( }, }, }), + { emitCjs: false }, ); diff --git a/packages/nitro-utils/src/index.ts b/packages/nitro-utils/src/index.ts index ae57db463f33..9aa98faf5d62 100644 --- a/packages/nitro-utils/src/index.ts +++ b/packages/nitro-utils/src/index.ts @@ -1 +1,4 @@ -export { wrapServerEntryWithDynamicImport } from './rollupPlugins/wrapServerEntryWithDynamicImport'; +export { + wrapServerEntryWithDynamicImport, + type WrapServerEntryPluginOptions, +} from './rollupPlugins/wrapServerEntryWithDynamicImport'; diff --git a/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts b/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts index cf855fc0b68e..8c4ad34b1b35 100644 --- a/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts +++ b/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts @@ -6,6 +6,15 @@ export const SENTRY_WRAPPED_FUNCTIONS = '?sentry-query-wrapped-functions='; export const SENTRY_REEXPORTED_FUNCTIONS = '?sentry-query-reexported-functions='; export const QUERY_END_INDICATOR = 'SENTRY-QUERY-END'; +export type WrapServerEntryPluginOptions = { + serverEntrypointFileName: string; + serverConfigFileName: string; + resolvedServerConfigPath: string; + entrypointWrappedFunctions: string[]; + additionalImports?: string[]; + debug?: boolean; +}; + /** * A Rollup plugin which wraps the server entry with a dynamic `import()`. This makes it possible to initialize Sentry first * by using a regular `import` and load the server after that. @@ -18,15 +27,15 @@ export const QUERY_END_INDICATOR = 'SENTRY-QUERY-END'; * @param config.additionalImports Adds additional imports to the entry file. Can be e.g. 'import-in-the-middle/hook.mjs' * @param config.debug Whether debug logs are enabled in the build time environment */ -export function wrapServerEntryWithDynamicImport(config: { - serverConfigFileName: string; - resolvedServerConfigPath: string; - entrypointWrappedFunctions: string[]; - additionalImports?: string[]; - debug?: boolean; -}): InputPluginOption { - const { serverConfigFileName, resolvedServerConfigPath, entrypointWrappedFunctions, additionalImports, debug } = - config; +export function wrapServerEntryWithDynamicImport(config: WrapServerEntryPluginOptions): InputPluginOption { + const { + serverEntrypointFileName, + serverConfigFileName, + resolvedServerConfigPath, + entrypointWrappedFunctions, + additionalImports, + debug, + } = config; return { name: 'sentry-wrap-server-entry-with-dynamic-import', @@ -43,7 +52,12 @@ export function wrapServerEntryWithDynamicImport(config: { return { id: source, moduleSideEffects: true, external: true }; } - if (options.isEntry && source.includes('.mjs') && !source.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`)) { + if ( + options.isEntry && + source.includes(serverEntrypointFileName) && + source.includes('.mjs') && + !source.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`) + ) { const resolution = await this.resolve(source, importer, options); // If it cannot be resolved or is external, just return it so that Rollup can display an error diff --git a/packages/nuxt/build.config.ts b/packages/nuxt/build.config.ts new file mode 100644 index 000000000000..4cb00345dc43 --- /dev/null +++ b/packages/nuxt/build.config.ts @@ -0,0 +1,7 @@ +import { defineBuildConfig } from 'unbuild'; + +// Build Config for the Nuxt Module Builder: https://github.com/nuxt/module-builder +export default defineBuildConfig({ + // The devDependency "@sentry-internal/nitro-utils" triggers "Inlined implicit external", but it's not external + failOnWarn: false, +}); diff --git a/packages/nuxt/src/common/types.ts b/packages/nuxt/src/common/types.ts index 46f390120cfe..b22a5216a99a 100644 --- a/packages/nuxt/src/common/types.ts +++ b/packages/nuxt/src/common/types.ts @@ -130,6 +130,15 @@ export type SentryNuxtModuleOptions = { */ entrypointWrappedFunctions?: string[]; + /** + * By default—unless you configure `dynamicImportForServerEntry: false`—the SDK will try to wrap your Nitro server entrypoint + * with a dynamic `import()` to ensure all dependencies can be properly instrumented. + * + * The server entrypoint filename is automatically set by the Sentry SDK depending on the Nitro present. + * In case the server entrypoint has a different filename, you can overwrite it here. + */ + serverEntrypointFileName?: string; + /** * Options to be passed directly to the Sentry Rollup Plugin (`@sentry/rollup-plugin`) and Sentry Vite Plugin (`@sentry/vite-plugin`) that ship with the Sentry Nuxt SDK. * You can use this option to override any options the SDK passes to the Vite (for Nuxt) and Rollup (for Nitro) plugin. diff --git a/packages/nuxt/src/vite/addServerConfig.ts b/packages/nuxt/src/vite/addServerConfig.ts index 03fbb29f1653..2538b16e314c 100644 --- a/packages/nuxt/src/vite/addServerConfig.ts +++ b/packages/nuxt/src/vite/addServerConfig.ts @@ -93,6 +93,7 @@ export function addDynamicImportEntryFileWrapper( nitro.options.rollupConfig.plugins.push( wrapServerEntryWithDynamicImport({ + serverEntrypointFileName: moduleOptions.serverEntrypointFileName || nitro.options.preset, serverConfigFileName: SERVER_CONFIG_FILENAME, resolvedServerConfigPath: createResolver(nitro.options.srcDir).resolve(`/${serverConfigFile}`), entrypointWrappedFunctions: moduleOptions.entrypointWrappedFunctions, diff --git a/packages/nuxt/tsconfig.json b/packages/nuxt/tsconfig.json index 7ddf2a7162c7..de9c931f2cd1 100644 --- a/packages/nuxt/tsconfig.json +++ b/packages/nuxt/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.json", - "include": ["src/**/*"], + "include": ["src/**/*", "build.config.ts"], "compilerOptions": { // package-specific options diff --git a/yarn.lock b/yarn.lock index 320f57f45de4..8ac6539a7618 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8473,91 +8473,181 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.2.tgz#07db37fcd9d401aae165f662c0069efd61d4ffcc" integrity sha512-ufoveNTKDg9t/b7nqI3lwbCG/9IJMhADBNjjz/Jn6LxIZxD7T5L8l2uO/wD99945F1Oo8FvgbbZJRguyk/BdzA== +"@rollup/rollup-android-arm-eabi@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.4.tgz#c460b54c50d42f27f8254c435a4f3b3e01910bc8" + integrity sha512-jfUJrFct/hTA0XDM5p/htWKoNNTbDLY0KRwEt6pyOA6k2fmk0WVwl65PdUdJZgzGEHWx+49LilkcSaumQRyNQw== + "@rollup/rollup-android-arm64@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.2.tgz#160975402adf85ecd58a0721ad60ae1779a68147" integrity sha512-iZoYCiJz3Uek4NI0J06/ZxUgwAfNzqltK0MptPDO4OR0a88R4h0DSELMsflS6ibMCJ4PnLvq8f7O1d7WexUvIA== +"@rollup/rollup-android-arm64@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.4.tgz#96e01f3a04675d8d5973ab8d3fd6bc3be21fa5e1" + integrity sha512-j4nrEO6nHU1nZUuCfRKoCcvh7PIywQPUCBa2UsootTHvTHIoIu2BzueInGJhhvQO/2FTRdNYpf63xsgEqH9IhA== + "@rollup/rollup-darwin-arm64@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.2.tgz#2b126f0aa4349694fe2941bcbcc4b0982b7f1a49" integrity sha512-/UhrIxobHYCBfhi5paTkUDQ0w+jckjRZDZ1kcBL132WeHZQ6+S5v9jQPVGLVrLbNUebdIRpIt00lQ+4Z7ys4Rg== +"@rollup/rollup-darwin-arm64@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.4.tgz#9b2ec23b17b47cbb2f771b81f86ede3ac6730bce" + integrity sha512-GmU/QgGtBTeraKyldC7cDVVvAJEOr3dFLKneez/n7BvX57UdhOqDsVwzU7UOnYA7AAOt+Xb26lk79PldDHgMIQ== + "@rollup/rollup-darwin-x64@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.2.tgz#3f4987eff6195532037c50b8db92736e326b5bb2" integrity sha512-1F/jrfhxJtWILusgx63WeTvGTwE4vmsT9+e/z7cZLKU8sBMddwqw3UV5ERfOV+H1FuRK3YREZ46J4Gy0aP3qDA== +"@rollup/rollup-darwin-x64@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.4.tgz#f30e4ee6929e048190cf10e0daa8e8ae035b6e46" + integrity sha512-N6oDBiZCBKlwYcsEPXGDE4g9RoxZLK6vT98M8111cW7VsVJFpNEqvJeIPfsCzbf0XEakPslh72X0gnlMi4Ddgg== + "@rollup/rollup-freebsd-arm64@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.2.tgz#15fe184ecfafc635879500f6985c954e57697c44" integrity sha512-1YWOpFcGuC6iGAS4EI+o3BV2/6S0H+m9kFOIlyFtp4xIX5rjSnL3AwbTBxROX0c8yWtiWM7ZI6mEPTI7VkSpZw== +"@rollup/rollup-freebsd-arm64@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.4.tgz#c54b2373ec5bcf71f08c4519c7ae80a0b6c8e03b" + integrity sha512-py5oNShCCjCyjWXCZNrRGRpjWsF0ic8f4ieBNra5buQz0O/U6mMXCpC1LvrHuhJsNPgRt36tSYMidGzZiJF6mw== + "@rollup/rollup-freebsd-x64@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.2.tgz#c72d37315d36b6e0763b7aabb6ae53c361b45e05" integrity sha512-3qAqTewYrCdnOD9Gl9yvPoAoFAVmPJsBvleabvx4bnu1Kt6DrB2OALeRVag7BdWGWLhP1yooeMLEi6r2nYSOjg== +"@rollup/rollup-freebsd-x64@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.4.tgz#3bc53aa29d5a34c28ba8e00def76aa612368458e" + integrity sha512-L7VVVW9FCnTTp4i7KrmHeDsDvjB4++KOBENYtNYAiYl96jeBThFfhP6HVxL74v4SiZEVDH/1ILscR5U9S4ms4g== + "@rollup/rollup-linux-arm-gnueabihf@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.2.tgz#f274f81abf845dcca5f1f40d434a09a79a3a73a0" integrity sha512-ArdGtPHjLqWkqQuoVQ6a5UC5ebdX8INPuJuJNWRe0RGa/YNhVvxeWmCTFQ7LdmNCSUzVZzxAvUznKaYx645Rig== +"@rollup/rollup-linux-arm-gnueabihf@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.4.tgz#c85aedd1710c9e267ee86b6d1ce355ecf7d9e8d9" + integrity sha512-10ICosOwYChROdQoQo589N5idQIisxjaFE/PAnX2i0Zr84mY0k9zul1ArH0rnJ/fpgiqfu13TFZR5A5YJLOYZA== + "@rollup/rollup-linux-arm-musleabihf@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.2.tgz#9edaeb1a9fa7d4469917cb0614f665f1cf050625" integrity sha512-B6UHHeNnnih8xH6wRKB0mOcJGvjZTww1FV59HqJoTJ5da9LCG6R4SEBt6uPqzlawv1LoEXSS0d4fBlHNWl6iYw== +"@rollup/rollup-linux-arm-musleabihf@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.4.tgz#e77313408bf13995aecde281aec0cceb08747e42" + integrity sha512-ySAfWs69LYC7QhRDZNKqNhz2UKN8LDfbKSMAEtoEI0jitwfAG2iZwVqGACJT+kfYvvz3/JgsLlcBP+WWoKCLcw== + "@rollup/rollup-linux-arm64-gnu@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.2.tgz#6eb6851f594336bfa00f074f58a00a61e9751493" integrity sha512-kr3gqzczJjSAncwOS6i7fpb4dlqcvLidqrX5hpGBIM1wtt0QEVtf4wFaAwVv8QygFU8iWUMYEoJZWuWxyua4GQ== +"@rollup/rollup-linux-arm64-gnu@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.4.tgz#633f632397b3662108cfaa1abca2a80b85f51102" + integrity sha512-uHYJ0HNOI6pGEeZ/5mgm5arNVTI0nLlmrbdph+pGXpC9tFHFDQmDMOEqkmUObRfosJqpU8RliYoGz06qSdtcjg== + "@rollup/rollup-linux-arm64-musl@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.2.tgz#9d8dc8e80df8f156d2888ecb8d6c96d653580731" integrity sha512-TDdHLKCWgPuq9vQcmyLrhg/bgbOvIQ8rtWQK7MRxJ9nvaxKx38NvY7/Lo6cYuEnNHqf6rMqnivOIPIQt6H2AoA== +"@rollup/rollup-linux-arm64-musl@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.4.tgz#63edd72b29c4cced93e16113a68e1be9fef88907" + integrity sha512-38yiWLemQf7aLHDgTg85fh3hW9stJ0Muk7+s6tIkSUOMmi4Xbv5pH/5Bofnsb6spIwD5FJiR+jg71f0CH5OzoA== + "@rollup/rollup-linux-powerpc64le-gnu@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.2.tgz#358e3e7dda2d60c46ff7c74f7075045736df5b50" integrity sha512-xv9vS648T3X4AxFFZGWeB5Dou8ilsv4VVqJ0+loOIgDO20zIhYfDLkk5xoQiej2RiSQkld9ijF/fhLeonrz2mw== +"@rollup/rollup-linux-powerpc64le-gnu@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.4.tgz#a9418a4173df80848c0d47df0426a0bf183c4e75" + integrity sha512-q73XUPnkwt9ZNF2xRS4fvneSuaHw2BXuV5rI4cw0fWYVIWIBeDZX7c7FWhFQPNTnE24172K30I+dViWRVD9TwA== + "@rollup/rollup-linux-riscv64-gnu@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.2.tgz#b08461ace599c3f0b5f27051f1756b6cf1c78259" integrity sha512-tbtXwnofRoTt223WUZYiUnbxhGAOVul/3StZ947U4A5NNjnQJV5irKMm76G0LGItWs6y+SCjUn/Q0WaMLkEskg== +"@rollup/rollup-linux-riscv64-gnu@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.4.tgz#bc9c195db036a27e5e3339b02f51526b4ce1e988" + integrity sha512-Aie/TbmQi6UXokJqDZdmTJuZBCU3QBDA8oTKRGtd4ABi/nHgXICulfg1KI6n9/koDsiDbvHAiQO3YAUNa/7BCw== + "@rollup/rollup-linux-s390x-gnu@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.2.tgz#daab36c9b5c8ac4bfe5a9c4c39ad711464b7dfee" integrity sha512-gc97UebApwdsSNT3q79glOSPdfwgwj5ELuiyuiMY3pEWMxeVqLGKfpDFoum4ujivzxn6veUPzkGuSYoh5deQ2Q== +"@rollup/rollup-linux-s390x-gnu@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.4.tgz#1651fdf8144ae89326c01da5d52c60be63e71a82" + integrity sha512-P8MPErVO/y8ohWSP9JY7lLQ8+YMHfTI4bAdtCi3pC2hTeqFJco2jYspzOzTUB8hwUWIIu1xwOrJE11nP+0JFAQ== + "@rollup/rollup-linux-x64-gnu@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.2.tgz#4cc3a4f31920bdb028dbfd7ce0e972a17424a63c" integrity sha512-jOG/0nXb3z+EM6SioY8RofqqmZ+9NKYvJ6QQaa9Mvd3RQxlH68/jcB/lpyVt4lCiqr04IyaC34NzhUqcXbB5FQ== +"@rollup/rollup-linux-x64-gnu@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.4.tgz#e473de5e4acb95fcf930a35cbb7d3e8080e57a6f" + integrity sha512-K03TljaaoPK5FOyNMZAAEmhlyO49LaE4qCsr0lYHUKyb6QacTNF9pnfPpXnFlFD3TXuFbFbz7tJ51FujUXkXYA== + "@rollup/rollup-linux-x64-musl@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.2.tgz#59800e26c538517ee05f4645315d9e1aded93200" integrity sha512-XAo7cJec80NWx9LlZFEJQxqKOMz/lX3geWs2iNT5CHIERLFfd90f3RYLLjiCBm1IMaQ4VOX/lTC9lWfzzQm14Q== +"@rollup/rollup-linux-x64-musl@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.4.tgz#0af12dd2578c29af4037f0c834b4321429dd5b01" + integrity sha512-VJYl4xSl/wqG2D5xTYncVWW+26ICV4wubwN9Gs5NrqhJtayikwCXzPL8GDsLnaLU3WwhQ8W02IinYSFJfyo34Q== + "@rollup/rollup-win32-arm64-msvc@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.2.tgz#c80e2c33c952b6b171fa6ad9a97dfbb2e4ebee44" integrity sha512-A+JAs4+EhsTjnPQvo9XY/DC0ztaws3vfqzrMNMKlwQXuniBKOIIvAAI8M0fBYiTCxQnElYu7mLk7JrhlQ+HeOw== +"@rollup/rollup-win32-arm64-msvc@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.4.tgz#e48e78cdd45313b977c1390f4bfde7ab79be8871" + integrity sha512-ku2GvtPwQfCqoPFIJCqZ8o7bJcj+Y54cZSr43hHca6jLwAiCbZdBUOrqE6y29QFajNAzzpIOwsckaTFmN6/8TA== + "@rollup/rollup-win32-ia32-msvc@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.2.tgz#a1e9d275cb16f6d5feb9c20aee7e897b1e193359" integrity sha512-ZhcrakbqA1SCiJRMKSU64AZcYzlZ/9M5LaYil9QWxx9vLnkQ9Vnkve17Qn4SjlipqIIBFKjBES6Zxhnvh0EAEw== +"@rollup/rollup-win32-ia32-msvc@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.4.tgz#a3fc8536d243fe161c796acb93eba43c250f311c" + integrity sha512-V3nCe+eTt/W6UYNr/wGvO1fLpHUrnlirlypZfKCT1fG6hWfqhPgQV/K/mRBXBpxc0eKLIF18pIOFVPh0mqHjlg== + "@rollup/rollup-win32-x64-msvc@4.24.2": version "4.24.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.2.tgz#0610af0fb8fec52be779d5b163bbbd6930150467" integrity sha512-2mLH46K1u3r6uwc95hU+OR9q/ggYMpnS7pSp83Ece1HUQgF9Nh/QwTK5rcgbFnV9j+08yBrU5sA/P0RK2MSBNA== +"@rollup/rollup-win32-x64-msvc@4.24.4": + version "4.24.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.4.tgz#e2a9d1fd56524103a6cc8a54404d9d3ebc73c454" + integrity sha512-LTw1Dfd0mBIEqUVCxbvTE/LLo+9ZxVC9k99v1v4ahg9Aak6FpqOfNu5kRkeTAn0wphoC4JU7No1/rL+bBCEwhg== + "@schematics/angular@14.2.13": version "14.2.13" resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-14.2.13.tgz#35ee9120a3ac07077bad169fa74fdf4ce4e193d7" @@ -8682,6 +8772,18 @@ "@sentry/bundler-plugin-core" "2.22.6" unplugin "1.0.1" +"@sentry/types@8.36.0": + version "8.36.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.36.0.tgz#b58397eb672d896b65b06103feb59dba74da9d39" + integrity sha512-K1pVFfdGHw115RzGHpwSOqoEPeayn4N1F9IfM0kxrYpQSbFT1X29eak88GBfC8gPiLEF0iFGlSaQ4ERmF7oRcA== + +"@sentry/utils@8.36.0": + version "8.36.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.36.0.tgz#e733042ae231fdeeafe6970e49283dcd9ac9700f" + integrity sha512-oJ3EDPj0I00z+AwC3EWBpSidXYUoKW0Id8MfMQP5Hflniz3gif7UEReblT+FJgPEVo6+6uNzAncY0MuNMxmDKQ== + dependencies: + "@sentry/types" "8.36.0" + "@sentry/vite-plugin@2.22.6", "@sentry/vite-plugin@^2.22.6": version "2.22.6" resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-2.22.6.tgz#d08a1ede05f137636d5b3c61845d24c0114f0d76" @@ -29763,6 +29865,33 @@ rollup@^4.13.0, rollup@^4.18.0, rollup@^4.20.0, rollup@^4.24.2: "@rollup/rollup-win32-x64-msvc" "4.24.2" fsevents "~2.3.2" +rollup@^4.24.4: + version "4.24.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.24.4.tgz#fdc76918de02213c95447c9ffff5e35dddb1d058" + integrity sha512-vGorVWIsWfX3xbcyAS+I047kFKapHYivmkaT63Smj77XwvLSJos6M1xGqZnBPFQFBRZDOcG1QnYEIxAvTr/HjA== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.24.4" + "@rollup/rollup-android-arm64" "4.24.4" + "@rollup/rollup-darwin-arm64" "4.24.4" + "@rollup/rollup-darwin-x64" "4.24.4" + "@rollup/rollup-freebsd-arm64" "4.24.4" + "@rollup/rollup-freebsd-x64" "4.24.4" + "@rollup/rollup-linux-arm-gnueabihf" "4.24.4" + "@rollup/rollup-linux-arm-musleabihf" "4.24.4" + "@rollup/rollup-linux-arm64-gnu" "4.24.4" + "@rollup/rollup-linux-arm64-musl" "4.24.4" + "@rollup/rollup-linux-powerpc64le-gnu" "4.24.4" + "@rollup/rollup-linux-riscv64-gnu" "4.24.4" + "@rollup/rollup-linux-s390x-gnu" "4.24.4" + "@rollup/rollup-linux-x64-gnu" "4.24.4" + "@rollup/rollup-linux-x64-musl" "4.24.4" + "@rollup/rollup-win32-arm64-msvc" "4.24.4" + "@rollup/rollup-win32-ia32-msvc" "4.24.4" + "@rollup/rollup-win32-x64-msvc" "4.24.4" + fsevents "~2.3.2" + rrweb-cssom@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" From a5d319388e963556488fae2f5f07892c3d3d2ad9 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Thu, 7 Nov 2024 13:37:56 +0100 Subject: [PATCH 03/18] don't publish nitro-utils --- dev-packages/e2e-tests/verdaccio-config/config.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dev-packages/e2e-tests/verdaccio-config/config.yaml b/dev-packages/e2e-tests/verdaccio-config/config.yaml index 67ee55a9d9ce..09de4fecba0d 100644 --- a/dev-packages/e2e-tests/verdaccio-config/config.yaml +++ b/dev-packages/e2e-tests/verdaccio-config/config.yaml @@ -206,6 +206,11 @@ packages: unpublish: $all # proxy: npmjs # Don't proxy for E2E tests! + '@sentry-internal/nitro-utils': + access: $undefined + publish: $undefined + unpublish: $undefined + '@*/*': # scoped packages access: $all From 413a5f6f1c25603ce4c9284102f485ead03595e0 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Fri, 8 Nov 2024 10:39:22 +0100 Subject: [PATCH 04/18] delete private (verdaccio) --- dev-packages/e2e-tests/verdaccio-config/config.yaml | 5 ----- packages/nitro-utils/package.json | 1 - 2 files changed, 6 deletions(-) diff --git a/dev-packages/e2e-tests/verdaccio-config/config.yaml b/dev-packages/e2e-tests/verdaccio-config/config.yaml index 09de4fecba0d..67ee55a9d9ce 100644 --- a/dev-packages/e2e-tests/verdaccio-config/config.yaml +++ b/dev-packages/e2e-tests/verdaccio-config/config.yaml @@ -206,11 +206,6 @@ packages: unpublish: $all # proxy: npmjs # Don't proxy for E2E tests! - '@sentry-internal/nitro-utils': - access: $undefined - publish: $undefined - unpublish: $undefined - '@*/*': # scoped packages access: $all diff --git a/packages/nitro-utils/package.json b/packages/nitro-utils/package.json index 11b859431c63..8a1397c611d0 100644 --- a/packages/nitro-utils/package.json +++ b/packages/nitro-utils/package.json @@ -6,7 +6,6 @@ "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nitro-utils", "author": "Sentry", "license": "MIT", - "private": true, "type": "module", "engines": { "node": ">=14.18" From 300b091fca24041af5d5b224d6243519af50c860 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Fri, 8 Nov 2024 10:42:39 +0100 Subject: [PATCH 05/18] change eslintrc to cjs --- packages/nitro-utils/{.eslintrc.js => .eslintrc.cjs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/nitro-utils/{.eslintrc.js => .eslintrc.cjs} (100%) diff --git a/packages/nitro-utils/.eslintrc.js b/packages/nitro-utils/.eslintrc.cjs similarity index 100% rename from packages/nitro-utils/.eslintrc.js rename to packages/nitro-utils/.eslintrc.cjs From dca73d3a44b8fc053e8e77d4361d267de3fc3c5c Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Fri, 8 Nov 2024 10:43:34 +0100 Subject: [PATCH 06/18] bump versions --- packages/nitro-utils/package.json | 6 +++--- packages/nuxt/package.json | 2 +- yarn.lock | 12 ------------ 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/packages/nitro-utils/package.json b/packages/nitro-utils/package.json index 8a1397c611d0..0b4d6b15b026 100644 --- a/packages/nitro-utils/package.json +++ b/packages/nitro-utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/nitro-utils", - "version": "8.36.0", + "version": "8.37.1", "description": "Utilities for all Sentry SDKs with Nitro on the server-side", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nitro-utils", @@ -35,8 +35,8 @@ "access": "public" }, "dependencies": { - "@sentry/types": "8.36.0", - "@sentry/utils": "8.36.0" + "@sentry/types": "8.37.1", + "@sentry/utils": "8.37.1" }, "devDependencies": { "rollup": "^4.24.4" diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 2697e61466e7..560c6aa4be3d 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -54,7 +54,7 @@ }, "devDependencies": { "@nuxt/module-builder": "^0.8.4", - "@sentry-internal/nitro-utils": "8.36.0", + "@sentry-internal/nitro-utils": "8.37.1", "nuxt": "^3.13.2" }, "scripts": { diff --git a/yarn.lock b/yarn.lock index 8ac6539a7618..7bae79772a90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8772,18 +8772,6 @@ "@sentry/bundler-plugin-core" "2.22.6" unplugin "1.0.1" -"@sentry/types@8.36.0": - version "8.36.0" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.36.0.tgz#b58397eb672d896b65b06103feb59dba74da9d39" - integrity sha512-K1pVFfdGHw115RzGHpwSOqoEPeayn4N1F9IfM0kxrYpQSbFT1X29eak88GBfC8gPiLEF0iFGlSaQ4ERmF7oRcA== - -"@sentry/utils@8.36.0": - version "8.36.0" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.36.0.tgz#e733042ae231fdeeafe6970e49283dcd9ac9700f" - integrity sha512-oJ3EDPj0I00z+AwC3EWBpSidXYUoKW0Id8MfMQP5Hflniz3gif7UEReblT+FJgPEVo6+6uNzAncY0MuNMxmDKQ== - dependencies: - "@sentry/types" "8.36.0" - "@sentry/vite-plugin@2.22.6", "@sentry/vite-plugin@^2.22.6": version "2.22.6" resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-2.22.6.tgz#d08a1ede05f137636d5b3c61845d24c0114f0d76" From eaf8845baaaaed009790703a11a047b9161730c7 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Fri, 8 Nov 2024 12:45:17 +0100 Subject: [PATCH 07/18] exclude from node 14 test --- packages/nitro-utils/{.eslintrc.cjs => .eslintrc.js} | 0 packages/nitro-utils/package.json | 1 - scripts/ci-unit-tests.ts | 1 + 3 files changed, 1 insertion(+), 1 deletion(-) rename packages/nitro-utils/{.eslintrc.cjs => .eslintrc.js} (100%) diff --git a/packages/nitro-utils/.eslintrc.cjs b/packages/nitro-utils/.eslintrc.js similarity index 100% rename from packages/nitro-utils/.eslintrc.cjs rename to packages/nitro-utils/.eslintrc.js diff --git a/packages/nitro-utils/package.json b/packages/nitro-utils/package.json index 0b4d6b15b026..d3f9dfc76cc5 100644 --- a/packages/nitro-utils/package.json +++ b/packages/nitro-utils/package.json @@ -6,7 +6,6 @@ "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nitro-utils", "author": "Sentry", "license": "MIT", - "type": "module", "engines": { "node": ">=14.18" }, diff --git a/scripts/ci-unit-tests.ts b/scripts/ci-unit-tests.ts index ea771b29a957..08459e9eabba 100644 --- a/scripts/ci-unit-tests.ts +++ b/scripts/ci-unit-tests.ts @@ -47,6 +47,7 @@ const SKIP_TEST_PACKAGES: Record = { '@sentry/nuxt', '@sentry/nestjs', '@sentry-internal/eslint-plugin-sdk', + '@sentry-internal/nitro-utils', ], }, '16': { From ee036ed0c1a39dab47d5cf97b664d20e3c02ec13 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Fri, 8 Nov 2024 15:18:08 +0100 Subject: [PATCH 08/18] add cjs again --- dev-packages/rollup-utils/npmHelpers.mjs | 10 ++-------- packages/nitro-utils/package.json | 2 +- packages/nitro-utils/rollup.npm.config.mjs | 3 +-- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/dev-packages/rollup-utils/npmHelpers.mjs b/dev-packages/rollup-utils/npmHelpers.mjs index 682350bf2d1e..4e6483364ee4 100644 --- a/dev-packages/rollup-utils/npmHelpers.mjs +++ b/dev-packages/rollup-utils/npmHelpers.mjs @@ -132,15 +132,9 @@ export function makeBaseNPMConfig(options = {}) { } export function makeNPMConfigVariants(baseConfig, options = {}) { - const { emitEsm = true, emitCjs = true } = options; + const { emitEsm = true } = options; - const variantSpecificConfigs = []; - - if (emitCjs) { - variantSpecificConfigs.push({ - output: { format: 'cjs', dir: path.join(baseConfig.output.dir, 'cjs') }, - }); - } + const variantSpecificConfigs = [{ output: { format: 'cjs', dir: path.join(baseConfig.output.dir, 'cjs') } }]; if (emitEsm) { variantSpecificConfigs.push({ diff --git a/packages/nitro-utils/package.json b/packages/nitro-utils/package.json index d3f9dfc76cc5..a903abc6e5a8 100644 --- a/packages/nitro-utils/package.json +++ b/packages/nitro-utils/package.json @@ -7,7 +7,7 @@ "author": "Sentry", "license": "MIT", "engines": { - "node": ">=14.18" + "node": ">=16.20" }, "files": [ "/build" diff --git a/packages/nitro-utils/rollup.npm.config.mjs b/packages/nitro-utils/rollup.npm.config.mjs index 648dd9b794f4..14d242366d04 100644 --- a/packages/nitro-utils/rollup.npm.config.mjs +++ b/packages/nitro-utils/rollup.npm.config.mjs @@ -13,6 +13,5 @@ export default makeNPMConfigVariants( : Boolean(process.env.SENTRY_BUILD_PRESERVE_MODULES), }, }, - }), - { emitCjs: false }, + }) ); From 31dfc08ae75a4d74389ed7125fd3e38d447e376b Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Fri, 8 Nov 2024 15:26:51 +0100 Subject: [PATCH 09/18] fix biome --- packages/nitro-utils/rollup.npm.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nitro-utils/rollup.npm.config.mjs b/packages/nitro-utils/rollup.npm.config.mjs index 14d242366d04..d28a7a6f54a0 100644 --- a/packages/nitro-utils/rollup.npm.config.mjs +++ b/packages/nitro-utils/rollup.npm.config.mjs @@ -13,5 +13,5 @@ export default makeNPMConfigVariants( : Boolean(process.env.SENTRY_BUILD_PRESERVE_MODULES), }, }, - }) + }), ); From cf786740bb3a900346e71840636bd4d3b283d836 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Fri, 8 Nov 2024 15:54:50 +0100 Subject: [PATCH 10/18] build cjs --- packages/nitro-utils/package.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/nitro-utils/package.json b/packages/nitro-utils/package.json index a903abc6e5a8..21e0812c138f 100644 --- a/packages/nitro-utils/package.json +++ b/packages/nitro-utils/package.json @@ -12,7 +12,8 @@ "files": [ "/build" ], - "main": "build/esm/index.js", + "main": "build/cjs/index.js", + "module": "build/esm/index.js", "types": "build/types/index.d.ts", "exports": { "./package.json": "./package.json", @@ -20,6 +21,10 @@ "import": { "types": "./build/types/index.d.ts", "default": "./build/esm/index.js" + }, + "require": { + "types": "./build/types/index.d.ts", + "default": "./build/cjs/index.js" } } }, From e4b92432013e84c02a7e34bf34505b626d19e065 Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Wed, 13 Nov 2024 11:01:46 +0100 Subject: [PATCH 11/18] Add nullbyte prefix to rollup plugin to ensure correct bundling --- .../wrapServerEntryWithDynamicImport.ts | 14 ++++++++++---- .../wrapServerEntryWithDynamicImport.test.ts | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts b/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts index 8c4ad34b1b35..7b7db391950c 100644 --- a/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts +++ b/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts @@ -37,6 +37,12 @@ export function wrapServerEntryWithDynamicImport(config: WrapServerEntryPluginOp debug, } = config; + // In order to correctly import the server config file + // and dynamically import the nitro runtime, we need to + // mark the resolutionId with '\0raw' to fall into the + // raw chunk group, c.f. https://github.com/nitrojs/nitro/commit/8b4a408231bdc222569a32ce109796a41eac4aa6#diff-e58102d2230f95ddeef2662957b48d847a6e891e354cfd0ae6e2e03ce848d1a2R142 + const resolutionIdPrefix = '\0raw'; + return { name: 'sentry-wrap-server-entry-with-dynamic-import', async resolveId(source, importer, options) { @@ -70,19 +76,19 @@ export function wrapServerEntryWithDynamicImport(config: WrapServerEntryPluginOp // The enclosing `if` already checks for the suffix in `source`, but a check in `resolution.id` is needed as well to prevent multiple attachment of the suffix return resolution.id.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`) ? resolution.id - : resolution.id + : `${resolutionIdPrefix}${resolution.id // Concatenates the query params to mark the file (also attaches names of re-exports - this is needed for serverless functions to re-export the handler) .concat(SENTRY_WRAPPED_ENTRY) .concat( constructWrappedFunctionExportQuery(moduleInfo.exportedBindings, entrypointWrappedFunctions, debug), ) - .concat(QUERY_END_INDICATOR); + .concat(QUERY_END_INDICATOR)}`; } return null; }, load(id: string) { if (id.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`)) { - const entryId = removeSentryQueryFromPath(id); + const entryId = removeSentryQueryFromPath(id).slice(resolutionIdPrefix.length); // Mostly useful for serverless `handler` functions const reExportedFunctions = @@ -188,7 +194,7 @@ export function constructWrappedFunctionExportQuery( consoleSandbox(() => // eslint-disable-next-line no-console console.warn( - "[Sentry] No functions found to wrap. In case the server needs to export async functions other than `handler` or `server`, consider adding the name(s) to Sentry's build options `sentry.entrypointWrappedFunctions` in `nuxt.config.ts`.", + '[Sentry] No functions found to wrap. In case the server needs to export async functions other than `handler` or `server`, consider adding the name(s) to `entrypointWrappedFunctions`.', ), ); } diff --git a/packages/nitro-utils/test/rollupPlugins/wrapServerEntryWithDynamicImport.test.ts b/packages/nitro-utils/test/rollupPlugins/wrapServerEntryWithDynamicImport.test.ts index 1ef8b8d0224c..c13973cc4afe 100644 --- a/packages/nitro-utils/test/rollupPlugins/wrapServerEntryWithDynamicImport.test.ts +++ b/packages/nitro-utils/test/rollupPlugins/wrapServerEntryWithDynamicImport.test.ts @@ -92,7 +92,7 @@ describe('constructWrappedFunctionExportQuery', () => { const result = constructWrappedFunctionExportQuery(exportedBindings, entrypointWrappedFunctions, debug); expect(result).toBe('?sentry-query-reexported-functions=handler'); expect(consoleWarnSpy).toHaveBeenCalledWith( - "[Sentry] No functions found to wrap. In case the server needs to export async functions other than `handler` or `server`, consider adding the name(s) to Sentry's build options `sentry.entrypointWrappedFunctions` in `nuxt.config.ts`.", + '[Sentry] No functions found to wrap. In case the server needs to export async functions other than `handler` or `server`, consider adding the name(s) to `entrypointWrappedFunctions`.', ); consoleWarnSpy.mockRestore(); From a0a480c4f29240cbd38dd370bab9cd6ec0063da1 Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Wed, 13 Nov 2024 11:05:05 +0100 Subject: [PATCH 12/18] Remove npm badges from readme --- packages/nitro-utils/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/nitro-utils/README.md b/packages/nitro-utils/README.md index 304aa0961836..321352938655 100644 --- a/packages/nitro-utils/README.md +++ b/packages/nitro-utils/README.md @@ -6,10 +6,6 @@ # Sentry Utilities for Nitro-based SDKs -[![npm version](https://img.shields.io/npm/v/@sentry-internal/nitro-utils.svg)](https://www.npmjs.com/package/@sentry-internal/nitro-utils) -[![npm dm](https://img.shields.io/npm/dm/@sentry-internal/nitro-utils.svg)](https://www.npmjs.com/package/@sentry-internal/nitro-utils) -[![npm dt](https://img.shields.io/npm/dt/@sentry-internal/nitro-utils.svg)](https://www.npmjs.com/package/@sentry-internal/nitro-utils) - ## Links - [Official SDK Docs](https://docs.sentry.io/quickstart/) From dcad00ff8b8bb3be34e061c05150e86b00e4fa10 Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Wed, 13 Nov 2024 11:07:45 +0100 Subject: [PATCH 13/18] Remove unused @sentry/types dep --- packages/nitro-utils/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/nitro-utils/package.json b/packages/nitro-utils/package.json index 21e0812c138f..5ddcfc17b431 100644 --- a/packages/nitro-utils/package.json +++ b/packages/nitro-utils/package.json @@ -39,7 +39,6 @@ "access": "public" }, "dependencies": { - "@sentry/types": "8.37.1", "@sentry/utils": "8.37.1" }, "devDependencies": { From 1205ade4b759bb5a5b256e856870255d666d5b95 Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Mon, 25 Nov 2024 14:49:53 +0100 Subject: [PATCH 14/18] Remove `flatten` call --- .../wrapServerEntryWithDynamicImport.ts | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts b/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts index 7b7db391950c..8bd3b3d939e4 100644 --- a/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts +++ b/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts @@ -1,4 +1,4 @@ -import { consoleSandbox, flatten } from '@sentry/utils'; +import { consoleSandbox } from '@sentry/core'; import type { InputPluginOption } from 'rollup'; export const SENTRY_WRAPPED_ENTRY = '?sentry-query-wrapped-entry'; @@ -176,18 +176,21 @@ export function constructWrappedFunctionExportQuery( entrypointWrappedFunctions: string[], debug?: boolean, ): string { + const functionsToExport: { wrap: string[]; reexport: string[] } = { + wrap: [], + reexport: [], + }; + // `exportedBindings` can look like this: `{ '.': [ 'handler' ] }` or `{ '.': [], './firebase-gen-1.mjs': [ 'server' ] }` // The key `.` refers to exports within the current file, while other keys show from where exports were imported first. - const functionsToExport = flatten(Object.values(exportedBindings || {})).reduce( - (functions, currFunctionName) => { - if (entrypointWrappedFunctions.includes(currFunctionName)) { - functions.wrap.push(currFunctionName); + Object.values(exportedBindings || {}).forEach(functions => + functions.forEach(fn => { + if (entrypointWrappedFunctions.includes(fn)) { + functionsToExport.wrap.push(fn); } else { - functions.reexport.push(currFunctionName); + functionsToExport.reexport.push(fn); } - return functions; - }, - { wrap: [], reexport: [] } as { wrap: string[]; reexport: string[] }, + }), ); if (debug && functionsToExport.wrap.length === 0) { From d893bb4204a09da157b868ca642eccd92445350a Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Mon, 25 Nov 2024 16:21:10 +0100 Subject: [PATCH 15/18] update sentry packages --- packages/nitro-utils/package.json | 4 ++-- packages/nuxt/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nitro-utils/package.json b/packages/nitro-utils/package.json index 5ddcfc17b431..526c099c3805 100644 --- a/packages/nitro-utils/package.json +++ b/packages/nitro-utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/nitro-utils", - "version": "8.37.1", + "version": "8.40.0", "description": "Utilities for all Sentry SDKs with Nitro on the server-side", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nitro-utils", @@ -39,7 +39,7 @@ "access": "public" }, "dependencies": { - "@sentry/utils": "8.37.1" + "@sentry/core": "8.40.0" }, "devDependencies": { "rollup": "^4.24.4" diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 560c6aa4be3d..b00a930d69c8 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -54,7 +54,7 @@ }, "devDependencies": { "@nuxt/module-builder": "^0.8.4", - "@sentry-internal/nitro-utils": "8.37.1", + "@sentry-internal/nitro-utils": "8.40.0", "nuxt": "^3.13.2" }, "scripts": { From 199e5a6c337a26a2b48f3fa503d28b3dbb9b595b Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Tue, 3 Dec 2024 09:21:14 +0100 Subject: [PATCH 16/18] update versions Co-authored-by: Andrei <168741329+andreiborza@users.noreply.github.com> --- packages/nitro-utils/package.json | 4 ++-- packages/nuxt/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nitro-utils/package.json b/packages/nitro-utils/package.json index 526c099c3805..0e024b525f61 100644 --- a/packages/nitro-utils/package.json +++ b/packages/nitro-utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/nitro-utils", - "version": "8.40.0", + "version": "8.42.0", "description": "Utilities for all Sentry SDKs with Nitro on the server-side", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nitro-utils", @@ -39,7 +39,7 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.40.0" + "@sentry/core": "8.42.0" }, "devDependencies": { "rollup": "^4.24.4" diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index b00a930d69c8..fe8477ca82dc 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -54,7 +54,7 @@ }, "devDependencies": { "@nuxt/module-builder": "^0.8.4", - "@sentry-internal/nitro-utils": "8.40.0", + "@sentry-internal/nitro-utils": "8.42.0", "nuxt": "^3.13.2" }, "scripts": { From 16a58c947000f612aac32c3d9dac998bea618326 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Tue, 3 Dec 2024 17:24:09 +0100 Subject: [PATCH 17/18] feat(nuxt): Use nitro-utils package --- package.json | 1 - packages/nitro-utils/.eslintrc.js | 21 -- packages/nitro-utils/LICENSE | 21 -- packages/nitro-utils/README.md | 19 -- packages/nitro-utils/package.json | 71 ----- packages/nitro-utils/rollup.npm.config.mjs | 17 -- packages/nitro-utils/src/index.ts | 4 - .../wrapServerEntryWithDynamicImport.ts | 242 ------------------ .../wrapServerEntryWithDynamicImport.test.ts | 193 -------------- packages/nitro-utils/test/tsconfig.json | 3 - packages/nitro-utils/test/vitest.setup.ts | 8 - packages/nitro-utils/tsconfig.json | 9 - packages/nitro-utils/tsconfig.test.json | 10 - packages/nitro-utils/tsconfig.types.json | 10 - packages/nitro-utils/vite.config.ts | 9 - scripts/ci-unit-tests.ts | 3 +- 16 files changed, 1 insertion(+), 640 deletions(-) delete mode 100644 packages/nitro-utils/.eslintrc.js delete mode 100644 packages/nitro-utils/LICENSE delete mode 100644 packages/nitro-utils/README.md delete mode 100644 packages/nitro-utils/package.json delete mode 100644 packages/nitro-utils/rollup.npm.config.mjs delete mode 100644 packages/nitro-utils/src/index.ts delete mode 100644 packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts delete mode 100644 packages/nitro-utils/test/rollupPlugins/wrapServerEntryWithDynamicImport.test.ts delete mode 100644 packages/nitro-utils/test/tsconfig.json delete mode 100644 packages/nitro-utils/test/vitest.setup.ts delete mode 100644 packages/nitro-utils/tsconfig.json delete mode 100644 packages/nitro-utils/tsconfig.test.json delete mode 100644 packages/nitro-utils/tsconfig.types.json delete mode 100644 packages/nitro-utils/vite.config.ts diff --git a/package.json b/package.json index 780c68c65f33..59d4fb4acdb6 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,6 @@ "packages/integration-shims", "packages/nestjs", "packages/nextjs", - "packages/nitro-utils", "packages/node", "packages/nuxt", "packages/opentelemetry", diff --git a/packages/nitro-utils/.eslintrc.js b/packages/nitro-utils/.eslintrc.js deleted file mode 100644 index 3849c1ee28a6..000000000000 --- a/packages/nitro-utils/.eslintrc.js +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = { - extends: ['../../.eslintrc.js'], - env: { - node: true, - }, - overrides: [ - { - files: ['src/**'], - rules: { - '@sentry-internal/sdk/no-optional-chaining': 'off', - }, - }, - { - files: ['src/metrics/**'], - rules: { - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', - }, - }, - ], -}; diff --git a/packages/nitro-utils/LICENSE b/packages/nitro-utils/LICENSE deleted file mode 100644 index 5af93a5bdae5..000000000000 --- a/packages/nitro-utils/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020-2024 Functional Software, Inc. dba Sentry - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/nitro-utils/README.md b/packages/nitro-utils/README.md deleted file mode 100644 index 321352938655..000000000000 --- a/packages/nitro-utils/README.md +++ /dev/null @@ -1,19 +0,0 @@ -

- - Sentry - -

- -# Sentry Utilities for Nitro-based SDKs - -## Links - -- [Official SDK Docs](https://docs.sentry.io/quickstart/) -- [TypeDoc](http://getsentry.github.io/sentry-node/) - -## General - -Common utilities used by Sentry SDKs that use Nitro on the server-side. - -Note: This package is only meant to be used internally, and as such is not part of our public API contract and does not -follow semver. diff --git a/packages/nitro-utils/package.json b/packages/nitro-utils/package.json deleted file mode 100644 index 0e024b525f61..000000000000 --- a/packages/nitro-utils/package.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "name": "@sentry-internal/nitro-utils", - "version": "8.42.0", - "description": "Utilities for all Sentry SDKs with Nitro on the server-side", - "repository": "git://github.com/getsentry/sentry-javascript.git", - "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nitro-utils", - "author": "Sentry", - "license": "MIT", - "engines": { - "node": ">=16.20" - }, - "files": [ - "/build" - ], - "main": "build/cjs/index.js", - "module": "build/esm/index.js", - "types": "build/types/index.d.ts", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./build/types/index.d.ts", - "default": "./build/esm/index.js" - }, - "require": { - "types": "./build/types/index.d.ts", - "default": "./build/cjs/index.js" - } - } - }, - "typesVersions": { - "<4.9": { - "build/types/index.d.ts": [ - "build/types-ts3.8/index.d.ts" - ] - } - }, - "publishConfig": { - "access": "public" - }, - "dependencies": { - "@sentry/core": "8.42.0" - }, - "devDependencies": { - "rollup": "^4.24.4" - }, - "scripts": { - "build": "run-p build:transpile build:types", - "build:dev": "yarn build", - "build:transpile": "rollup -c rollup.npm.config.mjs", - "build:types": "run-s build:types:core build:types:downlevel", - "build:types:core": "tsc -p tsconfig.types.json", - "build:types:downlevel": "yarn downlevel-dts build/types build/types-ts3.8 --to ts3.8", - "build:watch": "run-p build:transpile:watch build:types:watch", - "build:dev:watch": "run-p build:transpile:watch build:types:watch", - "build:transpile:watch": "rollup -c rollup.npm.config.mjs --watch", - "build:types:watch": "tsc -p tsconfig.types.json --watch", - "build:tarball": "npm pack", - "clean": "rimraf build coverage sentry-internal-nitro-utils-*.tgz", - "fix": "eslint . --format stylish --fix", - "lint": "eslint . --format stylish", - "test": "yarn test:unit", - "test:unit": "vitest run", - "test:watch": "vitest --watch", - "yalc:publish": "yalc publish --push --sig" - }, - "volta": { - "extends": "../../package.json" - }, - "sideEffects": false -} diff --git a/packages/nitro-utils/rollup.npm.config.mjs b/packages/nitro-utils/rollup.npm.config.mjs deleted file mode 100644 index d28a7a6f54a0..000000000000 --- a/packages/nitro-utils/rollup.npm.config.mjs +++ /dev/null @@ -1,17 +0,0 @@ -import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollup-utils'; - -export default makeNPMConfigVariants( - makeBaseNPMConfig({ - packageSpecificConfig: { - output: { - // set exports to 'named' or 'auto' so that rollup doesn't warn - exports: 'named', - // set preserveModules to true because we don't want to bundle everything into one file. - preserveModules: - process.env.SENTRY_BUILD_PRESERVE_MODULES === undefined - ? true - : Boolean(process.env.SENTRY_BUILD_PRESERVE_MODULES), - }, - }, - }), -); diff --git a/packages/nitro-utils/src/index.ts b/packages/nitro-utils/src/index.ts deleted file mode 100644 index 9aa98faf5d62..000000000000 --- a/packages/nitro-utils/src/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { - wrapServerEntryWithDynamicImport, - type WrapServerEntryPluginOptions, -} from './rollupPlugins/wrapServerEntryWithDynamicImport'; diff --git a/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts b/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts deleted file mode 100644 index 8bd3b3d939e4..000000000000 --- a/packages/nitro-utils/src/rollupPlugins/wrapServerEntryWithDynamicImport.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { consoleSandbox } from '@sentry/core'; -import type { InputPluginOption } from 'rollup'; - -export const SENTRY_WRAPPED_ENTRY = '?sentry-query-wrapped-entry'; -export const SENTRY_WRAPPED_FUNCTIONS = '?sentry-query-wrapped-functions='; -export const SENTRY_REEXPORTED_FUNCTIONS = '?sentry-query-reexported-functions='; -export const QUERY_END_INDICATOR = 'SENTRY-QUERY-END'; - -export type WrapServerEntryPluginOptions = { - serverEntrypointFileName: string; - serverConfigFileName: string; - resolvedServerConfigPath: string; - entrypointWrappedFunctions: string[]; - additionalImports?: string[]; - debug?: boolean; -}; - -/** - * A Rollup plugin which wraps the server entry with a dynamic `import()`. This makes it possible to initialize Sentry first - * by using a regular `import` and load the server after that. - * This also works with serverless `handler` functions, as it re-exports the `handler`. - * - * @param config Configuration options for the Rollup Plugin - * @param config.serverConfigFileName Name of the Sentry server config (without file extension). E.g. 'sentry.server.config' - * @param config.resolvedServerConfigPath Resolved path of the Sentry server config (based on `src` directory) - * @param config.entryPointWrappedFunctions Exported bindings of the server entry file, which are wrapped as async function. E.g. ['default', 'handler', 'server'] - * @param config.additionalImports Adds additional imports to the entry file. Can be e.g. 'import-in-the-middle/hook.mjs' - * @param config.debug Whether debug logs are enabled in the build time environment - */ -export function wrapServerEntryWithDynamicImport(config: WrapServerEntryPluginOptions): InputPluginOption { - const { - serverEntrypointFileName, - serverConfigFileName, - resolvedServerConfigPath, - entrypointWrappedFunctions, - additionalImports, - debug, - } = config; - - // In order to correctly import the server config file - // and dynamically import the nitro runtime, we need to - // mark the resolutionId with '\0raw' to fall into the - // raw chunk group, c.f. https://github.com/nitrojs/nitro/commit/8b4a408231bdc222569a32ce109796a41eac4aa6#diff-e58102d2230f95ddeef2662957b48d847a6e891e354cfd0ae6e2e03ce848d1a2R142 - const resolutionIdPrefix = '\0raw'; - - return { - name: 'sentry-wrap-server-entry-with-dynamic-import', - async resolveId(source, importer, options) { - if (source.includes(`/${serverConfigFileName}`)) { - return { id: source, moduleSideEffects: true }; - } - - if (additionalImports && additionalImports.includes(source)) { - // When importing additional imports like "import-in-the-middle/hook.mjs" in the returned code of the `load()` function below: - // By setting `moduleSideEffects` to `true`, the import is added to the bundle, although nothing is imported from it - // By importing "import-in-the-middle/hook.mjs", we can make sure this file is included, as not all node builders are including files imported with `module.register()`. - // Prevents the error "Failed to register ESM hook Error: Cannot find module 'import-in-the-middle/hook.mjs'" - return { id: source, moduleSideEffects: true, external: true }; - } - - if ( - options.isEntry && - source.includes(serverEntrypointFileName) && - source.includes('.mjs') && - !source.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`) - ) { - const resolution = await this.resolve(source, importer, options); - - // If it cannot be resolved or is external, just return it so that Rollup can display an error - if (!resolution || (resolution && resolution.external)) return resolution; - - const moduleInfo = await this.load(resolution); - - moduleInfo.moduleSideEffects = true; - - // The enclosing `if` already checks for the suffix in `source`, but a check in `resolution.id` is needed as well to prevent multiple attachment of the suffix - return resolution.id.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`) - ? resolution.id - : `${resolutionIdPrefix}${resolution.id - // Concatenates the query params to mark the file (also attaches names of re-exports - this is needed for serverless functions to re-export the handler) - .concat(SENTRY_WRAPPED_ENTRY) - .concat( - constructWrappedFunctionExportQuery(moduleInfo.exportedBindings, entrypointWrappedFunctions, debug), - ) - .concat(QUERY_END_INDICATOR)}`; - } - return null; - }, - load(id: string) { - if (id.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`)) { - const entryId = removeSentryQueryFromPath(id).slice(resolutionIdPrefix.length); - - // Mostly useful for serverless `handler` functions - const reExportedFunctions = - id.includes(SENTRY_WRAPPED_FUNCTIONS) || id.includes(SENTRY_REEXPORTED_FUNCTIONS) - ? constructFunctionReExport(id, entryId) - : ''; - - return ( - // Regular `import` of the Sentry config - `import ${JSON.stringify(resolvedServerConfigPath)};\n` + - // Dynamic `import()` for the previous, actual entry point. - // `import()` can be used for any code that should be run after the hooks are registered (https://nodejs.org/api/module.html#enabling) - `import(${JSON.stringify(entryId)});\n` + - // By importing additional imports like "import-in-the-middle/hook.mjs", we can make sure this file wil be included, as not all node builders are including files imported with `module.register()`. - `${additionalImports ? additionalImports.map(importPath => `import "${importPath}";\n`) : ''}` + - `${reExportedFunctions}\n` - ); - } - - return null; - }, - }; -} - -/** - * Strips the Sentry query part from a path. - * Example: example/path?sentry-query-wrapped-entry?sentry-query-functions-reexport=foo,SENTRY-QUERY-END -> /example/path - * - * **Only exported for testing** - */ -export function removeSentryQueryFromPath(url: string): string { - // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor - const regex = new RegExp(`\\${SENTRY_WRAPPED_ENTRY}.*?\\${QUERY_END_INDICATOR}`); - return url.replace(regex, ''); -} - -/** - * Extracts and sanitizes function re-export and function wrap query parameters from a query string. - * If it is a default export, it is not considered for re-exporting. - * - * **Only exported for testing** - */ -export function extractFunctionReexportQueryParameters(query: string): { wrap: string[]; reexport: string[] } { - // Regex matches the comma-separated params between the functions query - // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor - const wrapRegex = new RegExp( - `\\${SENTRY_WRAPPED_FUNCTIONS}(.*?)(\\${QUERY_END_INDICATOR}|\\${SENTRY_REEXPORTED_FUNCTIONS})`, - ); - // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor - const reexportRegex = new RegExp(`\\${SENTRY_REEXPORTED_FUNCTIONS}(.*?)(\\${QUERY_END_INDICATOR})`); - - const wrapMatch = query.match(wrapRegex); - const reexportMatch = query.match(reexportRegex); - - const wrap = - wrapMatch && wrapMatch[1] - ? wrapMatch[1] - .split(',') - .filter(param => param !== '') - // Sanitize, as code could be injected with another rollup plugin - .map((str: string) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) - : []; - - const reexport = - reexportMatch && reexportMatch[1] - ? reexportMatch[1] - .split(',') - .filter(param => param !== '' && param !== 'default') - // Sanitize, as code could be injected with another rollup plugin - .map((str: string) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) - : []; - - return { wrap, reexport }; -} - -/** - * Constructs a comma-separated string with all functions that need to be re-exported later from the server entry. - * It uses Rollup's `exportedBindings` to determine the functions to re-export. Functions which should be wrapped - * (e.g. serverless handlers) are wrapped by Sentry. - * - * **Only exported for testing** - */ -export function constructWrappedFunctionExportQuery( - exportedBindings: Record | null, - entrypointWrappedFunctions: string[], - debug?: boolean, -): string { - const functionsToExport: { wrap: string[]; reexport: string[] } = { - wrap: [], - reexport: [], - }; - - // `exportedBindings` can look like this: `{ '.': [ 'handler' ] }` or `{ '.': [], './firebase-gen-1.mjs': [ 'server' ] }` - // The key `.` refers to exports within the current file, while other keys show from where exports were imported first. - Object.values(exportedBindings || {}).forEach(functions => - functions.forEach(fn => { - if (entrypointWrappedFunctions.includes(fn)) { - functionsToExport.wrap.push(fn); - } else { - functionsToExport.reexport.push(fn); - } - }), - ); - - if (debug && functionsToExport.wrap.length === 0) { - consoleSandbox(() => - // eslint-disable-next-line no-console - console.warn( - '[Sentry] No functions found to wrap. In case the server needs to export async functions other than `handler` or `server`, consider adding the name(s) to `entrypointWrappedFunctions`.', - ), - ); - } - - const wrapQuery = functionsToExport.wrap.length - ? `${SENTRY_WRAPPED_FUNCTIONS}${functionsToExport.wrap.join(',')}` - : ''; - const reexportQuery = functionsToExport.reexport.length - ? `${SENTRY_REEXPORTED_FUNCTIONS}${functionsToExport.reexport.join(',')}` - : ''; - - return [wrapQuery, reexportQuery].join(''); -} - -/** - * Constructs a code snippet with function reexports (can be used in Rollup plugins as a return value for `load()`) - * - * **Only exported for testing** - */ -export function constructFunctionReExport(pathWithQuery: string, entryId: string): string { - const { wrap: wrapFunctions, reexport: reexportFunctions } = extractFunctionReexportQueryParameters(pathWithQuery); - - return wrapFunctions - .reduce( - (functionsCode, currFunctionName) => - functionsCode.concat( - `async function ${currFunctionName}_sentryWrapped(...args) {\n` + - ` const res = await import(${JSON.stringify(entryId)});\n` + - ` return res.${currFunctionName}.call(this, ...args);\n` + - '}\n' + - `export { ${currFunctionName}_sentryWrapped as ${currFunctionName} };\n`, - ), - '', - ) - .concat( - reexportFunctions.reduce( - (functionsCode, currFunctionName) => - functionsCode.concat(`export { ${currFunctionName} } from ${JSON.stringify(entryId)};`), - '', - ), - ); -} diff --git a/packages/nitro-utils/test/rollupPlugins/wrapServerEntryWithDynamicImport.test.ts b/packages/nitro-utils/test/rollupPlugins/wrapServerEntryWithDynamicImport.test.ts deleted file mode 100644 index c13973cc4afe..000000000000 --- a/packages/nitro-utils/test/rollupPlugins/wrapServerEntryWithDynamicImport.test.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { describe, expect, it, vi } from 'vitest'; -import { - QUERY_END_INDICATOR, - SENTRY_REEXPORTED_FUNCTIONS, - SENTRY_WRAPPED_ENTRY, - SENTRY_WRAPPED_FUNCTIONS, - constructFunctionReExport, - constructWrappedFunctionExportQuery, - extractFunctionReexportQueryParameters, - removeSentryQueryFromPath, -} from '../../src/rollupPlugins/wrapServerEntryWithDynamicImport'; - -describe('removeSentryQueryFromPath', () => { - it('strips the Sentry query part from the path', () => { - const url = `/example/path${SENTRY_WRAPPED_ENTRY}${SENTRY_WRAPPED_FUNCTIONS}foo,${QUERY_END_INDICATOR}`; - const url2 = `/example/path${SENTRY_WRAPPED_ENTRY}${QUERY_END_INDICATOR}`; - const result = removeSentryQueryFromPath(url); - const result2 = removeSentryQueryFromPath(url2); - expect(result).toBe('/example/path'); - expect(result2).toBe('/example/path'); - }); - - it('returns the same path if the specific query part is not present', () => { - const url = '/example/path?other-query=param'; - const result = removeSentryQueryFromPath(url); - expect(result).toBe(url); - }); -}); - -describe('extractFunctionReexportQueryParameters', () => { - it.each([ - [`${SENTRY_WRAPPED_FUNCTIONS}foo,bar,${QUERY_END_INDICATOR}`, { wrap: ['foo', 'bar'], reexport: [] }], - [ - `${SENTRY_WRAPPED_FUNCTIONS}foo,bar,default${QUERY_END_INDICATOR}`, - { wrap: ['foo', 'bar', 'default'], reexport: [] }, - ], - [ - `${SENTRY_WRAPPED_FUNCTIONS}foo,a.b*c?d[e]f(g)h|i\\\\j(){hello},${QUERY_END_INDICATOR}`, - { wrap: ['foo', 'a\\.b\\*c\\?d\\[e\\]f\\(g\\)h\\|i\\\\\\\\j\\(\\)\\{hello\\}'], reexport: [] }, - ], - [`/example/path/${SENTRY_WRAPPED_FUNCTIONS}foo,bar${QUERY_END_INDICATOR}`, { wrap: ['foo', 'bar'], reexport: [] }], - [ - `${SENTRY_WRAPPED_FUNCTIONS}foo,bar,${SENTRY_REEXPORTED_FUNCTIONS}${QUERY_END_INDICATOR}`, - { wrap: ['foo', 'bar'], reexport: [] }, - ], - [`${SENTRY_REEXPORTED_FUNCTIONS}${QUERY_END_INDICATOR}`, { wrap: [], reexport: [] }], - [ - `/path${SENTRY_WRAPPED_FUNCTIONS}foo,bar${SENTRY_REEXPORTED_FUNCTIONS}bar${QUERY_END_INDICATOR}`, - { wrap: ['foo', 'bar'], reexport: ['bar'] }, - ], - ['?other-query=param', { wrap: [], reexport: [] }], - ])('extracts parameters from the query string: %s', (query, expected) => { - const result = extractFunctionReexportQueryParameters(query); - expect(result).toEqual(expected); - }); -}); - -describe('constructWrappedFunctionExportQuery', () => { - it.each([ - [{ '.': ['handler'] }, ['handler'], `${SENTRY_WRAPPED_FUNCTIONS}handler`], - [{ '.': ['handler'], './module': ['server'] }, [], `${SENTRY_REEXPORTED_FUNCTIONS}handler,server`], - [ - { '.': ['handler'], './module': ['server'] }, - ['server'], - `${SENTRY_WRAPPED_FUNCTIONS}server${SENTRY_REEXPORTED_FUNCTIONS}handler`, - ], - [ - { '.': ['handler', 'otherFunction'] }, - ['handler'], - `${SENTRY_WRAPPED_FUNCTIONS}handler${SENTRY_REEXPORTED_FUNCTIONS}otherFunction`, - ], - [{ '.': ['handler', 'otherFn'] }, ['handler', 'otherFn'], `${SENTRY_WRAPPED_FUNCTIONS}handler,otherFn`], - [{ '.': ['bar'], './module': ['foo'] }, ['bar', 'foo'], `${SENTRY_WRAPPED_FUNCTIONS}bar,foo`], - [{ '.': ['foo', 'bar'] }, ['foo'], `${SENTRY_WRAPPED_FUNCTIONS}foo${SENTRY_REEXPORTED_FUNCTIONS}bar`], - [{ '.': ['foo', 'bar'] }, ['bar'], `${SENTRY_WRAPPED_FUNCTIONS}bar${SENTRY_REEXPORTED_FUNCTIONS}foo`], - [{ '.': ['foo', 'bar'] }, ['foo', 'bar'], `${SENTRY_WRAPPED_FUNCTIONS}foo,bar`], - [{ '.': ['foo', 'bar'] }, [], `${SENTRY_REEXPORTED_FUNCTIONS}foo,bar`], - ])( - 'constructs re-export query for exportedBindings: %j and entrypointWrappedFunctions: %j', - (exportedBindings, entrypointWrappedFunctions, expected) => { - const result = constructWrappedFunctionExportQuery(exportedBindings, entrypointWrappedFunctions); - expect(result).toBe(expected); - }, - ); - - it('logs a warning if no functions are found for re-export and debug is true', () => { - const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); - const exportedBindings = { '.': ['handler'] }; - const entrypointWrappedFunctions = ['nonExistentFunction']; - const debug = true; - - const result = constructWrappedFunctionExportQuery(exportedBindings, entrypointWrappedFunctions, debug); - expect(result).toBe('?sentry-query-reexported-functions=handler'); - expect(consoleWarnSpy).toHaveBeenCalledWith( - '[Sentry] No functions found to wrap. In case the server needs to export async functions other than `handler` or `server`, consider adding the name(s) to `entrypointWrappedFunctions`.', - ); - - consoleWarnSpy.mockRestore(); - }); -}); - -describe('constructFunctionReExport', () => { - it('constructs re-export code for given query parameters and entry ID', () => { - const query = `${SENTRY_WRAPPED_FUNCTIONS}foo,bar,${QUERY_END_INDICATOR}}`; - const query2 = `${SENTRY_WRAPPED_FUNCTIONS}foo,bar${QUERY_END_INDICATOR}}`; - const entryId = './module'; - const result = constructFunctionReExport(query, entryId); - const result2 = constructFunctionReExport(query2, entryId); - - const expected = ` -async function foo_sentryWrapped(...args) { - const res = await import("./module"); - return res.foo.call(this, ...args); -} -export { foo_sentryWrapped as foo }; -async function bar_sentryWrapped(...args) { - const res = await import("./module"); - return res.bar.call(this, ...args); -} -export { bar_sentryWrapped as bar }; -`; - expect(result.trim()).toBe(expected.trim()); - expect(result2.trim()).toBe(expected.trim()); - }); - - it('constructs re-export code for a "default" query parameters and entry ID', () => { - const query = `${SENTRY_WRAPPED_FUNCTIONS}default${QUERY_END_INDICATOR}}`; - const entryId = './index'; - const result = constructFunctionReExport(query, entryId); - - const expected = ` -async function default_sentryWrapped(...args) { - const res = await import("./index"); - return res.default.call(this, ...args); -} -export { default_sentryWrapped as default }; -`; - expect(result.trim()).toBe(expected.trim()); - }); - - it('constructs re-export code for a "default" query parameters and entry ID', () => { - const query = `${SENTRY_WRAPPED_FUNCTIONS}default${QUERY_END_INDICATOR}}`; - const entryId = './index'; - const result = constructFunctionReExport(query, entryId); - - const expected = ` -async function default_sentryWrapped(...args) { - const res = await import("./index"); - return res.default.call(this, ...args); -} -export { default_sentryWrapped as default }; -`; - expect(result.trim()).toBe(expected.trim()); - }); - - it('constructs re-export code for a mix of wrapped and re-exported functions', () => { - const query = `${SENTRY_WRAPPED_FUNCTIONS}foo,${SENTRY_REEXPORTED_FUNCTIONS}bar${QUERY_END_INDICATOR}`; - const entryId = './module'; - const result = constructFunctionReExport(query, entryId); - - const expected = ` -async function foo_sentryWrapped(...args) { - const res = await import("./module"); - return res.foo.call(this, ...args); -} -export { foo_sentryWrapped as foo }; -export { bar } from "./module"; -`; - expect(result.trim()).toBe(expected.trim()); - }); - - it('does not re-export a default export for regular re-exported functions', () => { - const query = `${SENTRY_WRAPPED_FUNCTIONS}foo${SENTRY_REEXPORTED_FUNCTIONS}default${QUERY_END_INDICATOR}`; - const entryId = './module'; - const result = constructFunctionReExport(query, entryId); - - const expected = ` -async function foo_sentryWrapped(...args) { - const res = await import("./module"); - return res.foo.call(this, ...args); -} -export { foo_sentryWrapped as foo }; -`; - expect(result.trim()).toBe(expected.trim()); - }); - - it('returns an empty string if the query string is empty', () => { - const query = ''; - const entryId = './module'; - const result = constructFunctionReExport(query, entryId); - expect(result).toBe(''); - }); -}); diff --git a/packages/nitro-utils/test/tsconfig.json b/packages/nitro-utils/test/tsconfig.json deleted file mode 100644 index 38ca0b13bcdd..000000000000 --- a/packages/nitro-utils/test/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../tsconfig.test.json" -} diff --git a/packages/nitro-utils/test/vitest.setup.ts b/packages/nitro-utils/test/vitest.setup.ts deleted file mode 100644 index 7676ce96afef..000000000000 --- a/packages/nitro-utils/test/vitest.setup.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function setup() {} - -if (!globalThis.fetch) { - // @ts-expect-error - Needed for vitest to work with our fetch instrumentation - globalThis.Request = class Request {}; - // @ts-expect-error - Needed for vitest to work with our fetch instrumentation - globalThis.Response = class Response {}; -} diff --git a/packages/nitro-utils/tsconfig.json b/packages/nitro-utils/tsconfig.json deleted file mode 100644 index 425f0657515d..000000000000 --- a/packages/nitro-utils/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - - "include": ["src/**/*"], - - "compilerOptions": { - "lib": ["ES2018"], - } -} diff --git a/packages/nitro-utils/tsconfig.test.json b/packages/nitro-utils/tsconfig.test.json deleted file mode 100644 index 3fbe012384ee..000000000000 --- a/packages/nitro-utils/tsconfig.test.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "include": ["test/**/*", "vite.config.ts"], - - "compilerOptions": { - // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "vitest/globals"] - } -} diff --git a/packages/nitro-utils/tsconfig.types.json b/packages/nitro-utils/tsconfig.types.json deleted file mode 100644 index 65455f66bd75..000000000000 --- a/packages/nitro-utils/tsconfig.types.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.json", - - "compilerOptions": { - "declaration": true, - "declarationMap": true, - "emitDeclarationOnly": true, - "outDir": "build/types" - } -} diff --git a/packages/nitro-utils/vite.config.ts b/packages/nitro-utils/vite.config.ts deleted file mode 100644 index 0229ec105e04..000000000000 --- a/packages/nitro-utils/vite.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import baseConfig from '../../vite/vite.config'; - -export default { - ...baseConfig, - test: { - environment: 'jsdom', - setupFiles: ['./test/vitest.setup.ts'], - }, -}; diff --git a/scripts/ci-unit-tests.ts b/scripts/ci-unit-tests.ts index 08459e9eabba..c75e5cc871c0 100644 --- a/scripts/ci-unit-tests.ts +++ b/scripts/ci-unit-tests.ts @@ -46,8 +46,7 @@ const SKIP_TEST_PACKAGES: Record = { '@sentry/astro', '@sentry/nuxt', '@sentry/nestjs', - '@sentry-internal/eslint-plugin-sdk', - '@sentry-internal/nitro-utils', + '@sentry-internal/eslint-plugin-sdk' ], }, '16': { From de56c1010b6c1b0185dba8d44a121cec888e281c Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Fri, 6 Dec 2024 15:04:15 +0100 Subject: [PATCH 18/18] update based on develop --- packages/nuxt/src/common/types.ts | 3 -- packages/nuxt/src/vite/addServerConfig.ts | 13 +----- packages/nuxt/src/vite/utils.ts | 9 ++++ packages/nuxt/test/vite/utils.test.ts | 52 +++++++++++++++++------ 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/packages/nuxt/src/common/types.ts b/packages/nuxt/src/common/types.ts index ef1da1947f7c..41a07bfbc27b 100644 --- a/packages/nuxt/src/common/types.ts +++ b/packages/nuxt/src/common/types.ts @@ -144,9 +144,6 @@ export type SentryNuxtModuleOptions = { experimental_entrypointWrappedFunctions?: string[]; /** - * By default—unless you configure `dynamicImportForServerEntry: false`—the SDK will try to wrap your Nitro server entrypoint - * with a dynamic `import()` to ensure all dependencies can be properly instrumented. - * * The server entrypoint filename is automatically set by the Sentry SDK depending on the Nitro present. * In case the server entrypoint has a different filename, you can overwrite it here. */ diff --git a/packages/nuxt/src/vite/addServerConfig.ts b/packages/nuxt/src/vite/addServerConfig.ts index 72d301bc8331..da4f5bc11bce 100644 --- a/packages/nuxt/src/vite/addServerConfig.ts +++ b/packages/nuxt/src/vite/addServerConfig.ts @@ -5,16 +5,7 @@ import { wrapServerEntryWithDynamicImport } from '@sentry-internal/nitro-utils'; import { consoleSandbox } from '@sentry/core'; import type { Nitro } from 'nitropack'; import type { SentryNuxtModuleOptions } from '../common/types'; -import { - QUERY_END_INDICATOR, - SENTRY_REEXPORTED_FUNCTIONS, - SENTRY_WRAPPED_ENTRY, - SENTRY_WRAPPED_FUNCTIONS, - constructFunctionReExport, - constructWrappedFunctionExportQuery, - getFilenameFromNodeStartCommand, - removeSentryQueryFromPath, -} from './utils'; +import { getFilenameFromNodeStartCommand } from './utils'; const SERVER_CONFIG_FILENAME = 'sentry.server.config'; @@ -155,7 +146,7 @@ export function addDynamicImportEntryFileWrapper( serverEntrypointFileName: moduleOptions.serverEntrypointFileName || nitro.options.preset, serverConfigFileName: SERVER_CONFIG_FILENAME, resolvedServerConfigPath: createResolver(nitro.options.srcDir).resolve(`/${serverConfigFile}`), - entrypointWrappedFunctions: moduleOptions.entrypointWrappedFunctions, + entrypointWrappedFunctions: moduleOptions.experimental_entrypointWrappedFunctions, additionalImports: ['import-in-the-middle/hook.mjs'], debug: moduleOptions.debug, }), diff --git a/packages/nuxt/src/vite/utils.ts b/packages/nuxt/src/vite/utils.ts index e41d3fb06cab..4accd44671a6 100644 --- a/packages/nuxt/src/vite/utils.ts +++ b/packages/nuxt/src/vite/utils.ts @@ -24,3 +24,12 @@ export function findDefaultSdkInitFile(type: 'server' | 'client'): string | unde return filePaths.find(filename => fs.existsSync(filename)); } + +/** + * Extracts the filename from a node command with a path. + */ +export function getFilenameFromNodeStartCommand(nodeCommand: string): string | null { + const regex = /[^/\\]+$/; + const match = nodeCommand.match(regex); + return match ? match[0] : null; +} diff --git a/packages/nuxt/test/vite/utils.test.ts b/packages/nuxt/test/vite/utils.test.ts index 26d24af6980b..d9763241ba24 100644 --- a/packages/nuxt/test/vite/utils.test.ts +++ b/packages/nuxt/test/vite/utils.test.ts @@ -1,18 +1,6 @@ import * as fs from 'fs'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import { findDefaultSdkInitFile } from '../../src/vite/utils'; -import { - QUERY_END_INDICATOR, - SENTRY_REEXPORTED_FUNCTIONS, - SENTRY_WRAPPED_ENTRY, - SENTRY_WRAPPED_FUNCTIONS, - constructFunctionReExport, - constructWrappedFunctionExportQuery, - extractFunctionReexportQueryParameters, - findDefaultSdkInitFile, - getFilenameFromNodeStartCommand, - removeSentryQueryFromPath, -} from '../../src/vite/utils'; +import { findDefaultSdkInitFile, getFilenameFromNodeStartCommand } from '../../src/vite/utils'; vi.mock('fs'); @@ -71,3 +59,41 @@ describe('findDefaultSdkInitFile', () => { expect(result).toMatch('packages/nuxt/sentry.server.config.js'); }); }); + +describe('getFilenameFromNodeStartCommand', () => { + it('should return the filename from a simple path', () => { + const path = 'node ./server/index.mjs'; + const filename = getFilenameFromNodeStartCommand(path); + expect(filename).toBe('index.mjs'); + }); + + it('should return the filename from a nested path', () => { + const path = 'node ./.output/whatever/path/server.js'; + const filename = getFilenameFromNodeStartCommand(path); + expect(filename).toBe('server.js'); + }); + + it('should return the filename from a Windows-style path', () => { + const path = '.\\Projects\\my-app\\src\\main.js'; + const filename = getFilenameFromNodeStartCommand(path); + expect(filename).toBe('main.js'); + }); + + it('should return null for an empty path', () => { + const path = ''; + const filename = getFilenameFromNodeStartCommand(path); + expect(filename).toBeNull(); + }); + + it('should return the filename when there are no directory separators', () => { + const path = 'index.mjs'; + const filename = getFilenameFromNodeStartCommand(path); + expect(filename).toBe('index.mjs'); + }); + + it('should return null for paths with trailing slashes', () => { + const path = 'node ./server/'; + const filename = getFilenameFromNodeStartCommand(path); + expect(filename).toBeNull(); + }); +});