Skip to content

Commit ec50b3e

Browse files
authored
feat(nuxt)!: Bundle server config into Nitro build (#24094)
Previously, the `sentry.server.config.ts` file was emitted in the build output so it can be added with `--import`. As we don't rely on early-importing anymore (thanks to orchestrion), we don't need this file anymore. - **Nitro plugin init (default):** The server config is now bundled into the Nitro build and imported by a generated nitro plugin, so `Sentry.init` runs at startup without `node --import`. Works on Nitro 2 and 3, prod and dev (hurray) - **`--import` compat:** The old file path now gets a shim that just prints "remove this flag". A CI variant runs the whole suite with the shim preloaded to see if the warning is printed - **Double init guard:** A preload that really calls `init` wins. The bundled init skips with a notice. - **Prerender:** Init is skipped during prerendering, so CI builds send nothing. Tests assert no trace meta tags in prerendered HTML. - **Early-crash tests:** Errors thrown before `listen` are captured and flushed (first and last plugin slot) - **Deprecated:** both `autoInjectServerSentry` modes, kept working until v12 Closes #24149
1 parent ac123ed commit ec50b3e

31 files changed

Lines changed: 808 additions & 353 deletions

‎MIGRATION.md‎

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,34 @@ Affected SDKs: `@sentry/cloudflare`.
12051205
12061206
Calls to rate limiter bindings (`env.MY_RATE_LIMITER.limit()`) no longer create a span. The removed span had the op `rpc`, the origin `auto.faas.cloudflare.rate_limit`, and the attribute `rpc.service: cloudflare.rate_limit`. Remove any dashboard, alert, or `ignoreSpans` entry that references it.
12071207
1208+
### `@sentry/nuxt`: the server config is bundled, `--import` is no longer needed
1209+
1210+
The SDK now bundles `sentry.server.config.ts` into the Nitro server build, where it initializes itself when the server starts. Instrumentation happens at build time, so preloading the config file is no longer necessary.
1211+
1212+
Remove the `--import` flag from your production start command:
1213+
1214+
```bash
1215+
# before
1216+
node --import ./.output/server/sentry.server.config.mjs .output/server/index.mjs
1217+
1218+
# after
1219+
node .output/server/index.mjs
1220+
```
1221+
1222+
Old start commands keep working: the SDK still emits a file at the old path, but it only prints a reminder that the flag can be removed. If you preload a file that calls `Sentry.init` yourself, that init wins and the bundled one is skipped.
1223+
1224+
The same applies in development. Remove the `NODE_OPTIONS` preload:
1225+
1226+
```bash
1227+
# before
1228+
NODE_OPTIONS='--import ./.nuxt/dev/sentry.server.config.mjs' nuxt dev
1229+
1230+
# after
1231+
nuxt dev
1232+
```
1233+
1234+
Since no preload is needed anymore, the `autoInjectServerSentry` option (`'top-level-import'` and `'experimental_dynamic-import'`) and `experimental_entrypointWrappedFunctions` are deprecated. Remove them from your `sentry` module options as the default behavior replaces both. They will be deleted in the next major version.
1235+
12081236
### `@sentry/ember` is now a v2 addon with manual setup
12091237
12101238
Affected SDKs: `@sentry/ember`.
@@ -1747,11 +1775,7 @@ public/instrument.server.ts
17471775
sentry.server.config.ts
17481776
```
17491777
1750-
After the rename, the SDK also emits `.output/server/sentry.server.config.mjs` for you to preload:
1751-
1752-
```bash
1753-
node --import ./.output/server/sentry.server.config.mjs .output/server/index.mjs
1754-
```
1778+
After the rename, the SDK bundles the file into the Nitro server build and initializes itself at server startup. See ["the server config is bundled"](#sentrynuxt-the-server-config-is-bundled---import-is-no-longer-needed) above: the `--import` preload is no longer needed.
17551779
17561780
The deprecated `sourceMapsUploadOptions` module option was removed. Move its fields to the root level of the `sentry` module options. Note that `url` was renamed to `sentryUrl`, and `enabled` was replaced by `sourcemaps.disable` (inverted: `enabled: false` becomes `sourcemaps: { disable: true }`).
17571781

‎dev-packages/e2e-tests/test-applications/nuxt-4-static/nuxt-start-dev-server.bash‎

Lines changed: 0 additions & 55 deletions
This file was deleted.

‎dev-packages/e2e-tests/test-applications/nuxt-4-static/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"clean": "npx nuxi cleanup",
1313
"test": "playwright test",
1414
"test:prod": "TEST_ENV=production playwright test",
15-
"test:dev": "bash ./nuxt-start-dev-server.bash && TEST_ENV=development playwright test environment",
15+
"test:dev": "TEST_ENV=development playwright test environment db-drivers",
1616
"test:build": "pnpm install && pnpm build",
1717
"test:build-canary": "pnpm add nuxt@npm:nuxt-nightly@latest && pnpm add nitropack@npm:nitropack-nightly@latest && pnpm install --force && pnpm build",
1818
"test:assert": "pnpm test:prod && pnpm test:dev"

‎dev-packages/e2e-tests/test-applications/nuxt-4-static/playwright.config.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ if (!testEnv) {
88

99
const getStartCommand = () => {
1010
if (testEnv === 'development') {
11-
return "NODE_OPTIONS='--import ./.nuxt/dev/sentry.server.config.mjs' nuxt dev -p 3030";
11+
// The Sentry server config is bundled into the dev server via a nitro plugin, so no preload is needed.
12+
return 'nuxt dev -p 3030';
1213
}
1314

1415
if (testEnv === 'production') {
15-
return 'pnpm start:import';
16+
return 'pnpm start';
1617
}
1718

1819
throw new Error(`Unknown test env: ${testEnv}`);

‎dev-packages/e2e-tests/test-applications/nuxt-4-static/tests/db-drivers.test.ts‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ import { expect, test } from '@playwright/test';
22
import { waitForTransaction } from '@sentry-internal/test-utils';
33

44
// The Nuxt module auto-wires the orchestrion build-time transform, which injects
5-
// `diagnostics_channel` publishers into these drivers as Nitro bundles them. That
6-
// only happens in the production build, so these tests are excluded from the
7-
// `test:dev` pass (which filters to `environment`).
5+
// `diagnostics_channel` publishers into these drivers as Nitro bundles them. `nuxt dev`
6+
// has no bundle to transform, so there the drivers rely on runtime injection instead.
87
test('Instruments ioredis automatically', async ({ baseURL }) => {
8+
// ioredis 5.10.x has no native channels, so dev needs runtime injection — but the dev bundle hoists
9+
// its import above the inlined `Sentry.init`, so it loads before injection is active. (ioredis >=5.11
10+
// publishes native channels and does work in dev; mysql requires its file lazily, after init.)
11+
test.skip(process.env.TEST_ENV === 'development', 'ioredis <5.11 loads before runtime injection is active in dev');
12+
913
const transactionEventPromise = waitForTransaction('nuxt-4-static', transactionEvent => {
1014
return (
1115
transactionEvent.contexts?.trace?.op === 'http.server' && transactionEvent.transaction === 'GET /api/db-ioredis'
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Simulates a v10-style `node --import` preload that fully initializes the SDK
2+
// before the config bundled into the server build runs its own `Sentry.init`.
3+
import * as Sentry from '@sentry/nuxt';
4+
5+
Sentry.init({
6+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
7+
tracesSampleRate: 1.0,
8+
tunnel: 'http://localhost:3031/',
9+
});

‎dev-packages/e2e-tests/test-applications/nuxt-4/nuxt-start-dev-server.bash‎

Lines changed: 0 additions & 55 deletions
This file was deleted.

‎dev-packages/e2e-tests/test-applications/nuxt-4/package.json‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
"clean": "npx nuxi cleanup",
1313
"test": "playwright test",
1414
"test:prod": "TEST_ENV=production playwright test",
15-
"test:dev": "bash ./nuxt-start-dev-server.bash && TEST_ENV=development playwright test environment",
16-
"test:build": "pnpm install && pnpm build",
17-
"test:build-canary": "pnpm add nuxt@npm:nuxt-nightly@latest && pnpm add nitropack@npm:nitropack-nightly@latest && pnpm install --force && pnpm build",
18-
"test:assert": "pnpm test:prod && pnpm test:dev"
15+
"test:dev": "TEST_ENV=development playwright test environment db-drivers",
16+
"test:build": "pnpm install && node ./scripts/build-with-prerender-event-sink.mjs",
17+
"test:build-canary": "pnpm add nuxt@npm:nuxt-nightly@latest && pnpm add nitropack@npm:nitropack-nightly@latest && pnpm install --force && node ./scripts/build-with-prerender-event-sink.mjs",
18+
"test:assert": "pnpm test:prod && pnpm test:dev",
19+
"test:prod:import": "TEST_ENV=production-import playwright test"
1920
},
21+
"//": "Pin ioredis to 5.10.1 because that's the last version before it publishes its own diagnostics channels",
2022
"dependencies": {
2123
"@pinia/nuxt": "^0.5.5",
2224
"@sentry/nuxt": "file:../../packed/sentry-nuxt-packed.tgz",
@@ -38,6 +40,11 @@
3840
"build-command": "E2E_TEST_OTEL_SETUP=true pnpm test:build",
3941
"assert-command": "E2E_TEST_OTEL_SETUP=true pnpm test:assert",
4042
"label": "nuxt-4 (tracer provider)"
43+
},
44+
{
45+
"build-command": "pnpm test:build",
46+
"assert-command": "pnpm test:prod:import",
47+
"label": "nuxt-4 (--import compat)"
4148
}
4249
],
4350
"optionalVariants": [

‎dev-packages/e2e-tests/test-applications/nuxt-4/playwright.config.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,16 @@ if (!testEnv) {
88

99
const getStartCommand = () => {
1010
if (testEnv === 'development') {
11-
return "NODE_OPTIONS='--import ./.nuxt/dev/sentry.server.config.mjs' nuxt dev -p 3030";
11+
// The Sentry server config is bundled into the dev server via a nitro plugin, so no preload is needed.
12+
return 'nuxt dev -p 3030';
1213
}
1314

1415
if (testEnv === 'production') {
16+
return 'pnpm start';
17+
}
18+
19+
// Runs the suite with the compat shim preloaded, like existing `--import` deploy commands do.
20+
if (testEnv === 'production-import') {
1521
return 'pnpm start:import';
1622
}
1723

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Runs `nuxt build` with a sink on the Sentry tunnel port and records how many envelopes arrive,
2+
// so tests can assert that prerendering sends no telemetry during the build.
3+
import { spawn } from 'node:child_process';
4+
import { mkdirSync, writeFileSync } from 'node:fs';
5+
import { createServer } from 'node:http';
6+
7+
let envelopeCount = 0;
8+
const sink = createServer((req, res) => {
9+
req.resume();
10+
req.on('end', () => {
11+
envelopeCount += 1;
12+
res.writeHead(200).end('{}');
13+
});
14+
});
15+
16+
const sinkAvailable = await new Promise(resolve => {
17+
sink.once('error', () => resolve(false));
18+
sink.listen(3031, () => resolve(true));
19+
});
20+
21+
const build = spawn('nuxt', ['build'], { stdio: 'inherit', shell: true });
22+
const exitCode = await new Promise(resolve => build.on('exit', resolve));
23+
24+
sink.close();
25+
mkdirSync('.output', { recursive: true });
26+
writeFileSync(
27+
'.output/build-envelope-count.json',
28+
JSON.stringify({ envelopeCount: sinkAvailable ? envelopeCount : null }),
29+
);
30+
process.exit(exitCode ?? 1);

0 commit comments

Comments
 (0)