Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions packages/nextjs/src/config/webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,13 @@ async function addSentryToEntryProperty(
// we know is that it won't have gotten *simpler* in form, so we only need to worry about the object and function
// options. See https://webpack.js.org/configuration/entry-context/#entry.

const { isServer, dir: projectDir, dev: isDev, config: userNextConfig } = buildContext;

const newEntryProperty =
typeof currentEntryProperty === 'function' ? await currentEntryProperty() : { ...currentEntryProperty };

// `sentry.server.config.js` or `sentry.client.config.js` (or their TS equivalents)
const userConfigFile = buildContext.isServer
? getUserConfigFile(buildContext.dir, 'server')
: getUserConfigFile(buildContext.dir, 'client');
const userConfigFile = isServer ? getUserConfigFile(projectDir, 'server') : getUserConfigFile(projectDir, 'client');

// we need to turn the filename into a path so webpack can find it
const filesToInject = [`./${userConfigFile}`];
Expand All @@ -131,12 +131,12 @@ async function addSentryToEntryProperty(
// server SDK's default `RewriteFrames` instance (which needs it at runtime). Doesn't work when using the dev server
// because it somehow tricks the file watcher into thinking that compilation itself is a file change, triggering an
// infinite recompiling loop. (This should be fine because we don't upload sourcemaps in dev in any case.)
if (buildContext.isServer && !buildContext.dev) {
if (isServer && !isDev) {
const rewriteFramesHelper = path.resolve(
fs.mkdtempSync(path.resolve(os.tmpdir(), 'sentry-')),
'rewriteFramesHelper.js',
);
fs.writeFileSync(rewriteFramesHelper, `global.__rewriteFramesDistDir__ = '${buildContext.config.distDir}';\n`);
fs.writeFileSync(rewriteFramesHelper, `global.__rewriteFramesDistDir__ = '${userNextConfig.distDir}';\n`);
// stick our helper file ahead of the user's config file so the value is in the global namespace *before*
// `Sentry.init()` is called
filesToInject.unshift(rewriteFramesHelper);
Expand Down Expand Up @@ -269,13 +269,13 @@ export function getWebpackPluginOptions(
buildContext: BuildContext,
userPluginOptions: Partial<SentryWebpackPluginOptions>,
): SentryWebpackPluginOptions {
const { isServer, dir: projectDir, buildId, dev: isDev, config: nextConfig, webpack } = buildContext;
const distDir = nextConfig.distDir ?? '.next'; // `.next` is the default directory
const { buildId, isServer, webpack, config: userNextConfig, dev: isDev, dir: projectDir } = buildContext;
const distDir = userNextConfig.distDir ?? '.next'; // `.next` is the default directory

const isWebpack5 = webpack.version.startsWith('5');
const isServerless = nextConfig.target === 'experimental-serverless-trace';
const isServerless = userNextConfig.target === 'experimental-serverless-trace';
const hasSentryProperties = fs.existsSync(path.resolve(projectDir, 'sentry.properties'));
const urlPrefix = nextConfig.basePath ? `~${nextConfig.basePath}/_next` : '~/_next';
const urlPrefix = userNextConfig.basePath ? `~${userNextConfig.basePath}/_next` : '~/_next';

const serverInclude = isServerless
? [{ paths: [`${distDir}/serverless/`], urlPrefix: `${urlPrefix}/serverless` }]
Expand Down
66 changes: 57 additions & 9 deletions packages/nextjs/test/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,11 +315,11 @@ describe('webpack config', () => {

expect(finalWebpackConfig.entry).toEqual(
expect.objectContaining({
// original entry point value is a string
// original entrypoint value is a string
// (was 'private-next-pages/api/dogs/[name].js')
'pages/api/dogs/[name]': [rewriteFramesHelper, serverConfigFilePath, 'private-next-pages/api/dogs/[name].js'],

// original entry point value is a string array
// original entrypoint value is a string array
// (was ['./node_modules/smellOVision/index.js', 'private-next-pages/_app.js'])
'pages/_app': [
rewriteFramesHelper,
Expand All @@ -328,14 +328,14 @@ describe('webpack config', () => {
'private-next-pages/_app.js',
],

// original entry point value is an object containing a string `import` value
// (`import` was 'private-next-pages/api/simulator/dogStats/[name].js')
// original entrypoint value is an object containing a string `import` value
// (was { import: 'private-next-pages/api/simulator/dogStats/[name].js' })
'pages/api/simulator/dogStats/[name]': {
import: [rewriteFramesHelper, serverConfigFilePath, 'private-next-pages/api/simulator/dogStats/[name].js'],
},

// original entry point value is an object containing a string array `import` value
// (`import` was ['./node_modules/dogPoints/converter.js', 'private-next-pages/api/simulator/leaderboard.js'])
// original entrypoint value is an object containing a string array `import` value
// (was { import: ['./node_modules/dogPoints/converter.js', 'private-next-pages/api/simulator/leaderboard.js'] })
'pages/api/simulator/leaderboard': {
import: [
rewriteFramesHelper,
Expand All @@ -345,16 +345,64 @@ describe('webpack config', () => {
],
},

// original entry point value is an object containg properties besides `import`
// (`dependOn` remains untouched)
// original entrypoint value is an object containg properties besides `import`
// (was { import: 'private-next-pages/api/tricks/[trickName].js', dependOn: 'treats', })
'pages/api/tricks/[trickName]': {
import: [rewriteFramesHelper, serverConfigFilePath, 'private-next-pages/api/tricks/[trickName].js'],
dependOn: 'treats',
dependOn: 'treats', // untouched
},
}),
);
});

it('injects user config file into `_app` in both server and client bundles', async () => {
const finalServerWebpackConfig = await materializeFinalWebpackConfig({
userNextConfig,
incomingWebpackConfig: serverWebpackConfig,
incomingWebpackBuildContext: serverBuildContext,
});
const finalClientWebpackConfig = await materializeFinalWebpackConfig({
userNextConfig,
incomingWebpackConfig: clientWebpackConfig,
incomingWebpackBuildContext: clientBuildContext,
});

expect(finalServerWebpackConfig.entry).toEqual(
expect.objectContaining({
'pages/_app': expect.arrayContaining([serverConfigFilePath]),
}),
);
expect(finalClientWebpackConfig.entry).toEqual(
expect.objectContaining({
'pages/_app': expect.arrayContaining([clientConfigFilePath]),
}),
);
});

it('injects user config file into API routes', async () => {
const finalWebpackConfig = await materializeFinalWebpackConfig({
userNextConfig,
incomingWebpackConfig: serverWebpackConfig,
incomingWebpackBuildContext: serverBuildContext,
});

expect(finalWebpackConfig.entry).toEqual(
expect.objectContaining({
'pages/api/simulator/dogStats/[name]': {
import: expect.arrayContaining([serverConfigFilePath]),
},

'pages/api/simulator/leaderboard': {
import: expect.arrayContaining([serverConfigFilePath]),
},

'pages/api/tricks/[trickName]': expect.objectContaining({
import: expect.arrayContaining([serverConfigFilePath]),
}),
}),
);
});

it('does not inject anything into non-_app, non-API routes', async () => {
const finalWebpackConfig = await materializeFinalWebpackConfig({
userNextConfig,
Expand Down