Skip to content

Commit 2c09d9a

Browse files
authored
feat(browser): Export Replay integration from Browser SDK (#6508)
Export the `Replay` integration from `@sentry/replay` in `@sentry/browser`. This will eliminate the need for users to separately install the `@sentry/replay` package as they have to do right now. Instead, they can just use Replay like it's part of the core Browser SDK or any FE Framework SDK that builds on top of Browser: ```js import * as Sentry from '@sentry/browser'; // or @sentry/react, ... Sentry.init({ // dsn, other options, etc replaysSessionSampleRate: 0.1, replaysOnErrorSampleRate: 1.0, integrations: [new Sentry.Replay()] }); ``` Alternatively, `import { Replay } from '@sentry/browser'` works, too. Note: This does not affect users who imported the integration from `@sentry/replay` so it should be fully backwards compatible.
1 parent ca30609 commit 2c09d9a

File tree

12 files changed

+93
-26
lines changed

12 files changed

+93
-26
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"karma-firefox-launcher": "^1.1.0",
8888
"lerna": "3.13.4",
8989
"madge": "4.0.2",
90+
"magic-string": "^0.27.0",
9091
"mocha": "^6.1.4",
9192
"nodemon": "^2.0.16",
9293
"npm-run-all": "^4.1.5",

packages/browser/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
},
1818
"dependencies": {
1919
"@sentry/core": "7.26.0",
20+
"@sentry/replay": "7.26.0",
2021
"@sentry/types": "7.26.0",
2122
"@sentry/utils": "7.26.0",
2223
"tslib": "^1.9.3"

packages/browser/src/client.ts

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,20 @@
11
import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, Scope, SDK_VERSION } from '@sentry/core';
2-
import { ClientOptions, Event, EventHint, Options, Severity, SeverityLevel } from '@sentry/types';
2+
import {
3+
BrowserClientReplayOptions,
4+
ClientOptions,
5+
Event,
6+
EventHint,
7+
Options,
8+
Severity,
9+
SeverityLevel,
10+
} from '@sentry/types';
311
import { createClientReportEnvelope, dsnToString, logger, serializeEnvelope } from '@sentry/utils';
412

513
import { eventFromException, eventFromMessage } from './eventbuilder';
614
import { WINDOW } from './helpers';
715
import { Breadcrumbs } from './integrations';
816
import { BREADCRUMB_INTEGRATION_ID } from './integrations/breadcrumbs';
917
import { BrowserTransportOptions } from './transports/types';
10-
11-
type BrowserClientReplayOptions = {
12-
/**
13-
* The sample rate for session-long replays.
14-
* 1.0 will record all sessions and 0 will record none.
15-
*/
16-
replaysSessionSampleRate?: number;
17-
18-
/**
19-
* The sample rate for sessions that has had an error occur.
20-
* This is independent of `sessionSampleRate`.
21-
* 1.0 will record all sessions and 0 will record none.
22-
*/
23-
replaysOnErrorSampleRate?: number;
24-
};
25-
2618
/**
2719
* Configuration options for the Sentry Browser SDK.
2820
* @see @sentry/types Options for more information.

packages/browser/src/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,12 @@ const INTEGRATIONS = {
1919
};
2020

2121
export { INTEGRATIONS as Integrations };
22+
23+
// DO NOT DELETE THESE COMMENTS!
24+
// We want to exclude Replay from CDN bundles, so we remove the block below with our
25+
// excludeReplay Rollup plugin when generating bundles. Everything between
26+
// ROLLUP_EXCLUDE_FROM_BUNDLES_BEGIN and _END__ is removed for bundles.
27+
28+
// __ROLLUP_EXCLUDE_FROM_BUNDLES_BEGIN__
29+
export { Replay } from '@sentry/replay';
30+
// __ROLLUP_EXCLUDE_FROM_BUNDLES_END__

packages/replay/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
"homepage": "https://docs.sentry.io/platforms/javascript/session-replay/",
4747
"devDependencies": {
4848
"@babel/core": "^7.17.5",
49-
"@sentry/browser": "7.26.0",
5049
"@types/lodash.debounce": "4.0.7",
5150
"@types/pako": "^2.0.0",
5251
"jsdom-worker": "^0.2.1",

packages/replay/src/integration.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { BrowserClient, BrowserOptions } from '@sentry/browser';
21
import { getCurrentHub } from '@sentry/core';
2+
import type { BrowserClientReplayOptions } from '@sentry/types';
33
import { Integration } from '@sentry/types';
44

55
import { DEFAULT_ERROR_SAMPLE_RATE, DEFAULT_SESSION_SAMPLE_RATE, MASK_ALL_TEXT_SELECTOR } from './constants';
@@ -184,8 +184,8 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
184184

185185
/** Parse Replay-related options from SDK options */
186186
private _loadReplayOptionsFromClient(): void {
187-
const client = getCurrentHub().getClient() as BrowserClient | undefined;
188-
const opt = client && (client.getOptions() as BrowserOptions | undefined);
187+
const client = getCurrentHub().getClient();
188+
const opt = client && (client.getOptions() as BrowserClientReplayOptions | undefined);
189189

190190
if (opt && typeof opt.replaysSessionSampleRate === 'number') {
191191
this.options.sessionSampleRate = opt.replaysSessionSampleRate;

packages/types/src/browseroptions.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Options added to the Browser SDK's init options that are specific for Replay.
3+
* Note: This type was moved to @sentry/types to avoid a circular dependency between Browser and Replay.
4+
*/
5+
export type BrowserClientReplayOptions = {
6+
/**
7+
* The sample rate for session-long replays.
8+
* 1.0 will record all sessions and 0 will record none.
9+
*/
10+
replaysSessionSampleRate?: number;
11+
12+
/**
13+
* The sample rate for sessions that has had an error occur.
14+
* This is independent of `sessionSampleRate`.
15+
* 1.0 will record all sessions and 0 will record none.
16+
*/
17+
replaysOnErrorSampleRate?: number;
18+
};

packages/types/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,5 @@ export type {
9393
export type { User, UserFeedback } from './user';
9494
export type { WrappedFunction } from './wrappedfunction';
9595
export type { Instrumenter } from './instrumenter';
96+
97+
export type { BrowserClientReplayOptions } from './browseroptions';

rollup/bundleHelpers.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
makeSucrasePlugin,
1717
makeTerserPlugin,
1818
makeTSPlugin,
19+
makeExcludeReplayPlugin,
1920
} from './plugins/index.js';
2021
import { mergePlugins } from './utils';
2122

@@ -30,6 +31,7 @@ export function makeBaseBundleConfig(options) {
3031
const markAsBrowserBuildPlugin = makeBrowserBuildPlugin(true);
3132
const licensePlugin = makeLicensePlugin(licenseTitle);
3233
const tsPlugin = makeTSPlugin(jsVersion.toLowerCase());
34+
const excludeReplayPlugin = makeExcludeReplayPlugin();
3335

3436
// The `commonjs` plugin is the `esModuleInterop` of the bundling world. When used with `transformMixedEsModules`, it
3537
// will include all dependencies, imported or required, in the final bundle. (Without it, CJS modules aren't included
@@ -43,7 +45,7 @@ export function makeBaseBundleConfig(options) {
4345
name: 'Sentry',
4446
},
4547
context: 'window',
46-
plugins: [markAsBrowserBuildPlugin],
48+
plugins: [markAsBrowserBuildPlugin, excludeReplayPlugin],
4749
};
4850

4951
// used by `@sentry/integrations` and `@sentry/wasm` (bundles which need to be combined with a stand-alone SDK bundle)

rollup/plugins/bundlePlugins.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88
* Typescript plugin docs: https://github.com/ezolenko/rollup-plugin-typescript2
99
*/
1010

11+
import path from 'path';
12+
1113
import commonjs from '@rollup/plugin-commonjs';
1214
import deepMerge from 'deepmerge';
1315
import license from 'rollup-plugin-license';
1416
import resolve from '@rollup/plugin-node-resolve';
1517
import replace from '@rollup/plugin-replace';
1618
import { terser } from 'rollup-plugin-terser';
1719
import typescript from 'rollup-plugin-typescript2';
20+
import MagicString from 'magic-string';
1821

1922
/**
2023
* Create a plugin to add an identification banner to the top of stand-alone bundles.
@@ -164,6 +167,37 @@ export function makeTSPlugin(jsVersion) {
164167
return plugin;
165168
}
166169

170+
/**
171+
* Creates a Rollup plugin that removes all code between the `__ROLLUP_EXCLUDE_FROM_BUNDLES_BEGIN__`
172+
* and `__ROLLUP_EXCLUDE_FROM_BUNDLES_END__` comment guards. This is used to exclude the Replay integration
173+
* from the browser and browser+tracing bundles.
174+
* If we need to add more such guards in the future, we might want to refactor this into a more generic plugin.
175+
*/
176+
export function makeExcludeReplayPlugin() {
177+
const replacementRegex = /\/\/ __ROLLUP_EXCLUDE_FROM_BUNDLES_BEGIN__(.|\n)*__ROLLUP_EXCLUDE_FROM_BUNDLES_END__/m;
178+
const browserIndexFilePath = path.resolve(__dirname, '../../packages/browser/src/index.ts');
179+
180+
const plugin = {
181+
transform(code, id) {
182+
const isBrowserIndexFile = path.resolve(id) === browserIndexFilePath;
183+
if (!isBrowserIndexFile || !replacementRegex.test(code)) {
184+
return null;
185+
}
186+
187+
const ms = new MagicString(code);
188+
const transformedCode = ms.replace(replacementRegex, '');
189+
return {
190+
code: transformedCode.toString(),
191+
map: transformedCode.generateMap({ hires: true }),
192+
};
193+
},
194+
};
195+
196+
plugin.name = 'excludeReplay';
197+
198+
return plugin;
199+
}
200+
167201
// We don't pass these plugins any options which need to be calculated or changed by us, so no need to wrap them in
168202
// another factory function, as they are themselves already factory functions.
169203
export { resolve as makeNodeResolvePlugin };

rollup/utils.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ export const insertAt = (arr, index, ...insertees) => {
1616
export function mergePlugins(pluginsA, pluginsB) {
1717
const plugins = [...pluginsA, ...pluginsB];
1818
plugins.sort((a, b) => {
19-
// Hacky way to make sure the ones we care about end up where they belong in the order. (Really the TS and sucrase
19+
// Hacky way to make sure the ones we care about end up where they belong in the order. Really the TS and sucrase
2020
// plugins are tied - both should come first - but they're mutually exclusive, so they can come in arbitrary order
21-
// here.)
22-
const order = ['typescript', 'sucrase', '...', 'terser', 'license'];
21+
// here.
22+
// Additionally, the excludeReplay plugin must run before TS/Sucrase so that we can eliminate the replay code
23+
// before anything is type-checked (TS-only) and transpiled.
24+
const order = ['excludeReplay', 'typescript', 'sucrase', '...', 'terser', 'license'];
2325
const sortKeyA = order.includes(a.name) ? a.name : '...';
2426
const sortKeyB = order.includes(b.name) ? b.name : '...';
2527

yarn.lock

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2232,7 +2232,7 @@
22322232
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
22332233
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
22342234

2235-
"@jridgewell/[email protected]", "@jridgewell/sourcemap-codec@^1.4.10":
2235+
"@jridgewell/[email protected]", "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13":
22362236
version "1.4.14"
22372237
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
22382238
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
@@ -16502,6 +16502,13 @@ magic-string@^0.26.2:
1650216502
dependencies:
1650316503
sourcemap-codec "^1.4.8"
1650416504

16505+
magic-string@^0.27.0:
16506+
version "0.27.0"
16507+
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3"
16508+
integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==
16509+
dependencies:
16510+
"@jridgewell/sourcemap-codec" "^1.4.13"
16511+
1650516512
make-dir@^1.0.0:
1650616513
version "1.3.0"
1650716514
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"

0 commit comments

Comments
 (0)