Skip to content

Commit

Permalink
ref(nextjs): Exclude cross-platform tracing code from bundles (#3978)
Browse files Browse the repository at this point in the history
The `@sentry/tracing` package contains shared code, code which only applies to browser, and code which only applies to node. At the moment, webpack is unable to treeshake the former out of the server build and the latter out of the client build. This PR does that treeshaking manually, taking advantage of webpack 5’s new ability[1] to replace a module with an empty object by setting `resolve.alias.<someModule> = false` in the webpack config.

Notes:

- All node tracing integrations have been moved into a `node` folder, so we don't need to worry that at some point a non-node integration might get added to `integrations` and be accidentally excluded.

- Normally we only care about bundle size on the browser side, but since Vercel turns server routes into serverless functions, bundle size matters on the backend, too, so the change was made in both directions.

- As a point of reference, on my test app it took the sentry portion of the client `_app` bundle down from ~32.3 kb to ~30.6 kb, which is a little over a 5% savings.

[1] https://webpack.js.org/configuration/resolve/#resolvealias
  • Loading branch information
lobsterkatie committed Sep 14, 2021
1 parent 42dffe5 commit c8a941c
Show file tree
Hide file tree
Showing 9 changed files with 29 additions and 8 deletions.
3 changes: 3 additions & 0 deletions packages/nextjs/src/config/types.ts
Expand Up @@ -42,6 +42,9 @@ export type WebpackConfigObject = {
output: { filename: string; path: string };
target: string;
context: string;
resolve?: {
alias?: { [key: string]: string | boolean };
};
} & {
// other webpack options
[key: string]: unknown;
Expand Down
18 changes: 18 additions & 0 deletions packages/nextjs/src/config/webpack.ts
Expand Up @@ -60,6 +60,24 @@ export function constructWebpackConfigFunction(
const origEntryProperty = newConfig.entry;
newConfig.entry = async () => addSentryToEntryProperty(origEntryProperty, buildContext);

// In webpack 5, you can get webpack to replace any module you'd like with an empty object, just by setting its
// `resolve.alias` value to `false`. Not much of our code is neatly separated into "things node needs" and "things
// the browser needs," but where it is, we can save ~1.6 kb in eventual bundle size by excluding code we know we
// don't need. (Normally this would only matter for the client side, but because vercel turns backend code into
// serverless functions, it's worthwhile to do it for both.)
if (buildContext.webpack.version.startsWith('5')) {
const excludedTracingDir = buildContext.isServer ? 'browser' : 'integrations/node';
newConfig.resolve = {
...newConfig.resolve,
alias: {
...newConfig.resolve?.alias,
[path.resolve(buildContext.dir, `./node_modules/@sentry/tracing/esm/${excludedTracingDir}`)]: false,
// TODO It's not clear if it will ever pull from `dist` (in testing it never does), so we may not need this.
[path.resolve(buildContext.dir, `./node_modules/@sentry/tracing/dist/${excludedTracingDir}`)]: false,
},
};
}

// Enable the Sentry plugin (which uploads source maps to Sentry when not in dev) by default
const enableWebpackPlugin = buildContext.isServer
? !userNextConfig.sentry?.disableServerWebpackPlugin
Expand Down
8 changes: 4 additions & 4 deletions packages/tracing/src/integrations/index.ts
@@ -1,4 +1,4 @@
export { Express } from './express';
export { Postgres } from './postgres';
export { Mysql } from './mysql';
export { Mongo } from './mongo';
export { Express } from './node/express';
export { Postgres } from './node/postgres';
export { Mysql } from './node/mysql';
export { Mongo } from './node/mongo';
File renamed without changes.
File renamed without changes.
@@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/unbound-method */
import { Hub, Scope } from '@sentry/hub';

import { Mongo } from '../../src/integrations/mongo';
import { Span } from '../../src/span';
import { Mongo } from '../../../src/integrations/node/mongo';
import { Span } from '../../../src/span';

class Collection {
public collectionName: string = 'mockedCollectionName';
Expand Down
@@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/unbound-method */
import { Hub, Scope } from '@sentry/hub';

import { Postgres } from '../../src/integrations/postgres';
import { Span } from '../../src/span';
import { Postgres } from '../../../src/integrations/node/postgres';
import { Span } from '../../../src/span';

class PgClient {
// https://node-postgres.com/api/client#clientquery
Expand Down

0 comments on commit c8a941c

Please sign in to comment.