diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/assert.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/assert.mjs new file mode 100644 index 000000000000..db243c44e199 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/assert.mjs @@ -0,0 +1,6 @@ +import { assertBundlerInstrumentation } from '@sentry-internal/test-utils'; + +// Drives the four built bundles (plain / plugin / plain-external / plugin-external) across the +// build-time and runtime instrumentation paths. The external variants preload the runtime hook +// of `@sentry/aws-serverless` rather than `@sentry/node`, since that is the one a Lambda user has. +assertBundlerInstrumentation('graphql', { importHook: '@sentry/aws-serverless/import' }); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/build.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/build.mjs new file mode 100644 index 000000000000..2f9176797576 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/build.mjs @@ -0,0 +1,42 @@ +// Same four-way esbuild matrix as the `node-esbuild` app, but with the plugin imported from +// `@sentry/aws-serverless/esbuild`, which is how a Lambda project that bundles with esbuild (CDK, +// SAM, Serverless Framework) would use it. See `node-esbuild/build.mjs` for the variant layout. +import { rmSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { build } from 'esbuild'; +import { sentryEsbuildPlugin } from '@sentry/aws-serverless/esbuild'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +rmSync(join(__dirname, 'dist'), { recursive: true, force: true }); + +// No auth/release/telemetry — we only care about the build-time transforms and defines. +const makeSentryPlugin = () => + sentryEsbuildPlugin({ + telemetry: false, + sourcemaps: { disable: true }, + release: { create: false, finalize: false, inject: false }, + }); + +function run(name, { external, plugins }) { + return build({ + entryPoints: [join(__dirname, 'src', 'entry.mjs')], + outfile: join(__dirname, 'dist', name, 'main.cjs'), + bundle: true, + platform: 'node', + format: 'cjs', + external: external ? ['graphql'] : [], + minify: false, + logLevel: 'silent', + plugins, + }); +} + +await run('plain', { external: false, plugins: [] }); +await run('plugin', { external: false, plugins: [makeSentryPlugin()] }); +await run('plain-external', { external: true, plugins: [] }); +await run('plugin-external', { external: true, plugins: [makeSentryPlugin()] }); + +// eslint-disable-next-line no-console +console.log('built plain + plugin (inlined) and plain-external + plugin-external with esbuild'); diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/package.json b/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/package.json new file mode 100644 index 000000000000..fae771717d24 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/package.json @@ -0,0 +1,23 @@ +{ + "name": "aws-serverless-esbuild", + "description": "ensure the esbuild plugin from @sentry/aws-serverless/esbuild build-time instruments a bundled graphql at runtime", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "clean": "npx rimraf node_modules dist pnpm-lock.yaml", + "test:build": "pnpm install && node ./build.mjs", + "test:assert": "node ./assert.mjs" + }, + "dependencies": { + "@sentry/aws-serverless": "file:../../packed/sentry-aws-serverless-packed.tgz" + }, + "devDependencies": { + "@sentry-internal/test-utils": "file:../../packed/sentry-internal-test-utils-packed.tgz", + "graphql": "16.9.0", + "esbuild": "0.28.2" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/pnpm-workspace.yaml b/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/pnpm-workspace.yaml new file mode 100644 index 000000000000..3cdf2ff22992 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +allowBuilds: + esbuild: true +minimumReleaseAge: 0 diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/src/app.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/src/app.mjs new file mode 100644 index 000000000000..df51731aa1d2 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/src/app.mjs @@ -0,0 +1,9 @@ +// The workload the bundle instruments: a `graphql` query. Whether `graphql` is inlined into the +// bundle or kept external is decided per-variant by `build.mjs`. +import { buildSchema, graphql } from 'graphql'; + +const schema = buildSchema('type Query { hello: String }'); + +export async function runWorkload() { + return graphql({ schema, source: '{ hello }', rootValue: { hello: () => 'world' } }); +} diff --git a/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/src/entry.mjs b/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/src/entry.mjs new file mode 100644 index 000000000000..4cfbc3022a7f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/aws-serverless-esbuild/src/entry.mjs @@ -0,0 +1,45 @@ +// Bundled entrypoint, run directly with `node` (no `--import` runtime hook). `Sentry.init` runs +// first so the instrumentation's channel subscriber is ready, then the workload is imported and run. +// Spans are collected via the `spanEnd` hook and written to the file named by +// `SENTRY_E2E_RESULT_FILE` for `assert.mjs` to read back. See `node-esbuild/src/entry.mjs`. +import { writeFileSync } from 'node:fs'; +import * as Sentry from '@sentry/aws-serverless'; + +async function main() { + Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + tracesSampleRate: 1, + // `@sentry/aws-serverless` leaves the performance integrations out by default, so the one + // that subscribes to the graphql channel has to be added for the spans to show up. + integrations: [Sentry.graphqlIntegration()], + // Isolate the build-time path: with the runtime hook off, the bundler plugin is the only possible + // injector, so a `plain` (no-plugin) build is a true negative. + enableRuntimeChannelInjection: false, + // Hermetic — never hit the network. + transport: () => ({ send: () => Promise.resolve({}), flush: () => Promise.resolve(true) }), + }); + + const spans = []; + Sentry.getClient()?.on('spanEnd', span => { + const json = Sentry.spanToJSON(span); + spans.push({ name: json.name, origin: json.attributes?.['sentry.origin'] }); + }); + + const { runWorkload } = await import('./app.mjs'); + + let result; + await Sentry.startSpan({ name: 'workload' }, async () => { + result = await runWorkload(); + }); + + await Sentry.flush(2000); + + const resultFile = process.env.SENTRY_E2E_RESULT_FILE; + if (!resultFile) { + throw new Error('SENTRY_E2E_RESULT_FILE is required (assertBundlerInstrumentation sets it).'); + } + writeFileSync(resultFile, JSON.stringify({ result, spans })); + process.exit(0); +} + +void main(); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/nest-cli.json b/dev-packages/e2e-tests/test-applications/nestjs-webpack/nest-cli.json new file mode 100644 index 000000000000..c6e6d9356b5d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/nest-cli.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true, + "webpack": true, + "webpackConfigPath": "webpack.config.js" + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/package.json b/dev-packages/e2e-tests/test-applications/nestjs-webpack/package.json new file mode 100644 index 000000000000..e61a30b99e56 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/package.json @@ -0,0 +1,36 @@ +{ + "name": "nestjs-webpack", + "description": "ensure @sentry/nestjs/webpack works with the Nest CLI webpack builder", + "version": "0.0.1", + "private": true, + "scripts": { + "build": "nest build", + "start": "node dist/main", + "clean": "npx rimraf node_modules dist pnpm-lock.yaml", + "test": "playwright test", + "test:build": "pnpm install && pnpm build", + "test:assert": "pnpm test" + }, + "dependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0", + "@nestjs/platform-express": "^11.0.0", + "@sentry/nestjs": "file:../../packed/sentry-nestjs-packed.tgz", + "reflect-metadata": "^0.2.0", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@playwright/test": "1.64.1", + "@sentry-internal/test-utils": "file:../../packed/sentry-internal-test-utils-packed.tgz", + "@nestjs/cli": "^11.0.0", + "@nestjs/schematics": "^11.0.0", + "@types/express": "^4.17.17", + "@types/node": "^18.19.1", + "ts-loader": "^9.4.3", + "tsconfig-paths": "^4.2.0", + "typescript": "~5.0.0" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/nestjs-webpack/playwright.config.mjs new file mode 100644 index 000000000000..31f2b913b58b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/playwright.config.mjs @@ -0,0 +1,7 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +const config = getPlaywrightConfig({ + startCommand: `pnpm start`, +}); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/pnpm-workspace.yaml b/dev-packages/e2e-tests/test-applications/nestjs-webpack/pnpm-workspace.yaml new file mode 100644 index 000000000000..50ece95aba3b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +minimumReleaseAge: 0 +overrides: + 'minimatch': '10.0.1' diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/app.controller.ts new file mode 100644 index 000000000000..122b769162cf --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/app.controller.ts @@ -0,0 +1,23 @@ +import { Controller, Get, Param } from '@nestjs/common'; +import { flush } from '@sentry/nestjs'; +import { AppService } from './app.service'; + +@Controller() +export class AppController { + constructor(private readonly appService: AppService) {} + + @Get('test-transaction') + testTransaction() { + return this.appService.testTransaction(); + } + + @Get('test-exception/:id') + async testException(@Param('id') id: string) { + return this.appService.testException(id); + } + + @Get('flush') + async flush() { + await flush(); + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/app.module.ts b/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/app.module.ts new file mode 100644 index 000000000000..059d719456fb --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/app.module.ts @@ -0,0 +1,18 @@ +import { Module } from '@nestjs/common'; +import { APP_FILTER } from '@nestjs/core'; +import { SentryGlobalFilter, SentryModule } from '@sentry/nestjs/setup'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; + +@Module({ + imports: [SentryModule.forRoot()], + controllers: [AppController], + providers: [ + AppService, + { + provide: APP_FILTER, + useClass: SentryGlobalFilter, + }, + ], +}) +export class AppModule {} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/app.service.ts new file mode 100644 index 000000000000..715eaf8bdac1 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/app.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@nestjs/common'; +import * as Sentry from '@sentry/nestjs'; + +@Injectable() +export class AppService { + testTransaction() { + Sentry.startSpan({ name: 'test-span' }, () => { + Sentry.startSpan({ name: 'child-span' }, () => {}); + }); + } + + testException(id: string) { + throw new Error(`This is an exception with id ${id}`); + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/instrument.ts b/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/instrument.ts new file mode 100644 index 000000000000..f1f4de865435 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/instrument.ts @@ -0,0 +1,8 @@ +import * as Sentry from '@sentry/nestjs'; + +Sentry.init({ + environment: 'qa', // dynamic sampling bias to keep transactions + dsn: process.env.E2E_TEST_DSN, + tunnel: `http://localhost:3031/`, // proxy server + tracesSampleRate: 1, +}); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/main.ts b/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/main.ts new file mode 100644 index 000000000000..71ce685f4d61 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/src/main.ts @@ -0,0 +1,15 @@ +// Import this first +import './instrument'; + +// Import other modules +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; + +const PORT = 3030; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + await app.listen(PORT); +} + +bootstrap(); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/nestjs-webpack/start-event-proxy.mjs new file mode 100644 index 000000000000..6d02c6d5e3fe --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'nestjs-webpack', +}); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-webpack/tests/errors.test.ts new file mode 100644 index 000000000000..cd5d05c3c2bf --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/tests/errors.test.ts @@ -0,0 +1,24 @@ +import { expect, test } from '@playwright/test'; +import { waitForError } from '@sentry-internal/test-utils'; + +const APP_NAME = 'nestjs-webpack'; + +test('Sends an exception with the release injected by the webpack plugin', async ({ baseURL }) => { + const errorEventPromise = waitForError(APP_NAME, event => { + return !event.type && event.exception?.values?.[0]?.value === 'This is an exception with id 123'; + }); + + const response = await fetch(`${baseURL}/test-exception/123`); + expect(response.status).toBe(500); + + const errorEvent = await errorEventPromise; + + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.http.nestjs.global_filter', + }); + expect(errorEvent.transaction).toEqual('GET /test-exception/:id'); + + // Set by the release injection of `sentryWebpackPlugin` from `@sentry/nestjs/webpack`. + expect(errorEvent.release).toBe('nestjs-webpack-e2e'); +}); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-webpack/tests/transactions.test.ts new file mode 100644 index 000000000000..6eb0e0b03480 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/tests/transactions.test.ts @@ -0,0 +1,27 @@ +import { expect, test } from '@playwright/test'; +import { collectStreamedSpansUntilSegment } from '@sentry-internal/test-utils'; + +const APP_NAME = 'nestjs-webpack'; + +test('Sends streamed spans for an API route', async ({ baseURL }) => { + const spansPromise = collectStreamedSpansUntilSegment(APP_NAME, 'GET /test-transaction'); + + await fetch(`${baseURL}/test-transaction`); + + const spans = await spansPromise; + const segmentSpan = spans.find(span => span.is_segment)!; + + expect(segmentSpan).toMatchObject({ + name: 'GET /test-transaction', + is_segment: true, + status: 'ok', + attributes: expect.objectContaining({ + 'sentry.op': { type: 'string', value: 'http.server' }, + 'http.route': { type: 'string', value: '/test-transaction' }, + 'http.response.status_code': { type: 'integer', value: 200 }, + }), + }); + + expect(spans.find(span => span.name === 'test-span')).toMatchObject({ is_segment: false }); + expect(spans.find(span => span.name === 'child-span')).toMatchObject({ is_segment: false }); +}); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/tsconfig.build.json b/dev-packages/e2e-tests/test-applications/nestjs-webpack/tsconfig.build.json new file mode 100644 index 000000000000..26c30d4eddf2 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist"] +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/tsconfig.json b/dev-packages/e2e-tests/test-applications/nestjs-webpack/tsconfig.json new file mode 100644 index 000000000000..cf79f029c781 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2021", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": false, + "noImplicitAny": false, + "strictBindCallApply": false, + "forceConsistentCasingInFileNames": false, + "noFallthroughCasesInSwitch": false, + "moduleResolution": "Node16" + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-webpack/webpack.config.js b/dev-packages/e2e-tests/test-applications/nestjs-webpack/webpack.config.js new file mode 100644 index 000000000000..f1a87303328f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-webpack/webpack.config.js @@ -0,0 +1,18 @@ +// Extends the Nest CLI's own webpack config (`nest build` with `"webpack": true` in nest-cli.json) +// with the Sentry plugin from `@sentry/nestjs/webpack`. The injected release name shows up on +// every event, which is how the tests know the plugin ran. No auth, upload or telemetry. +const { sentryWebpackPlugin } = require('@sentry/nestjs/webpack'); + +module.exports = function (options) { + return { + ...options, + plugins: [ + ...options.plugins, + sentryWebpackPlugin({ + telemetry: false, + sourcemaps: { disable: true }, + release: { name: 'nestjs-webpack-e2e', create: false, finalize: false }, + }), + ], + }; +}; diff --git a/dev-packages/test-utils/src/bundler-instrumentation.ts b/dev-packages/test-utils/src/bundler-instrumentation.ts index 3e14a4084f74..10ae6c813564 100644 --- a/dev-packages/test-utils/src/bundler-instrumentation.ts +++ b/dev-packages/test-utils/src/bundler-instrumentation.ts @@ -69,10 +69,12 @@ interface BundleRun { * * @param fixtureOrName A built-in fixture name (e.g. `'graphql'`) or an {@link InstrumentationFixture}. * @param options.appDir The test app directory holding `dist//main.*`. Defaults to `cwd`. + * @param options.importHook The `--import` entry that registers the runtime hook for the external + * variants. Defaults to `@sentry/node/import`; SDKs that wrap `@sentry/node` ship their own. */ export function assertBundlerInstrumentation( fixtureOrName: string | InstrumentationFixture, - { appDir = process.cwd() }: { appDir?: string } = {}, + { appDir = process.cwd(), importHook = '@sentry/node/import' }: { appDir?: string; importHook?: string } = {}, ): void { const fixture = typeof fixtureOrName === 'string' ? FIXTURES[fixtureOrName] : fixtureOrName; if (!fixture) { @@ -98,7 +100,7 @@ export function assertBundlerInstrumentation( // result to a file (not stdout) so a piped, buffered write can't be truncated by the child's exit. const runBundle = (name: string, withImport: boolean): BundleRun => { const resultFile = join(tmpdir(), `sentry-bundler-e2e-${name}-${randomUUID()}.json`); - const args = withImport ? ['--import', '@sentry/node/import', entryPath(name)] : [entryPath(name)]; + const args = withImport ? ['--import', importHook, entryPath(name)] : [entryPath(name)]; const label = `${name}${withImport ? ' (--import)' : ''}`; let stdout: string; try { diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index 7779dc1cc698..a42b517c25b5 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -50,6 +50,16 @@ "require": { "default": "./build/npm/cjs/awslambda-auto.js" } + }, + "./esbuild": { + "import": { + "types": "./build/npm/types/bundler-plugin/esbuild.d.ts", + "default": "./build/npm/esm/bundler-plugin/esbuild.js" + }, + "require": { + "types": "./build/npm/types/bundler-plugin/esbuild.d.ts", + "default": "./build/npm/cjs/bundler-plugin/esbuild.js" + } } }, "publishConfig": { diff --git a/packages/aws-serverless/rollup.npm.config.mjs b/packages/aws-serverless/rollup.npm.config.mjs index ff398be22729..9c89af481558 100644 --- a/packages/aws-serverless/rollup.npm.config.mjs +++ b/packages/aws-serverless/rollup.npm.config.mjs @@ -31,7 +31,7 @@ export default [ // TODO: `awslambda-auto.ts` is a file which the lambda layer uses to automatically init the SDK. Does it need to be // in the npm package? Is it possible that some people are using it themselves in the same way the layer uses it (in // which case removing it would be a breaking change)? Should it stay here or not? - entrypoints: ['src/index.ts', 'src/awslambda-auto.ts'], + entrypoints: ['src/index.ts', 'src/awslambda-auto.ts', 'src/bundler-plugin/esbuild.ts'], // packages with bundles have a different build directory structure hasBundles: true, packageSpecificConfig: { diff --git a/packages/aws-serverless/src/bundler-plugin/esbuild.ts b/packages/aws-serverless/src/bundler-plugin/esbuild.ts new file mode 100644 index 000000000000..0177c7dfd2b2 --- /dev/null +++ b/packages/aws-serverless/src/bundler-plugin/esbuild.ts @@ -0,0 +1,2 @@ +export { sentryEsbuildPlugin } from '@sentry/node/esbuild'; +export type { SentryEsbuildPluginOptions } from '@sentry/node/esbuild'; diff --git a/packages/aws-serverless/test/bundler-plugin.test.ts b/packages/aws-serverless/test/bundler-plugin.test.ts new file mode 100644 index 000000000000..9a190740a7ef --- /dev/null +++ b/packages/aws-serverless/test/bundler-plugin.test.ts @@ -0,0 +1,9 @@ +import { sentryEsbuildPlugin as nodeEsbuildPlugin } from '@sentry/node/esbuild'; +import { describe, expect, it } from 'vitest'; +import { sentryEsbuildPlugin } from '../src/bundler-plugin/esbuild'; + +describe('@sentry/aws-serverless bundler plugin', () => { + it('re-exports the @sentry/node esbuild plugin', () => { + expect(sentryEsbuildPlugin).toBe(nodeEsbuildPlugin); + }); +}); diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index a1904cd78987..5fc09bb65293 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -38,6 +38,16 @@ "types": "./setup.d.ts", "default": "./build/cjs/setup.js" } + }, + "./webpack": { + "import": { + "types": "./build/types/bundler-plugin/webpack.d.ts", + "default": "./build/esm/bundler-plugin/webpack.js" + }, + "require": { + "types": "./build/types/bundler-plugin/webpack.d.ts", + "default": "./build/cjs/bundler-plugin/webpack.js" + } } }, "publishConfig": { diff --git a/packages/nestjs/rollup.npm.config.mjs b/packages/nestjs/rollup.npm.config.mjs index 0ce71546935c..d9bf9afbc29c 100644 --- a/packages/nestjs/rollup.npm.config.mjs +++ b/packages/nestjs/rollup.npm.config.mjs @@ -2,6 +2,6 @@ import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollu export default makeNPMConfigVariants( makeBaseNPMConfig({ - entrypoints: ['src/index.ts', 'src/setup.ts'], + entrypoints: ['src/index.ts', 'src/setup.ts', 'src/bundler-plugin/webpack.ts'], }), ); diff --git a/packages/nestjs/src/bundler-plugin/webpack.ts b/packages/nestjs/src/bundler-plugin/webpack.ts new file mode 100644 index 000000000000..1bbc9a1233ea --- /dev/null +++ b/packages/nestjs/src/bundler-plugin/webpack.ts @@ -0,0 +1,2 @@ +export { sentryWebpackPlugin } from '@sentry/node/webpack'; +export type { SentryWebpackPluginOptions } from '@sentry/node/webpack'; diff --git a/packages/nestjs/test/bundler-plugin.test.ts b/packages/nestjs/test/bundler-plugin.test.ts new file mode 100644 index 000000000000..9930558ee3b2 --- /dev/null +++ b/packages/nestjs/test/bundler-plugin.test.ts @@ -0,0 +1,9 @@ +import { sentryWebpackPlugin as nodeWebpackPlugin } from '@sentry/node/webpack'; +import { describe, expect, it } from 'vitest'; +import { sentryWebpackPlugin } from '../src/bundler-plugin/webpack'; + +describe('@sentry/nestjs bundler plugin', () => { + it('re-exports the @sentry/node webpack plugin', () => { + expect(sentryWebpackPlugin).toBe(nodeWebpackPlugin); + }); +});