diff --git a/packages/nextjs/src/config/loaders/dataFetchersLoader.ts b/packages/nextjs/src/config/loaders/dataFetchersLoader.ts index 9f42e86ab6e9..a1a75a5d8268 100644 --- a/packages/nextjs/src/config/loaders/dataFetchersLoader.ts +++ b/packages/nextjs/src/config/loaders/dataFetchersLoader.ts @@ -37,6 +37,7 @@ const DATA_FETCHING_FUNCTIONS = { type LoaderOptions = { projectDir: string; + pageRegex: RegExp; }; /** @@ -108,7 +109,7 @@ export default function wrapDataFetchersLoader(this: LoaderThis, } // We know one or the other will be defined, depending on the version of webpack being used - const { projectDir } = 'getOptions' in this ? this.getOptions() : this.query; + const { projectDir, pageRegex } = 'getOptions' in this ? this.getOptions() : this.query; // In the following branch we will proxy the user's file. This means we return code (basically an entirely new file) // that re - exports all the user file's originial export, but with a "sentry-proxy-loader" query in the module @@ -170,12 +171,23 @@ export default function wrapDataFetchersLoader(this: LoaderThis, path.relative(projectDir, this.resourcePath), ); + // Get the route name from this page's filepath + let route = this.resourcePath.match(pageRegex)?.[2]; + if (!route) { + logger.warn(`Unable to wrap code from page ${this.resourcePath}, because the route regex has no matches.`); + return userCode; + } + // Fill in template placeholders let injectedCode = modifiedTemplateCode; for (const { placeholder, alias } of Object.values(DATA_FETCHING_FUNCTIONS)) { injectedCode = injectedCode.replace(new RegExp(placeholder, 'g'), alias); } + // Any route ending in '/index' will correspond to the root of that directory, '/'. + route = route.replace(/index$/, ''); + injectedCode = injectedCode.replace('__FILEPATH__', route); + return `${modifiedUserCode}\n${injectedCode}`; } } diff --git a/packages/nextjs/src/config/templates/dataFetchersLoaderTemplate.ts b/packages/nextjs/src/config/templates/dataFetchersLoaderTemplate.ts index 78788d6826ac..66a75c599148 100644 --- a/packages/nextjs/src/config/templates/dataFetchersLoaderTemplate.ts +++ b/packages/nextjs/src/config/templates/dataFetchersLoaderTemplate.ts @@ -17,6 +17,8 @@ declare const __ORIG_GSPROPS__: GetStaticPropsFunction; // eslint-disable-next-line import/no-extraneous-dependencies, import/no-unresolved import * as ServerSideSentryNextjsSDK from '@sentry/nextjs'; +const PARAMETERIZED_ROUTE = '__FILEPATH__'; + export const getServerSideProps = typeof __ORIG_GSSP__ === 'function' ? ServerSideSentryNextjsSDK.withSentryGetServerSideProps(__ORIG_GSSP__) diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index a636609a9a7b..109f37653d0c 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -56,8 +56,6 @@ export function constructWebpackConfigFunction( newConfig = userNextConfig.webpack(newConfig, buildContext); } - const pageRegex = new RegExp(`${escapeStringForRegex(projectDir)}(/src)?/pages(/.+)\\.(jsx?|tsx?)`); - if (isServer) { newConfig.module = { ...newConfig.module, @@ -80,13 +78,25 @@ export function constructWebpackConfigFunction( ], }; + // There are a few different things going on in this regex: + // - It is being used here as a matcher for which files to process with the loader, and will be used in the + // loader itself to isolate the route at which the page will be served (represented here by the `/.*` between + // `pages` and `index`) + // - `/src` is optional because the `pages` directory can either be at the root level of the project or in a + // `src` directory + // - `/?index` isn't included in the capturing group for the route because `a/b/c/index.js` will be served at + // `a/b/c/` + // - In `/?index`, the slash is optional because the route `/` will already have had its single slash consumed + // by the `/.*` + const pageRegex = new RegExp(`${escapeStringForRegex(projectDir)}(/src)?/pages(/.*)(/?index)?\\.(jsx?|tsx?)`); + if (userSentryOptions.experiments?.autoWrapDataFetchers) { newConfig.module.rules.push({ test: pageRegex, use: [ { loader: path.resolve(__dirname, 'loaders/dataFetchersLoader.js'), - options: { projectDir }, + options: { projectDir, pageRegex }, }, ], });