Skip to content

Commit

Permalink
feat(nuxt): Add option autoInjectServerSentry (no default `import()…
Browse files Browse the repository at this point in the history
…`) (#14553)

As injecting the dynamic `import()` came with a bunch of challenges
related to how the build output looks like, this default is changed to
make users use the `--import` CLI flag.

This PR basically reverts this:
#13958
  • Loading branch information
s1gr1d authored Dec 6, 2024
1 parent 3778482 commit 121ae07
Show file tree
Hide file tree
Showing 64 changed files with 1,303 additions and 62 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,27 @@

- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott

### Important Changes

- **feat(nuxt): Add option autoInjectServerSentry (no default import()) ([#14553](https://github.com/getsentry/sentry-javascript/pull/14553))**

Using the dynamic `import()` as the default behavior for initializing the SDK on the server-side did not work for every project.
The default behavior of the SDK has been changed, and you now need to **use the `--import` flag to initialize Sentry on the server-side** to leverage full functionality.

Example with `--import`:

```bash
node --import ./.output/server/sentry.server.config.mjs .output/server/index.mjs
```

In case you are not able to use the `--import` flag, you can enable auto-injecting Sentry in the `nuxt.config.ts` (comes with limitations):

```json
sentry: {
autoInjectServerSentry: 'top-level-import', // or 'experimental_dynamic-import'
},
```

Work in this release was contributed by @lsmurray. Thank you for your contribution!

## 8.42.0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist

# Node dependencies
node_modules

# Logs
logs
*.log

# Misc
.DS_Store
.fleet
.idea

# Local env files
.env
.env.*
!.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@sentry:registry=http://127.0.0.1:4873
@sentry-internal:registry=http://127.0.0.1:4873
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<template>
<NuxtLayout>
<header>
<nav>
<ul>
<li><NuxtLink to="/fetch-server-error">Fetch Server Error</NuxtLink></li>
<li><NuxtLink to="/test-param/1234">Fetch Param</NuxtLink></li>
<li><NuxtLink to="/client-error">Client Error</NuxtLink></li>
</ul>
</nav>
</header>
<NuxtPage />
</NuxtLayout>
</template>

<script setup>
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
errorText: {
type: String,
required: true
},
id: {
type: String,
required: true
}
})
const triggerError = () => {
throw new Error(props.errorText);
};
</script>

<template>
<button :id="props.id" @click="triggerError">Trigger Error</button>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This script copies the `import-in-the-middle` content of the E2E test project root `node_modules` to the build output `node_modules`
# For some reason, some files are missing in the output (like `hook.mjs`) and this is not reproducible in external, standalone projects.
#
# Things we tried (that did not fix the problem):
# - Adding a resolution for `@vercel/nft` v0.27.0 (this worked in the standalone project)
# - Also adding `@vercel/nft` v0.27.0 to pnpm `peerDependencyRules`
cp -r node_modules/.pnpm/import-in-the-middle@1.*/node_modules/import-in-the-middle .output/server/node_modules/import-in-the-middle
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
modules: ['@sentry/nuxt/module'],
imports: {
autoImport: false,
},
runtimeConfig: {
public: {
sentry: {
dsn: 'https://[email protected]/1337',
},
},
},
nitro: {
rollupConfig: {
// @sentry/... is set external to prevent bundling all of Sentry into the `runtime.mjs` file in the build output
external: [/@sentry\/.*/],
},
},
sentry: {
autoInjectServerSentry: 'experimental_dynamic-import',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "nuxt-3-dynamic-import",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build && bash ./copyIITM.bash",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"start": "node .output/server/index.mjs",
"clean": "npx nuxi cleanup",
"test": "playwright test",
"test:build": "pnpm install && npx playwright install && pnpm build",
"test:assert": "pnpm test"
},
"dependencies": {
"@sentry/nuxt": "latest || *",
"nuxt": "^3.14.0"
},
"devDependencies": {
"@nuxt/test-utils": "^3.14.1",
"@playwright/test": "^1.44.1",
"@sentry-internal/test-utils": "link:../../../test-utils"
},
"overrides": {
"nitropack": "~2.9.7",
"ofetch": "^1.4.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script setup>
import ErrorButton from '../components/ErrorButton.vue';
</script>

<template>
<ErrorButton id="errorBtn" error-text="Error thrown from Nuxt-3 E2E test app"/>
<ErrorButton id="errorBtn2" error-text="Another Error thrown from Nuxt-3 E2E test app"/>
</template>



Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<template>
<div>
<button @click="fetchData">Fetch Server Data</button>
</div>
</template>

<script setup lang="ts">
import { useFetch} from '#imports'
const fetchData = async () => {
await useFetch('/api/server-error');
}
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<h1>Hello!</h1>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script setup lang="ts">
import { useRoute, useFetch } from '#imports'
const route = useRoute();
const param = route.params.param;
const fetchError = async () => {
await useFetch(`/api/param-error/${param}`);
}
const fetchData = async () => {
await useFetch(`/api/test-param/${param}`);
};
</script>

<template>
<p>Param: {{ $route.params.param }}</p>

<ErrorButton id="errorBtn" errorText="Error thrown from Param Route Button" />
<button @click="fetchData">Fetch Server Data</button>
<button @click="fetchError">Fetch Server Error</button>
</template>

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { fileURLToPath } from 'node:url';
import type { ConfigOptions } from '@nuxt/test-utils/playwright';
import { getPlaywrightConfig } from '@sentry-internal/test-utils';

const nuxtConfigOptions: ConfigOptions = {
nuxt: {
rootDir: fileURLToPath(new URL('.', import.meta.url)),
},
};

/* Make sure to import from '@nuxt/test-utils/playwright' in the tests
* Like this: import { expect, test } from '@nuxt/test-utils/playwright' */

const config = getPlaywrightConfig({
startCommand: `pnpm start`,
use: { ...nuxtConfigOptions },
});

export default config;
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as Sentry from '@sentry/nuxt';
import { useRuntimeConfig } from '#imports';

Sentry.init({
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: useRuntimeConfig().public.sentry.dsn,
tunnel: `http://localhost:3031/`, // proxy server
tracesSampleRate: 1.0,
integrations: [
Sentry.vueIntegration({
tracingOptions: {
trackComponents: true,
},
}),
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as Sentry from '@sentry/nuxt';

Sentry.init({
dsn: 'https://[email protected]/1337',
environment: 'qa', // dynamic sampling bias to keep transactions
tracesSampleRate: 1.0, // Capture 100% of the transactions
tunnel: 'http://localhost:3031/', // proxy server
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { defineEventHandler } from '#imports';

export default defineEventHandler(_e => {
throw new Error('Nuxt 3 Param Server error');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { defineEventHandler } from '#imports';

export default defineEventHandler(event => {
throw new Error('Nuxt 3 Server error');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineEventHandler, getRouterParam } from '#imports';

export default defineEventHandler(event => {
const param = getRouterParam(event, 'param');

return `Param: ${param}!`;
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../.nuxt/tsconfig.server.json"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { startEventProxyServer } from '@sentry-internal/test-utils';

startEventProxyServer({
port: 3031,
proxyServerName: 'nuxt-3-dynamic-import',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { expect, test } from '@nuxt/test-utils/playwright';
import { waitForError } from '@sentry-internal/test-utils';

test.describe('client-side errors', async () => {
test('captures error thrown on click', async ({ page }) => {
const errorPromise = waitForError('nuxt-3-dynamic-import', async errorEvent => {
return errorEvent?.exception?.values?.[0]?.value === 'Error thrown from Nuxt-3 E2E test app';
});

await page.goto(`/client-error`);
await page.locator('#errorBtn').click();

const error = await errorPromise;

expect(error.transaction).toEqual('/client-error');
expect(error).toMatchObject({
exception: {
values: [
{
type: 'Error',
value: 'Error thrown from Nuxt-3 E2E test app',
mechanism: {
handled: false,
},
},
],
},
});
});

test('shows parametrized route on button error', async ({ page }) => {
const errorPromise = waitForError('nuxt-3-dynamic-import', async errorEvent => {
return errorEvent?.exception?.values?.[0]?.value === 'Error thrown from Param Route Button';
});

await page.goto(`/test-param/1234`);
await page.locator('#errorBtn').click();

const error = await errorPromise;

expect(error.sdk.name).toEqual('sentry.javascript.nuxt');
expect(error.transaction).toEqual('/test-param/:param()');
expect(error.request.url).toMatch(/\/test-param\/1234/);
expect(error).toMatchObject({
exception: {
values: [
{
type: 'Error',
value: 'Error thrown from Param Route Button',
mechanism: {
handled: false,
},
},
],
},
});
});

test('page is still interactive after client error', async ({ page }) => {
const error1Promise = waitForError('nuxt-3-dynamic-import', async errorEvent => {
return errorEvent?.exception?.values?.[0]?.value === 'Error thrown from Nuxt-3 E2E test app';
});

await page.goto(`/client-error`);
await page.locator('#errorBtn').click();

const error1 = await error1Promise;

const error2Promise = waitForError('nuxt-3-dynamic-import', async errorEvent => {
return errorEvent?.exception?.values?.[0]?.value === 'Another Error thrown from Nuxt-3 E2E test app';
});

await page.locator('#errorBtn2').click();

const error2 = await error2Promise;

expect(error1).toMatchObject({
exception: {
values: [
{
type: 'Error',
value: 'Error thrown from Nuxt-3 E2E test app',
mechanism: {
handled: false,
},
},
],
},
});

expect(error2).toMatchObject({
exception: {
values: [
{
type: 'Error',
value: 'Another Error thrown from Nuxt-3 E2E test app',
mechanism: {
handled: false,
},
},
],
},
});
});
});
Loading

0 comments on commit 121ae07

Please sign in to comment.