Skip to content

Commit

Permalink
fix(nextjs): Detect new locations for request async storage to suppor…
Browse files Browse the repository at this point in the history
…t Next.js v15.0.0-canary.180 and higher (#13920)
  • Loading branch information
ykzts authored Oct 10, 2024
1 parent 0be3137 commit 6b4401f
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"@types/node": "18.11.17",
"@types/react": "18.0.26",
"@types/react-dom": "18.0.9",
"next": "15.0.0-canary.112",
"next": "15.0.0-canary.182",
"react": "beta",
"react-dom": "beta",
"typescript": "4.9.5"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,29 @@ import * as Sentry from '@sentry/nextjs';
import type { WebFetchHeaders } from '@sentry/types';
// @ts-expect-error Because we cannot be sure if the RequestAsyncStorage module exists (it is not part of the Next.js public
// API) we use a shim if it doesn't exist. The logic for this is in the wrapping loader.
import { requestAsyncStorage } from '__SENTRY_NEXTJS_REQUEST_ASYNC_STORAGE_SHIM__';
import * as origModule from '__SENTRY_NEXTJS_REQUEST_ASYNC_STORAGE_SHIM__';
// @ts-expect-error See above
import * as routeModule from '__SENTRY_WRAPPING_TARGET_FILE__';

import type { RequestAsyncStorage } from './requestAsyncStorageShim';

type NextAsyncStorageModule =
| {
workUnitAsyncStorage: RequestAsyncStorage;
}
| {
requestAsyncStorage: RequestAsyncStorage;
};

const asyncStorageModule = { ...origModule } as NextAsyncStorageModule;

const requestAsyncStorage: RequestAsyncStorage | undefined =
'workUnitAsyncStorage' in asyncStorageModule
? asyncStorageModule.workUnitAsyncStorage
: 'requestAsyncStorage' in asyncStorageModule
? asyncStorageModule.requestAsyncStorage
: undefined;

function wrapHandler<T>(handler: T, method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'): T {
// Running the instrumentation code during the build phase will mark any function as "dynamic" because we're accessing
// the Request object. We do not want to turn handlers dynamic so we skip instrumentation in the build phase.
Expand All @@ -28,7 +45,7 @@ function wrapHandler<T>(handler: T, method: 'GET' | 'POST' | 'PUT' | 'PATCH' | '
// We try-catch here just in case the API around `requestAsyncStorage` changes unexpectedly since it is not public API
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const requestAsyncStore = requestAsyncStorage.getStore() as ReturnType<RequestAsyncStorage['getStore']>;
const requestAsyncStore = requestAsyncStorage?.getStore() as ReturnType<RequestAsyncStorage['getStore']>;
sentryTraceHeader = requestAsyncStore?.headers.get('sentry-trace') ?? undefined;
baggageHeader = requestAsyncStore?.headers.get('baggage') ?? undefined;
headers = requestAsyncStore?.headers;
Expand All @@ -54,8 +71,6 @@ export * from '__SENTRY_WRAPPING_TARGET_FILE__';
// @ts-expect-error This is the file we're wrapping
export { default } from '__SENTRY_WRAPPING_TARGET_FILE__';

declare const requestAsyncStorage: RequestAsyncStorage;

type RouteHandler = (...args: unknown[]) => unknown;

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,29 @@ import * as Sentry from '@sentry/nextjs';
import type { WebFetchHeaders } from '@sentry/types';
// @ts-expect-error Because we cannot be sure if the RequestAsyncStorage module exists (it is not part of the Next.js public
// API) we use a shim if it doesn't exist. The logic for this is in the wrapping loader.
// biome-ignore lint/nursery/noUnusedImports: Biome doesn't understand the shim with variable import path
import { requestAsyncStorage } from '__SENTRY_NEXTJS_REQUEST_ASYNC_STORAGE_SHIM__';
import * as origModule from '__SENTRY_NEXTJS_REQUEST_ASYNC_STORAGE_SHIM__';
// @ts-expect-error We use `__SENTRY_WRAPPING_TARGET_FILE__` as a placeholder for the path to the file being wrapped.
// biome-ignore lint/nursery/noUnusedImports: Biome doesn't understand the shim with variable import path
import * as serverComponentModule from '__SENTRY_WRAPPING_TARGET_FILE__';

import type { RequestAsyncStorage } from './requestAsyncStorageShim';

declare const requestAsyncStorage: RequestAsyncStorage;
type NextAsyncStorageModule =
| {
workUnitAsyncStorage: RequestAsyncStorage;
}
| {
requestAsyncStorage: RequestAsyncStorage;
};

const asyncStorageModule = { ...origModule } as NextAsyncStorageModule;

const requestAsyncStorage: RequestAsyncStorage | undefined =
'workUnitAsyncStorage' in asyncStorageModule
? asyncStorageModule.workUnitAsyncStorage
: 'requestAsyncStorage' in asyncStorageModule
? asyncStorageModule.requestAsyncStorage
: undefined;

declare const serverComponentModule: {
default: unknown;
Expand All @@ -34,7 +48,8 @@ if (typeof serverComponent === 'function') {

// We try-catch here just in `requestAsyncStorage` is undefined since it may not be defined
try {
const requestAsyncStore = requestAsyncStorage.getStore();
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const requestAsyncStore = requestAsyncStorage?.getStore() as ReturnType<RequestAsyncStorage['getStore']>;
sentryTraceHeader = requestAsyncStore?.headers.get('sentry-trace') ?? undefined;
baggageHeader = requestAsyncStore?.headers.get('baggage') ?? undefined;
headers = requestAsyncStore?.headers;
Expand Down
6 changes: 6 additions & 0 deletions packages/nextjs/src/config/webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,12 @@ const POTENTIAL_REQUEST_ASYNC_STORAGE_LOCATIONS = [
// Introduced in Next.js 13.4.20
// https://github.com/vercel/next.js/blob/e1bc270830f2fc2df3542d4ef4c61b916c802df3/packages/next/src/client/components/request-async-storage.external.ts
'next/dist/client/components/request-async-storage.external.js',
// Introduced in Next.js 15.0.0-canary.180
// https://github.com/vercel/next.js/blob/541167b9b0fed6af9f36472e632863ffec41f18c/packages/next/src/server/app-render/work-unit-async-storage.external.ts
'next/dist/server/app-render/work-unit-async-storage.external.js',
// Introduced in Next.js 15.0.0-canary.182
// https://github.com/vercel/next.js/blob/f35159e5e80138ca7373f57b47edcaae3bcf1728/packages/next/src/client/components/work-unit-async-storage.external.ts
'next/dist/client/components/work-unit-async-storage.external.js',
];

function getRequestAsyncStorageModuleLocation(
Expand Down

0 comments on commit 6b4401f

Please sign in to comment.