diff --git a/packages/nextjs/src/config/loaders/wrappingLoader.ts b/packages/nextjs/src/config/loaders/wrappingLoader.ts index 3742724ae4fb..1e7aac4b7c8a 100644 --- a/packages/nextjs/src/config/loaders/wrappingLoader.ts +++ b/packages/nextjs/src/config/loaders/wrappingLoader.ts @@ -153,8 +153,15 @@ async function wrapUserCode( // People may use `module.exports` in their API routes or page files. Next.js allows that and we also need to // handle that correctly so we let a plugin to take care of bundling cjs exports for us. commonjs({ - transformMixedEsModules: true, sourceMap: true, + strictRequires: true, // Don't hoist require statements that users may define + ignoreDynamicRequires: true, // Don't break dynamic requires and things like Webpack's `require.context` + ignore() { + // We want basically only want to use this plugin for handling the case where users export their handlers with module.exports. + // This plugin would also be able to convert any `require` into something esm compatible but webpack does that anyways so we just skip that part of the plugin. + // (Also, modifying require may break user code) + return true; + }, }), ], diff --git a/packages/nextjs/test/integration/pages/api/requireTest.ts b/packages/nextjs/test/integration/pages/api/requireTest.ts new file mode 100644 index 000000000000..cb0674d28c42 --- /dev/null +++ b/packages/nextjs/test/integration/pages/api/requireTest.ts @@ -0,0 +1,14 @@ +import { NextApiResponse, NextApiRequest } from 'next'; + +if (process.env.NEXT_PUBLIC_SOME_FALSE_ENV_VAR === 'enabled') { + require('../../test/server/utils/throw'); // Should not throw unless the hoisting in the wrapping loader is messed up! +} + +const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise => { + require('@sentry/nextjs').captureException; // Should not throw unless the wrapping loader messes up cjs imports + // @ts-ignore + require.context('.'); // This is a webpack utility call. Should not throw unless the wrapping loader messes it up by mangling. + res.status(200).json({ success: true }); +}; + +module.exports = handler; diff --git a/packages/nextjs/test/integration/test/server/cjsApiEndpoints.test.ts b/packages/nextjs/test/integration/test/server/cjsApiEndpoints.test.ts index 510a8c917e4e..fc0ae186f64b 100644 --- a/packages/nextjs/test/integration/test/server/cjsApiEndpoints.test.ts +++ b/packages/nextjs/test/integration/test/server/cjsApiEndpoints.test.ts @@ -66,4 +66,37 @@ describe('CommonJS API Endpoints', () => { success: true, }); }); + + it('should not mess up require statements', async () => { + const env = await NextTestEnv.init(); + const route = '/api/requireTest'; + const url = `${env.url}${route}`; + + const wrappedEnvelope = await env.getEnvelopeRequest({ + url, + envelopeType: 'transaction', + endServer: false, + }); + + expect(wrappedEnvelope[2]).toMatchObject({ + contexts: { + trace: { + op: 'http.server', + status: 'ok', + tags: { 'http.status_code': '200' }, + }, + }, + transaction: `GET ${route}`, + type: 'transaction', + request: { + url, + }, + }); + + const response = await env.getAPIResponse(url); + + expect(response).toMatchObject({ + success: true, + }); + }); }); diff --git a/packages/nextjs/test/integration/test/server/utils/throw.js b/packages/nextjs/test/integration/test/server/utils/throw.js new file mode 100644 index 000000000000..0e37a4135be4 --- /dev/null +++ b/packages/nextjs/test/integration/test/server/utils/throw.js @@ -0,0 +1 @@ +throw new Error('I am throwing');