diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/.gitignore b/packages/e2e-tests/test-applications/nextjs-13-app-dir/.gitignore new file mode 100644 index 000000000000..35b1048ce099 --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/.gitignore @@ -0,0 +1,43 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +!*.d.ts + +# Sentry +.sentryclirc + +.vscode diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/.npmrc b/packages/e2e-tests/test-applications/nextjs-13-app-dir/.npmrc new file mode 100644 index 000000000000..c6b3ef9b3eaa --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://localhost:4873 +@sentry-internal:registry=http://localhost:4873 diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/head.tsx b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/head.tsx new file mode 100644 index 000000000000..f11b259697ff --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/head.tsx @@ -0,0 +1,10 @@ +export default function Head() { + return ( + <> + Create Next App + + + + + ); +} diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/layout.tsx b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/layout.tsx new file mode 100644 index 000000000000..f3ef34cd8b91 --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/layout.tsx @@ -0,0 +1,7 @@ +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/page.tsx b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/page.tsx new file mode 100644 index 000000000000..1caf96ed7786 --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/page.tsx @@ -0,0 +1,22 @@ +'use client'; + +import * as Sentry from '@sentry/nextjs'; +import Link from 'next/link'; + +export default function Home() { + return ( +
+ { + Sentry.captureException(new Error('I am a click error!')); + }} + /> + + navigate + +
+ ); +} diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/user/[id]/page.tsx b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/user/[id]/page.tsx new file mode 100644 index 000000000000..bdb52ea5547a --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/user/[id]/page.tsx @@ -0,0 +1,4 @@ +export default async function Home() { + const dynamid = await (await fetch('http://example.com', { cache: 'no-store' })).text(); // do a fetch request so that this server component is always rendered when requested + return

I am a blank page :) {dynamid}

; +} diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/globals.d.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/globals.d.ts new file mode 100644 index 000000000000..109dbcd55648 --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/globals.d.ts @@ -0,0 +1,4 @@ +interface Window { + recordedTransactions?: string[]; + capturedExceptionId?: string; +} diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/next-env.d.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/next-env.d.ts new file mode 100644 index 000000000000..7aa8e8ef74e1 --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/next.config.js b/packages/e2e-tests/test-applications/nextjs-13-app-dir/next.config.js new file mode 100644 index 000000000000..b4110295ace3 --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/next.config.js @@ -0,0 +1,33 @@ +// This file sets a custom webpack configuration to use your Next.js app +// with Sentry. +// https://nextjs.org/docs/api-reference/next.config.js/introduction +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +const { withSentryConfig } = require('@sentry/nextjs'); + +const moduleExports = { + experimental: { + appDir: true, + }, +}; + +const sentryWebpackPluginOptions = { + // Additional config options for the Sentry Webpack plugin. Keep in mind that + // the following options are set automatically, and overriding them is not + // recommended: + // release, url, org, project, authToken, configFile, stripPrefix, + // urlPrefix, include, ignore + + silent: true, // Suppresses all logs + // For all available options, see: + // https://github.com/getsentry/sentry-webpack-plugin#options. + + // We're not testing source map uploads at the moment. + dryRun: true, +}; + +// Make sure adding Sentry options is the last code to run before exporting, to +// ensure that your source maps include changes from all other Webpack plugins +module.exports = withSentryConfig(moduleExports, sentryWebpackPluginOptions, { + hideSourceMaps: true, +}); diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/package.json b/packages/e2e-tests/test-applications/nextjs-13-app-dir/package.json new file mode 100644 index 000000000000..2bb563d85e25 --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/package.json @@ -0,0 +1,27 @@ +{ + "name": "create-next-app", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "test": "playwright test" + }, + "dependencies": { + "@next/font": "13.0.7", + "@sentry/nextjs": "*", + "@types/node": "18.11.17", + "@types/react": "18.0.26", + "@types/react-dom": "18.0.9", + "next": "13.2.1", + "react": "18.2.0", + "react-dom": "18.2.0", + "typescript": "4.9.4" + }, + "devDependencies": { + "ts-node": "10.9.1", + "@playwright/test": "^1.27.1" + } +} diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/playwright.config.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/playwright.config.ts new file mode 100644 index 000000000000..55e54aefaefa --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/playwright.config.ts @@ -0,0 +1,62 @@ +import type { PlaywrightTestConfig } from '@playwright/test'; +import { devices } from '@playwright/test'; + +/** + * See https://playwright.dev/docs/test-configuration. + */ +const config: PlaywrightTestConfig = { + testDir: './tests', + /* Maximum time one test can run for. */ + timeout: 60 * 1000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 5000, + }, + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: 0, + /* Opt out of parallel tests on CI. */ + workers: 1, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'dot', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 0, + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: 'http://localhost:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + }, + }, + ], + + /* Run your local dev server before starting the tests */ + webServer: [ + { + command: 'yarn start', + port: 3000, + }, + { + command: 'yarn ts-node-script start-event-proxy.ts', + port: 27496, + }, + ], +}; + +export default config; diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.client.config.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.client.config.ts new file mode 100644 index 000000000000..af39dd76f384 --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.client.config.ts @@ -0,0 +1,7 @@ +import * as Sentry from '@sentry/nextjs'; + +Sentry.init({ + dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, + tunnel: 'http://localhost:27496/', // proxy server + tracesSampleRate: 1.0, +}); diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.edge.config.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.edge.config.ts new file mode 100644 index 000000000000..af39dd76f384 --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.edge.config.ts @@ -0,0 +1,7 @@ +import * as Sentry from '@sentry/nextjs'; + +Sentry.init({ + dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, + tunnel: 'http://localhost:27496/', // proxy server + tracesSampleRate: 1.0, +}); diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.server.config.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.server.config.ts new file mode 100644 index 000000000000..af39dd76f384 --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.server.config.ts @@ -0,0 +1,7 @@ +import * as Sentry from '@sentry/nextjs'; + +Sentry.init({ + dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, + tunnel: 'http://localhost:27496/', // proxy server + tracesSampleRate: 1.0, +}); diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/start-event-proxy.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/start-event-proxy.ts new file mode 100644 index 000000000000..9c959e1e180e --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/start-event-proxy.ts @@ -0,0 +1,6 @@ +import { startEventProxyServer, waitForTransaction } from '../../test-utils/event-proxy-server'; + +startEventProxyServer({ + port: 27496, + proxyServerName: 'nextjs-13-app-dir', +}); diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/test-recipe.json b/packages/e2e-tests/test-applications/nextjs-13-app-dir/test-recipe.json new file mode 100644 index 000000000000..bfdef9508d7b --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/test-recipe.json @@ -0,0 +1,11 @@ +{ + "$schema": "../../test-recipe-schema.json", + "testApplicationName": "nextjs-13-app-dir", + "buildCommand": "yarn install --pure-lockfile && npx playwright install && yarn build", + "tests": [ + { + "testName": "Playwright tests", + "testCommand": "yarn test" + } + ] +} diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/tests/exceptions.test.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/tests/exceptions.test.ts new file mode 100644 index 000000000000..d1b88470fd37 --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/tests/exceptions.test.ts @@ -0,0 +1,50 @@ +import { test, expect } from '@playwright/test'; +import { waitForError } from '../../../test-utils/event-proxy-server'; +import axios, { AxiosError } from 'axios'; + +const authToken = process.env.E2E_TEST_AUTH_TOKEN; +const sentryTestOrgSlug = process.env.E2E_TEST_SENTRY_ORG_SLUG; +const sentryTestProject = process.env.E2E_TEST_SENTRY_TEST_PROJECT; +const EVENT_POLLING_TIMEOUT = 30_000; + +test('Sends a client-side exception to Sentry', async ({ page }) => { + await page.goto('/'); + + const errorEventPromise = waitForError('nextjs-13-app-dir', errorEvent => { + return errorEvent?.exception?.values?.[0]?.value === 'I am a click error!'; + }); + + const exceptionButton = page.locator('id=exception-button'); + await exceptionButton.click(); + + const errorEvent = await errorEventPromise; + const exceptionEventId = errorEvent.event_id; + + await expect + .poll( + async () => { + try { + const response = await axios.get( + `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${exceptionEventId}/`, + { headers: { Authorization: `Bearer ${authToken}` } }, + ); + + return response.status; + } catch (e) { + if (e instanceof AxiosError && e.response) { + if (e.response.status !== 404) { + throw e; + } else { + return e.response.status; + } + } else { + throw e; + } + } + }, + { + timeout: EVENT_POLLING_TIMEOUT, + }, + ) + .toBe(200); +}); diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/tests/transactions.test.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/tests/transactions.test.ts new file mode 100644 index 000000000000..c52ab35475bd --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/tests/transactions.test.ts @@ -0,0 +1,89 @@ +import { test, expect } from '@playwright/test'; +import { waitForTransaction } from '../../../test-utils/event-proxy-server'; +import axios, { AxiosError } from 'axios'; + +const authToken = process.env.E2E_TEST_AUTH_TOKEN; +const sentryTestOrgSlug = process.env.E2E_TEST_SENTRY_ORG_SLUG; +const sentryTestProject = process.env.E2E_TEST_SENTRY_TEST_PROJECT; +const EVENT_POLLING_TIMEOUT = 30_000; + +test('Sends a pageload transaction', async ({ page }) => { + const pageloadTransactionEventPromise = waitForTransaction('nextjs-13-app-dir', transactionEvent => { + return transactionEvent?.contexts?.trace?.op === 'pageload' && transactionEvent?.transaction === '/'; + }); + + await page.goto('/'); + + const transactionEvent = await pageloadTransactionEventPromise; + const transactionEventId = transactionEvent.event_id; + + await expect + .poll( + async () => { + try { + const response = await axios.get( + `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${transactionEventId}/`, + { headers: { Authorization: `Bearer ${authToken}` } }, + ); + + return response.status; + } catch (e) { + if (e instanceof AxiosError && e.response) { + if (e.response.status !== 404) { + throw e; + } else { + return e.response.status; + } + } else { + throw e; + } + } + }, + { + timeout: EVENT_POLLING_TIMEOUT, + }, + ) + .toBe(200); +}); + +test('Sends a transaction for a server component', async ({ page }) => { + const serverComponentTransactionPromise = waitForTransaction('nextjs-13-app-dir', transactionEvent => { + return ( + transactionEvent?.contexts?.trace?.op === 'function.nextjs' && + transactionEvent?.transaction === 'Page Server Component (/user/[id])' + ); + }); + + await page.goto('/user/4'); + + const transactionEvent = await serverComponentTransactionPromise; + const transactionEventId = transactionEvent.event_id; + + await expect + .poll( + async () => { + try { + const response = await axios.get( + `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${transactionEventId}/`, + { headers: { Authorization: `Bearer ${authToken}` } }, + ); + + return response.status; + } catch (e) { + if (e instanceof AxiosError && e.response) { + if (e.response.status !== 404) { + throw e; + } else { + return e.response.status; + } + } else { + throw e; + } + } + }, + { + timeout: EVENT_POLLING_TIMEOUT, + }, + ) + .toBe(200); +}); diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/tsconfig.json b/packages/e2e-tests/test-applications/nextjs-13-app-dir/tsconfig.json new file mode 100644 index 000000000000..bacd391b697e --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ] + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "next.config.js", ".next/types/**/*.ts"], + "exclude": ["node_modules"], + "ts-node": { + "compilerOptions": { + "module": "CommonJS" + } + } +} diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/yarn.lock b/packages/e2e-tests/test-applications/nextjs-13-app-dir/yarn.lock new file mode 100644 index 000000000000..14727ded24f5 --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/yarn.lock @@ -0,0 +1,369 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@next/env@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.2.1.tgz#082d42cfc0c794e9185d7b4133d71440ba2e795d" + integrity sha512-Hq+6QZ6kgmloCg8Kgrix+4F0HtvLqVK3FZAnlAoS0eonaDemHe1Km4kwjSWRE3JNpJNcKxFHF+jsZrYo0SxWoQ== + +"@next/font@13.0.7": + version "13.0.7" + resolved "https://registry.yarnpkg.com/@next/font/-/font-13.0.7.tgz#e0046376edb0ce592d9cfddea8f4ab321eb1515a" + integrity sha512-39SzuoMI6jbrIzPs3KtXdKX03OrVp6Y7kRHcoVmOg69spiBzruPJ5x5DQSfN+OXqznbvVBNZBXnmdnSqs3qXiA== + +"@next/swc-android-arm-eabi@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.2.1.tgz#67f2580fbbe05ee006220688972c5e3a555fc741" + integrity sha512-Yua7mUpEd1wzIT6Jjl3dpRizIfGp9NR4F2xeRuQv+ae+SDI1Em2WyM9m46UL+oeW5GpMiEHoaBagr47RScZFmQ== + +"@next/swc-android-arm64@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.2.1.tgz#460a02b69eb23bb5f402266bcea9cadae59415c1" + integrity sha512-Bifcr2f6VwInOdq1uH/9lp8fH7Nf7XGkIx4XceVd32LPJqG2c6FZU8ZRBvTdhxzXVpt5TPtuXhOP4Ij9UPqsVw== + +"@next/swc-darwin-arm64@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.2.1.tgz#8b8530ff417802027471aee2419f78a58a863ccb" + integrity sha512-gvqm+fGMYxAkwBapH0Vvng5yrb6HTkIvZfY4oEdwwYrwuLdkjqnJygCMgpNqIFmAHSXgtlWxfYv1VC8sjN81Kw== + +"@next/swc-darwin-x64@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.2.1.tgz#80aebb3329a1e4568a28de1ee177780b3d50330c" + integrity sha512-HGqVqmaZWj6zomqOZUVbO5NhlABL0iIaxTmd0O5B0MoMa5zpDGoaHSG+fxgcWMXcGcxmUNchv1NfNOYiTKoHOg== + +"@next/swc-freebsd-x64@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.2.1.tgz#250ea2ab7e1734f22d11c677c463fab9ac33a516" + integrity sha512-N/a4JarAq+E+g+9K2ywJUmDIgU2xs2nA+BBldH0oq4zYJMRiUhL0iaN9G4e72VmGOJ61L/3W6VN8RIUOwTLoqQ== + +"@next/swc-linux-arm-gnueabihf@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.2.1.tgz#fe6bb29ed348a5f8ecae3740df22a8d8130c474a" + integrity sha512-WaFoerF/eRbhbE57TaIGJXbQAERADZ/RZ45u6qox9beb5xnWsyYgzX+WuN7Tkhyvga0/aMuVYFzS9CEay7D+bw== + +"@next/swc-linux-arm64-gnu@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.2.1.tgz#4781b927fc5e421f3cea2b29e5d38e5e4837b198" + integrity sha512-R+Jhc1/RJTnncE9fkePboHDNOCm1WJ8daanWbjKhfPySMyeniKYRwGn5SLYW3S8YlRS0QVdZaaszDSZWgUcsmA== + +"@next/swc-linux-arm64-musl@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.2.1.tgz#c2ba0a121b0255ba62450916bc70e6d0e26cbc98" + integrity sha512-oI1UfZPidGAVddlL2eOTmfsuKV9EaT1aktIzVIxIAgxzQSdwsV371gU3G55ggkurzfdlgF3GThFePDWF0d8dmw== + +"@next/swc-linux-x64-gnu@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.2.1.tgz#573c220f8b087e5d131d1fba58d3e1a670b220ad" + integrity sha512-PCygPwrQmS+7WUuAWWioWMZCzZm4PG91lfRxToLDg7yIm/3YfAw5N2EK2TaM9pzlWdvHQAqRMX/oLvv027xUiA== + +"@next/swc-linux-x64-musl@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.2.1.tgz#950b5bb920b322ca7b447efbd12a9c7a10c3a642" + integrity sha512-sUAKxo7CFZYGHNxheGh9nIBElLYBM6md/liEGfOTwh/xna4/GTTcmkGWkF7PdnvaYNgcPIQgHIMYiAa6yBKAVw== + +"@next/swc-win32-arm64-msvc@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.2.1.tgz#dbff3c4f5a3812a7059dac05804148a0f98682db" + integrity sha512-qDmyEjDBpl/vBXxuOOKKWmPQOcARcZIMach1s7kjzaien0SySut/PHRlj56sosa81Wt4hTGhfhZ1R7g1n7+B8w== + +"@next/swc-win32-ia32-msvc@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.2.1.tgz#7d2c17be7b8d9963984f5c15cc2588127101f620" + integrity sha512-2joqFQ81ZYPg6DcikIzQn3DgjKglNhPAozx6dL5sCNkr1CPMD0YIkJgT3CnYyMHQ04Qi3Npv0XX3MD6LJO8OCA== + +"@next/swc-win32-x64-msvc@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.2.1.tgz#09713c6a925461f414e89422851326d1625bd4d2" + integrity sha512-r3+0fSaIZT6N237iMzwUhfNwjhAFvXjqB+4iuW+wcpxW+LHm1g/IoxN8eSRcb8jPItC86JxjAxpke0QL97qd6g== + +"@playwright/test@^1.27.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.31.1.tgz#39d6873dc46af135f12451d79707db7d1357455d" + integrity sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA== + dependencies: + "@types/node" "*" + playwright-core "1.31.1" + optionalDependencies: + fsevents "2.3.2" + +"@swc/helpers@0.4.14": + version "0.4.14" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74" + integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw== + dependencies: + tslib "^2.4.0" + +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + +"@types/node@*": + version "18.14.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.1.tgz#90dad8476f1e42797c49d6f8b69aaf9f876fc69f" + integrity sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ== + +"@types/node@18.11.17": + version "18.11.17" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.17.tgz#5c009e1d9c38f4a2a9d45c0b0c493fe6cdb4bcb5" + integrity sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng== + +"@types/prop-types@*": + version "15.7.5" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" + integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== + +"@types/react-dom@18.0.9": + version "18.0.9" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.9.tgz#ffee5e4bfc2a2f8774b15496474f8e7fe8d0b504" + integrity sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg== + dependencies: + "@types/react" "*" + +"@types/react@*": + version "18.0.28" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.28.tgz#accaeb8b86f4908057ad629a26635fe641480065" + integrity sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/react@18.0.26": + version "18.0.26" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.26.tgz#8ad59fc01fef8eaf5c74f4ea392621749f0b7917" + integrity sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/scheduler@*": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" + integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^8.4.1: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +caniuse-lite@^1.0.30001406: + version "1.0.30001457" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz#6af34bb5d720074e2099432aa522c21555a18301" + integrity sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA== + +client-only@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" + integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +csstype@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" + integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +fsevents@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +loose-envify@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +nanoid@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + +next@13.2.1: + version "13.2.1" + resolved "https://registry.yarnpkg.com/next/-/next-13.2.1.tgz#34d823f518632b36379863228ed9f861c335b9c0" + integrity sha512-qhgJlDtG0xidNViJUPeQHLGJJoT4zDj/El7fP3D3OzpxJDUfxsm16cK4WTMyvSX1ciIfAq05u+0HqFAa+VJ+Hg== + dependencies: + "@next/env" "13.2.1" + "@swc/helpers" "0.4.14" + caniuse-lite "^1.0.30001406" + postcss "8.4.14" + styled-jsx "5.1.1" + optionalDependencies: + "@next/swc-android-arm-eabi" "13.2.1" + "@next/swc-android-arm64" "13.2.1" + "@next/swc-darwin-arm64" "13.2.1" + "@next/swc-darwin-x64" "13.2.1" + "@next/swc-freebsd-x64" "13.2.1" + "@next/swc-linux-arm-gnueabihf" "13.2.1" + "@next/swc-linux-arm64-gnu" "13.2.1" + "@next/swc-linux-arm64-musl" "13.2.1" + "@next/swc-linux-x64-gnu" "13.2.1" + "@next/swc-linux-x64-musl" "13.2.1" + "@next/swc-win32-arm64-msvc" "13.2.1" + "@next/swc-win32-ia32-msvc" "13.2.1" + "@next/swc-win32-x64-msvc" "13.2.1" + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +playwright-core@1.31.1: + version "1.31.1" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.31.1.tgz#4deeebbb8fb73b512593fe24bea206d8fd85ff7f" + integrity sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ== + +postcss@8.4.14: + version "8.4.14" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" + integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +react-dom@18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react@18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +styled-jsx@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f" + integrity sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw== + dependencies: + client-only "0.0.1" + +ts-node@10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +tslib@^2.4.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== + +typescript@4.9.4: + version "4.9.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" + integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== diff --git a/packages/nextjs/rollup.npm.config.js b/packages/nextjs/rollup.npm.config.js index 63f420f466ad..5b23aa2fa0a4 100644 --- a/packages/nextjs/rollup.npm.config.js +++ b/packages/nextjs/rollup.npm.config.js @@ -16,7 +16,7 @@ export default [ // prevent this internal nextjs code from ending up in our built package (this doesn't happen automatially because // the name doesn't match an SDK dependency) - packageSpecificConfig: { external: ['next/router', 'next/constants'] }, + packageSpecificConfig: { external: ['next/router', 'next/constants', 'next/headers'] }, }), ), ...makeNPMConfigVariants( @@ -41,7 +41,7 @@ export default [ // make it so Rollup calms down about the fact that we're combining default and named exports exports: 'named', }, - external: ['@sentry/nextjs', '__SENTRY_WRAPPING_TARGET_FILE__'], + external: ['@sentry/nextjs', 'next/headers', '__SENTRY_WRAPPING_TARGET_FILE__'], }, }), ), diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts index 38700f1a02b4..9894067d69e2 100644 --- a/packages/nextjs/src/client/index.ts +++ b/packages/nextjs/src/client/index.ts @@ -148,5 +148,3 @@ export { withSentryGetStaticProps, wrapGetStaticPropsWithSentry, } from './wrapGetStaticPropsWithSentry'; - -export { wrapAppDirComponentWithSentry } from './wrapAppDirComponentWithSentry'; diff --git a/packages/nextjs/src/client/wrapAppDirComponentWithSentry.ts b/packages/nextjs/src/client/wrapAppDirComponentWithSentry.ts deleted file mode 100644 index 767f2beb5be7..000000000000 --- a/packages/nextjs/src/client/wrapAppDirComponentWithSentry.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Currently just a pass-through to provide isomorphism for the client. May be used in the future to add instrumentation - * for client components. - */ -export function wrapAppDirComponentWithSentry(wrappingTarget: any): any { - return wrappingTarget; -} diff --git a/packages/nextjs/src/common/types.ts b/packages/nextjs/src/common/types.ts new file mode 100644 index 000000000000..d21f3fa92880 --- /dev/null +++ b/packages/nextjs/src/common/types.ts @@ -0,0 +1,4 @@ +export type ServerComponentContext = { + componentRoute: string; + componentType: string; +}; diff --git a/packages/nextjs/src/config/loaders/wrappingLoader.ts b/packages/nextjs/src/config/loaders/wrappingLoader.ts index eba33dfb7ac3..879855cafa08 100644 --- a/packages/nextjs/src/config/loaders/wrappingLoader.ts +++ b/packages/nextjs/src/config/loaders/wrappingLoader.ts @@ -34,7 +34,7 @@ type LoaderOptions = { appDir: string; pageExtensionRegex: string; excludeServerRoutes: Array; - wrappingTargetKind: 'page' | 'api-route' | 'middleware' | 'page-server-component'; + wrappingTargetKind: 'page' | 'api-route' | 'middleware' | 'server-component'; }; /** @@ -95,14 +95,14 @@ export default function wrappingLoader( // Inject the route and the path to the file we're wrapping into the template templateCode = templateCode.replace(/__ROUTE__/g, parameterizedPagesRoute.replace(/\\/g, '\\\\')); - } else if (wrappingTargetKind === 'page-server-component') { + } else if (wrappingTargetKind === 'server-component') { // Get the parameterized route name from this page's filepath const parameterizedPagesRoute = path.posix .normalize(path.relative(appDir, this.resourcePath)) // Add a slash at the beginning .replace(/(.*)/, '/$1') // Pull off the file name - .replace(/\/page\.(js|jsx|tsx)$/, '') + .replace(/\/[^/]+\.(js|jsx|tsx)$/, '') // Remove routing groups: https://beta.nextjs.org/docs/routing/defining-routes#example-creating-multiple-root-layouts .replace(/\/(\(.*?\)\/)+/g, '/') // In case all of the above have left us with an empty string (which will happen if we're dealing with the @@ -125,6 +125,36 @@ export default function wrappingLoader( } templateCode = serverComponentWrapperTemplateCode; + + templateCode = templateCode.replace(/__ROUTE__/g, parameterizedPagesRoute.replace(/\\/g, '\\\\')); + + const componentTypeMatch = path.posix + .normalize(path.relative(appDir, this.resourcePath)) + .match(/\/?([^/]+)\.(?:js|jsx|tsx)$/); + + if (componentTypeMatch && componentTypeMatch[1]) { + let componentType; + switch (componentTypeMatch[1]) { + case 'page': + componentType = 'Page'; + break; + case 'layout': + componentType = 'Layout'; + break; + case 'head': + componentType = 'Head'; + break; + case 'not-found': + componentType = 'Not-found'; + break; + default: + componentType = 'Unknown'; + } + + templateCode = templateCode.replace(/__COMPONENT_TYPE__/g, componentType); + } else { + templateCode = templateCode.replace(/__COMPONENT_TYPE__/g, 'Unknown'); + } } else if (wrappingTargetKind === 'middleware') { templateCode = middlewareWrapperTemplateCode; } else { diff --git a/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts b/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts index 4ba89238aad8..74e8e1a5b1c3 100644 --- a/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts +++ b/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts @@ -22,7 +22,10 @@ const serverComponent = serverComponentModule.default; let wrappedServerComponent; if (typeof serverComponent === 'function') { - wrappedServerComponent = Sentry.wrapAppDirComponentWithSentry(serverComponent); + wrappedServerComponent = Sentry.wrapServerComponentWithSentry(serverComponent, { + componentRoute: '__ROUTE__', + componentType: '__COMPONENT_TYPE__', + }); } else { wrappedServerComponent = serverComponent; } diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index 0849ff761ea4..7fd142fc8aa8 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -87,12 +87,18 @@ export function constructWebpackConfigFunction( }); let pagesDirPath: string; - let appDirPath: string; - if (fs.existsSync(path.join(projectDir, 'pages')) && fs.lstatSync(path.join(projectDir, 'pages')).isDirectory()) { + const maybePagesDirPath = path.join(projectDir, 'pages'); + if (fs.existsSync(maybePagesDirPath) && fs.lstatSync(maybePagesDirPath).isDirectory()) { pagesDirPath = path.join(projectDir, 'pages'); - appDirPath = path.join(projectDir, 'app'); } else { pagesDirPath = path.join(projectDir, 'src', 'pages'); + } + + let appDirPath: string; + const maybeAppDirPath = path.join(projectDir, 'app'); + if (fs.existsSync(maybeAppDirPath) && fs.lstatSync(maybeAppDirPath).isDirectory()) { + appDirPath = path.join(projectDir, 'app'); + } else { appDirPath = path.join(projectDir, 'src', 'app'); } @@ -199,7 +205,7 @@ export function constructWebpackConfigFunction( // https://beta.nextjs.org/docs/routing/pages-and-layouts#pages:~:text=.js%2C%20.jsx%2C%20or%20.tsx%20file%20extensions%20can%20be%20used%20for%20Pages. return ( normalizedAbsoluteResourcePath.startsWith(appDirPath) && - !!normalizedAbsoluteResourcePath.match(/[\\/]page\.(js|jsx|tsx)$/) + !!normalizedAbsoluteResourcePath.match(/[\\/](page|layout|loading|head|not-found)\.(js|jsx|tsx)$/) ); }, use: [ @@ -207,7 +213,7 @@ export function constructWebpackConfigFunction( loader: path.resolve(__dirname, 'loaders', 'wrappingLoader.js'), options: { ...staticWrappingLoaderOptions, - wrappingTargetKind: 'page-server-component', + wrappingTargetKind: 'server-component', }, }, ], diff --git a/packages/nextjs/src/edge/index.ts b/packages/nextjs/src/edge/index.ts index 087e0a1482d5..6f8cd2f42cc4 100644 --- a/packages/nextjs/src/edge/index.ts +++ b/packages/nextjs/src/edge/index.ts @@ -140,4 +140,4 @@ export { export { wrapMiddlewareWithSentry } from './wrapMiddlewareWithSentry'; -export { wrapAppDirComponentWithSentry } from './wrapAppDirComponentWithSentry'; +export { wrapServerComponentWithSentry } from './wrapServerComponentWithSentry'; diff --git a/packages/nextjs/src/edge/wrapAppDirComponentWithSentry.ts b/packages/nextjs/src/edge/wrapServerComponentWithSentry.ts similarity index 82% rename from packages/nextjs/src/edge/wrapAppDirComponentWithSentry.ts rename to packages/nextjs/src/edge/wrapServerComponentWithSentry.ts index 116df75a4caa..349207e7b039 100644 --- a/packages/nextjs/src/edge/wrapAppDirComponentWithSentry.ts +++ b/packages/nextjs/src/edge/wrapServerComponentWithSentry.ts @@ -1,9 +1,14 @@ import { captureException } from '@sentry/core'; +import type { ServerComponentContext } from '../common/types'; + /** * Wraps an `app` directory server component with Sentry error instrumentation. */ -export function wrapAppDirComponentWithSentry any>(appDirComponent: F): F { +export function wrapServerComponentWithSentry any>( + appDirComponent: F, + _context: ServerComponentContext, +): F { // Even though users may define server components as async functions, for the client bundles // Next.js will turn them into synchronous functions and it will transform any`await`s into instances of the`use` // hook. 🤯 diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts index eb5fb9ca258f..47c24e44751f 100644 --- a/packages/nextjs/src/index.types.ts +++ b/packages/nextjs/src/index.types.ts @@ -10,6 +10,7 @@ export * from './edge'; import type { Integration, Options, StackParser } from '@sentry/types'; import type * as clientSdk from './client'; +import type { ServerComponentContext } from './common/types'; import type * as edgeSdk from './edge'; import type * as serverSdk from './server'; @@ -168,6 +169,9 @@ export declare function withSentryGetStaticProps a ): (...args: Parameters) => ReturnType extends Promise ? ReturnType : Promise>; /** - * Wraps an `app` directory component with Sentry error instrumentation. (Currently only reports errors for server components) + * Wraps an `app` directory server component with Sentry error and performance instrumentation. */ -export declare function wrapAppDirComponentWithSentry any>(WrappingTarget: F): F; +export declare function wrapServerComponentWithSentry any>( + WrappingTarget: F, + context: ServerComponentContext, +): F; diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index 348eff6778d9..8c16195aa570 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -223,4 +223,4 @@ export { wrapApiHandlerWithSentry, } from './wrapApiHandlerWithSentry'; -export { wrapAppDirComponentWithSentry } from './wrapAppDirComponentWithSentry'; +export { wrapServerComponentWithSentry } from './wrapServerComponentWithSentry'; diff --git a/packages/nextjs/src/server/wrapAppDirComponentWithSentry.ts b/packages/nextjs/src/server/wrapAppDirComponentWithSentry.ts deleted file mode 100644 index 116df75a4caa..000000000000 --- a/packages/nextjs/src/server/wrapAppDirComponentWithSentry.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { captureException } from '@sentry/core'; - -/** - * Wraps an `app` directory server component with Sentry error instrumentation. - */ -export function wrapAppDirComponentWithSentry any>(appDirComponent: F): F { - // Even though users may define server components as async functions, for the client bundles - // Next.js will turn them into synchronous functions and it will transform any`await`s into instances of the`use` - // hook. 🤯 - return new Proxy(appDirComponent, { - apply: (originalFunction, thisArg, args) => { - let maybePromiseResult; - - try { - maybePromiseResult = originalFunction.apply(thisArg, args); - } catch (e) { - captureException(e); - throw e; - } - - if (typeof maybePromiseResult === 'object' && maybePromiseResult !== null && 'then' in maybePromiseResult) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - return maybePromiseResult.then(null, (e: Error) => { - captureException(e); - throw e; - }); - } else { - return maybePromiseResult; - } - }, - }); -} diff --git a/packages/nextjs/src/server/wrapServerComponentWithSentry.ts b/packages/nextjs/src/server/wrapServerComponentWithSentry.ts new file mode 100644 index 000000000000..05c17c6bede3 --- /dev/null +++ b/packages/nextjs/src/server/wrapServerComponentWithSentry.ts @@ -0,0 +1,67 @@ +import { captureException, getCurrentHub, startTransaction } from '@sentry/core'; +import * as domain from 'domain'; + +import type { ServerComponentContext } from '../common/types'; + +/** + * Wraps an `app` directory server component with Sentry error instrumentation. + */ +export function wrapServerComponentWithSentry any>( + appDirComponent: F, + context: ServerComponentContext, +): F { + const { componentRoute, componentType } = context; + + // Even though users may define server components as async functions, for the client bundles + // Next.js will turn them into synchronous functions and it will transform any `await`s into instances of the `use` + // hook. 🤯 + return new Proxy(appDirComponent, { + apply: (originalFunction, thisArg, args) => { + return domain.create().bind(() => { + let maybePromiseResult; + + const transaction = startTransaction({ + op: 'function.nextjs', + name: `${componentType} Server Component (${componentRoute})`, + status: 'ok', + metadata: { + source: 'component', + }, + }); + + const currentScope = getCurrentHub().getScope(); + if (currentScope) { + currentScope.setSpan(transaction); + } + + try { + maybePromiseResult = originalFunction.apply(thisArg, args); + } catch (e) { + transaction.setStatus('internal_error'); + captureException(e); + transaction.finish(); + throw e; + } + + if (typeof maybePromiseResult === 'object' && maybePromiseResult !== null && 'then' in maybePromiseResult) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + return maybePromiseResult.then( + (res: unknown) => { + transaction.finish(); + return res; + }, + (e: Error) => { + transaction.setStatus('internal_error'); + captureException(e); + transaction.finish(); + throw e; + }, + ); + } else { + transaction.finish(); + return maybePromiseResult; + } + })(); + }, + }); +} diff --git a/packages/nextjs/test/config/loaders.test.ts b/packages/nextjs/test/config/loaders.test.ts index 84fb39ea80da..94a6a5ec0654 100644 --- a/packages/nextjs/test/config/loaders.test.ts +++ b/packages/nextjs/test/config/loaders.test.ts @@ -157,11 +157,11 @@ describe('webpack loaders', () => { }, { resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/page.js', - expectedWrappingTargetKind: 'page-server-component', + expectedWrappingTargetKind: 'server-component', }, { resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/nested/page.js', - expectedWrappingTargetKind: 'page-server-component', + expectedWrappingTargetKind: 'server-component', }, { resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/nested/page.ts', // ts is not a valid file ending for pages in the app dir @@ -169,7 +169,7 @@ describe('webpack loaders', () => { }, { resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/(group)/nested/page.tsx', - expectedWrappingTargetKind: 'page-server-component', + expectedWrappingTargetKind: 'server-component', }, { resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/(group)/nested/loading.ts', @@ -177,7 +177,7 @@ describe('webpack loaders', () => { }, { resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/layout.js', - expectedWrappingTargetKind: undefined, + expectedWrappingTargetKind: 'server-component', }, ])( 'should apply the right wrappingTargetKind with wrapping loader ($resourcePath)',