Skip to content

Commit 4bff5a9

Browse files
authored
feat(bundles): Ensure CDN bundles always have a Replay export (#7414)
1 parent ba99e7c commit 4bff5a9

23 files changed

+367
-92
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"packages/hub",
4848
"packages/integration-tests",
4949
"packages/integrations",
50+
"packages/integration-shims",
5051
"packages/overhead-metrics",
5152
"packages/nextjs",
5253
"packages/node",
@@ -105,6 +106,7 @@
105106
"rollup": "^2.67.1",
106107
"rollup-plugin-cleanup": "3.2.1",
107108
"rollup-plugin-license": "^2.6.1",
109+
"rollup-plugin-modify": "^3.0.0",
108110
"rollup-plugin-terser": "^7.0.2",
109111
"rollup-plugin-typescript2": "^0.31.2",
110112
"sinon": "^7.3.2",

packages/browser/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"tslib": "^1.9.3"
2424
},
2525
"devDependencies": {
26+
"@sentry-internal/integration-shims": "7.43.0",
2627
"@types/md5": "2.1.33",
2728
"btoa": "^1.2.1",
2829
"chai": "^4.1.2",

packages/browser/rollup.bundle.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const builds = [];
88
entrypoints: ['src/index.ts'],
99
jsVersion,
1010
licenseTitle: '@sentry/browser',
11+
includeReplay: 'shim',
1112
outputFileBase: () => `bundles/bundle${jsVersion === 'es5' ? '.es5' : ''}`,
1213
});
1314

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Note: All paths are relative to the directory in which eslint is being run, rather than the directory where this file
2+
// lives
3+
4+
// ESLint config docs: https://eslint.org/docs/user-guide/configuring/
5+
6+
module.exports = {
7+
extends: ['../../.eslintrc.js'],
8+
};

packages/integration-shims/LICENSE

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Copyright (c) 2023 Sentry (https://sentry.io) and individual contributors. All rights reserved.
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4+
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
5+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
6+
persons to whom the Software is furnished to do so, subject to the following conditions:
7+
8+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
9+
Software.
10+
11+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
12+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
13+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

packages/integration-shims/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<p align="center">
2+
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank">
3+
<img src="https://sentry-brand.storage.googleapis.com/sentry-wordmark-dark-280x84.png" alt="Sentry" width="280" height="84">
4+
</a>
5+
</p>
6+
7+
# Sentry Integration Shims
8+
9+
This internal package exports shims for Integrations, which are used in order to guarantee a consistent CDN bundle output.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "@sentry-internal/integration-shims",
3+
"version": "7.43.0",
4+
"description": "Shims for integrations in Sentry SDK.",
5+
"main": "build/cjs/index.js",
6+
"module": "build/esm/index.js",
7+
"types": "build/types/index.d.ts",
8+
"sideEffects": false,
9+
"private": true,
10+
"scripts": {
11+
"build": "run-p build:transpile build:types",
12+
"build:transpile": "rollup -c rollup.npm.config.js",
13+
"build:types": "tsc -p tsconfig.types.json",
14+
"build:dev": "yarn build",
15+
"build:watch": "run-p build:transpile:watch build:types:watch",
16+
"build:dev:watch": "run-p build:watch",
17+
"build:transpile:watch": "yarn build:rollup --watch",
18+
"build:types:watch": "yarn build:types --watch",
19+
"clean": "rimraf build",
20+
"fix": "run-s fix:eslint fix:prettier",
21+
"fix:eslint": "eslint . --format stylish --fix",
22+
"fix:prettier": "prettier --write \"{src,test}/**/*.ts\"",
23+
"lint": "run-s lint:prettier lint:eslint",
24+
"lint:eslint": "eslint . --format stylish",
25+
"lint:prettier": "prettier --check \"{src,test}/**/*.ts\""
26+
},
27+
"repository": {
28+
"type": "git",
29+
"url": "git+https://github.com/getsentry/sentry-javascript.git"
30+
},
31+
"author": "Sentry",
32+
"license": "MIT",
33+
"bugs": {
34+
"url": "https://github.com/getsentry/sentry-javascript/issues"
35+
},
36+
"devDependencies": {},
37+
"dependencies": {
38+
"@sentry/types": "7.43.0"
39+
},
40+
"engines": {
41+
"node": ">=12"
42+
},
43+
"volta": {
44+
"extends": "../../package.json"
45+
}
46+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js';
2+
3+
export default makeNPMConfigVariants(
4+
makeBaseNPMConfig({
5+
entrypoints: ['src/index.ts'],
6+
}),
7+
);
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type { Integration } from '@sentry/types';
2+
3+
/**
4+
* This is a shim for the Replay integration.
5+
* It is needed in order for the CDN bundles to continue working when users add/remove replay
6+
* from it, without changing their config. This is necessary for the loader mechanism.
7+
*/
8+
class ReplayShim implements Integration {
9+
/**
10+
* @inheritDoc
11+
*/
12+
public static id: string = 'Replay';
13+
14+
/**
15+
* @inheritDoc
16+
*/
17+
public name: string = ReplayShim.id;
18+
19+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
20+
public constructor(_options: any) {
21+
// eslint-disable-next-line no-console
22+
console.error('You are using new Replay() even though this bundle does not include the replay integration.');
23+
}
24+
25+
/** jsdoc */
26+
public setupOnce(): void {
27+
// noop
28+
}
29+
30+
/** jsdoc */
31+
public start(): void {
32+
// noop
33+
}
34+
35+
/** jsdoc */
36+
public stop(): void {
37+
// noop
38+
}
39+
40+
/** jsdoc */
41+
public flush(): void {
42+
// noop
43+
}
44+
}
45+
46+
export { ReplayShim as Replay };
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './Replay';
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"compilerOptions": {
4+
"module": "esnext",
5+
"lib": ["ES6"],
6+
"esModuleInterop": true,
7+
"target": "es6",
8+
"strictPropertyInitialization": false
9+
},
10+
"include": ["src/**/*.ts"]
11+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"include": ["src/index.ts"],
4+
"compilerOptions": {
5+
"declaration": true,
6+
"declarationMap": true,
7+
"emitDeclarationOnly": true,
8+
"outDir": "build/types"
9+
}
10+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
// Replay should not actually work, but still not error out
6+
window.Replay = new Sentry.Replay({
7+
flushMinDelay: 200,
8+
flushMaxDelay: 200,
9+
});
10+
11+
Sentry.init({
12+
dsn: 'https://[email protected]/1337',
13+
sampleRate: 1,
14+
replaysSessionSampleRate: 1.0,
15+
replaysOnErrorSampleRate: 0.0,
16+
integrations: [window.Replay],
17+
});
18+
19+
// Ensure none of these break
20+
window.Replay.start();
21+
window.Replay.stop();
22+
window.Replay.flush();
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
</head>
6+
<body>
7+
<button onclick="console.log('Test log')">Click me</button>
8+
</body>
9+
</html>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { expect } from '@playwright/test';
2+
3+
import { sentryTest } from '../../../utils/fixtures';
4+
5+
sentryTest(
6+
'exports a shim Replay integration for non-replay bundles',
7+
async ({ getLocalTestPath, page, forceFlushReplay }) => {
8+
const bundle = process.env.PW_BUNDLE;
9+
10+
if (!bundle || !bundle.startsWith('bundle_') || bundle.includes('replay')) {
11+
sentryTest.skip();
12+
}
13+
14+
const consoleMessages: string[] = [];
15+
page.on('console', msg => consoleMessages.push(msg.text()));
16+
17+
let requestCount = 0;
18+
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
19+
requestCount++;
20+
return route.fulfill({
21+
status: 200,
22+
contentType: 'application/json',
23+
body: JSON.stringify({ id: 'test-id' }),
24+
});
25+
});
26+
27+
const url = await getLocalTestPath({ testDir: __dirname });
28+
29+
await page.goto(url);
30+
await forceFlushReplay();
31+
32+
expect(requestCount).toBe(0);
33+
expect(consoleMessages).toEqual([
34+
'You are using new Replay() even though this bundle does not include the replay integration.',
35+
]);
36+
},
37+
);

packages/tracing/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
},
2424
"devDependencies": {
2525
"@sentry/browser": "7.43.0",
26+
"@sentry-internal/integration-shims": "7.43.0",
2627
"@types/express": "^4.17.14"
2728
},
2829
"scripts": {

packages/tracing/rollup.bundle.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const builds = [];
88
entrypoints: ['src/index.bundle.ts'],
99
jsVersion,
1010
licenseTitle: '@sentry/tracing & @sentry/browser',
11+
includeReplay: false,
1112
outputFileBase: () => `bundles/bundle.tracing${jsVersion === 'es5' ? '.es5' : ''}`,
1213
});
1314

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
export type {
2+
Breadcrumb,
3+
Request,
4+
SdkInfo,
5+
Event,
6+
Exception,
7+
// eslint-disable-next-line deprecation/deprecation
8+
Severity,
9+
SeverityLevel,
10+
StackFrame,
11+
Stacktrace,
12+
Thread,
13+
User,
14+
} from '@sentry/types';
15+
16+
export type { BrowserOptions, ReportDialogOptions } from '@sentry/browser';
17+
18+
export {
19+
addGlobalEventProcessor,
20+
addBreadcrumb,
21+
captureException,
22+
captureEvent,
23+
captureMessage,
24+
configureScope,
25+
getHubFromCarrier,
26+
getCurrentHub,
27+
Hub,
28+
Scope,
29+
setContext,
30+
setExtra,
31+
setExtras,
32+
setTag,
33+
setTags,
34+
setUser,
35+
startTransaction,
36+
makeFetchTransport,
37+
makeXHRTransport,
38+
withScope,
39+
} from '@sentry/browser';
40+
41+
export { BrowserClient } from '@sentry/browser';
42+
export {
43+
defaultIntegrations,
44+
defaultStackParser,
45+
forceLoad,
46+
init,
47+
lastEventId,
48+
onLoad,
49+
showReportDialog,
50+
flush,
51+
close,
52+
wrap,
53+
} from '@sentry/browser';
54+
export { SDK_VERSION } from '@sentry/browser';
55+
56+
import { Integrations as BrowserIntegrations } from '@sentry/browser';
57+
import type { Integration } from '@sentry/types';
58+
import { GLOBAL_OBJ } from '@sentry/utils';
59+
60+
import { BrowserTracing } from './browser';
61+
import { addExtensionMethods } from './extensions';
62+
63+
export { Span } from '@sentry/core';
64+
65+
let windowIntegrations = {};
66+
67+
// This block is needed to add compatibility with the integrations packages when used with a CDN
68+
if (GLOBAL_OBJ.Sentry && GLOBAL_OBJ.Sentry.Integrations) {
69+
windowIntegrations = GLOBAL_OBJ.Sentry.Integrations;
70+
}
71+
72+
// For whatever reason, it does not recognize BrowserTracing or some of the BrowserIntegrations as Integration
73+
const INTEGRATIONS: Record<
74+
string,
75+
Integration | typeof BrowserTracing | typeof BrowserIntegrations[keyof typeof BrowserIntegrations]
76+
> = {
77+
...windowIntegrations,
78+
...BrowserIntegrations,
79+
BrowserTracing,
80+
};
81+
82+
export { INTEGRATIONS as Integrations };
83+
// Though in this case exporting `BrowserTracing` separately (in addition to exporting it as part of
84+
// `Sentry.Integrations`) doesn't gain us any bundle size advantage (we're making the bundle here, not the user, and we
85+
// can't leave anything out of ours), it does bring the API for using the integration in line with that recommended for
86+
// users bundling Sentry themselves.
87+
export { BrowserTracing };
88+
89+
// We are patching the global object with our hub extension methods
90+
addExtensionMethods();
91+
92+
export { addExtensionMethods };

packages/tracing/src/index.bundle.replay.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ Sentry.Integrations.Replay = Replay;
99

1010
export { Replay };
1111

12-
export * from './index.bundle';
12+
export * from './index.bundle.base';

0 commit comments

Comments
 (0)