Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
22 changes: 11 additions & 11 deletions .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ module.exports = [
// Browser SDK (ESM)
{
name: '@sentry/browser',
path: 'packages/browser/build/npm/esm/index.js',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init'),
gzip: true,
limit: '25 KB',
},
{
name: '@sentry/browser - with treeshaking flags',
path: 'packages/browser/build/npm/esm/index.js',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init'),
gzip: true,
limit: '24.1 KB',
Expand All @@ -35,28 +35,28 @@ module.exports = [
},
{
name: '@sentry/browser (incl. Tracing)',
path: 'packages/browser/build/npm/esm/index.js',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'browserTracingIntegration'),
gzip: true,
limit: '41.3 KB',
},
{
name: '@sentry/browser (incl. Tracing, Profiling)',
path: 'packages/browser/build/npm/esm/index.js',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'browserTracingIntegration', 'browserProfilingIntegration'),
gzip: true,
limit: '48 KB',
},
{
name: '@sentry/browser (incl. Tracing, Replay)',
path: 'packages/browser/build/npm/esm/index.js',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'browserTracingIntegration', 'replayIntegration'),
gzip: true,
limit: '80 KB',
},
{
name: '@sentry/browser (incl. Tracing, Replay) - with treeshaking flags',
path: 'packages/browser/build/npm/esm/index.js',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'browserTracingIntegration', 'replayIntegration'),
gzip: true,
limit: '75 KB',
Expand All @@ -79,35 +79,35 @@ module.exports = [
},
{
name: '@sentry/browser (incl. Tracing, Replay with Canvas)',
path: 'packages/browser/build/npm/esm/index.js',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'browserTracingIntegration', 'replayIntegration', 'replayCanvasIntegration'),
gzip: true,
limit: '85 KB',
},
{
name: '@sentry/browser (incl. Tracing, Replay, Feedback)',
path: 'packages/browser/build/npm/esm/index.js',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'browserTracingIntegration', 'replayIntegration', 'feedbackIntegration'),
gzip: true,
limit: '97 KB',
},
{
name: '@sentry/browser (incl. Feedback)',
path: 'packages/browser/build/npm/esm/index.js',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'feedbackIntegration'),
gzip: true,
limit: '42 KB',
},
{
name: '@sentry/browser (incl. sendFeedback)',
path: 'packages/browser/build/npm/esm/index.js',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'sendFeedback'),
gzip: true,
limit: '30 KB',
},
{
name: '@sentry/browser (incl. FeedbackAsync)',
path: 'packages/browser/build/npm/esm/index.js',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'feedbackAsyncIntegration'),
gzip: true,
limit: '35 KB',
Expand Down
12 changes: 6 additions & 6 deletions dev-packages/browser-integration-tests/utils/generatePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ const IMPORTED_INTEGRATION_CDN_BUNDLE_PATHS: Record<string, string> = {

const BUNDLE_PATHS: Record<string, Record<string, string>> = {
browser: {
cjs: 'build/npm/cjs/index.js',
esm: 'build/npm/esm/index.js',
cjs: 'build/npm/cjs/prod/index.js',
esm: 'build/npm/esm/prod/index.js',
bundle: 'build/bundles/bundle.js',
bundle_min: 'build/bundles/bundle.min.js',
bundle_replay: 'build/bundles/bundle.replay.js',
Expand All @@ -67,8 +67,8 @@ const BUNDLE_PATHS: Record<string, Record<string, string>> = {
loader_tracing_replay: 'build/bundles/bundle.tracing.replay.debug.min.js',
},
integrations: {
cjs: 'build/npm/cjs/index.js',
esm: 'build/npm/esm/index.js',
cjs: 'build/npm/cjs/prod/index.js',
esm: 'build/npm/esm/prod/index.js',
bundle: 'build/bundles/[INTEGRATION_NAME].js',
bundle_min: 'build/bundles/[INTEGRATION_NAME].min.js',
},
Expand All @@ -77,8 +77,8 @@ const BUNDLE_PATHS: Record<string, Record<string, string>> = {
bundle_min: 'build/bundles/[INTEGRATION_NAME].min.js',
},
wasm: {
cjs: 'build/npm/cjs/index.js',
esm: 'build/npm/esm/index.js',
cjs: 'build/npm/cjs/prod/index.js',
esm: 'build/npm/esm/prod/index.js',
bundle: 'build/bundles/wasm.js',
bundle_min: 'build/bundles/wasm.min.js',
},
Expand Down
7 changes: 3 additions & 4 deletions dev-packages/rollup-utils/bundleHelpers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
makeCleanupPlugin,
makeCommonJSPlugin,
makeIsDebugBuildPlugin,
makeJsonPlugin,
makeLicensePlugin,
makeNodeResolvePlugin,
makeRrwebBuildPlugin,
Expand All @@ -20,6 +19,7 @@ import {
makeTerserPlugin,
} from './plugins/index.mjs';
import { mergePlugins } from './utils.mjs';
import { makeProductionReplacePlugin } from './plugins/npmPlugins.mjs';

const BUNDLE_VARIANTS = ['.js', '.min.js', '.debug.min.js'];

Expand All @@ -35,14 +35,13 @@ export function makeBaseBundleConfig(options) {
excludeIframe: false,
excludeShadowDom: false,
});
const productionReplacePlugin = makeProductionReplacePlugin();

// The `commonjs` plugin is the `esModuleInterop` of the bundling world. When used with `transformMixedEsModules`, it
// will include all dependencies, imported or required, in the final bundle. (Without it, CJS modules aren't included
// at all, and without `transformMixedEsModules`, they're only included if they're imported, not if they're required.)
const commonJSPlugin = makeCommonJSPlugin({ transformMixedEsModules: true });

const jsonPlugin = makeJsonPlugin();

// used by `@sentry/browser`
const standAloneBundleConfig = {
output: {
Expand Down Expand Up @@ -119,7 +118,7 @@ export function makeBaseBundleConfig(options) {
strict: false,
esModule: false,
},
plugins: [sucrasePlugin, nodeResolvePlugin, cleanupPlugin],
plugins: [productionReplacePlugin, sucrasePlugin, nodeResolvePlugin, cleanupPlugin],
treeshake: 'smallest',
};

Expand Down
44 changes: 35 additions & 9 deletions dev-packages/rollup-utils/npmHelpers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
makeCleanupPlugin,
makeDebugBuildStatementReplacePlugin,
makeNodeResolvePlugin,
makeProductionReplacePlugin,
makeRrwebBuildPlugin,
makeSucrasePlugin,
} from './plugins/index.mjs';
Expand Down Expand Up @@ -114,22 +115,47 @@ export function makeBaseNPMConfig(options = {}) {
}

export function makeNPMConfigVariants(baseConfig, options = {}) {
const { emitEsm = true, emitCjs = true } = options;
const { emitEsm = true, emitCjs = true, splitDevProd = false } = options;

const variantSpecificConfigs = [];

if (emitCjs) {
variantSpecificConfigs.push({ output: { format: 'cjs', dir: path.join(baseConfig.output.dir, 'cjs') } });
if (splitDevProd) {
variantSpecificConfigs.push({ output: { format: 'cjs', dir: path.join(baseConfig.output.dir, 'cjs/dev') } });
variantSpecificConfigs.push({
output: { format: 'cjs', dir: path.join(baseConfig.output.dir, 'cjs/prod') },
plugins: [makeProductionReplacePlugin()],
});
} else {
variantSpecificConfigs.push({ output: { format: 'cjs', dir: path.join(baseConfig.output.dir, 'cjs') } });
}
}

if (emitEsm) {
variantSpecificConfigs.push({
output: {
format: 'esm',
dir: path.join(baseConfig.output.dir, 'esm'),
plugins: [makePackageNodeEsm()],
},
});
if (splitDevProd) {
variantSpecificConfigs.push({
output: {
format: 'esm',
dir: path.join(baseConfig.output.dir, 'esm/dev'),
plugins: [makePackageNodeEsm()],
},
});
variantSpecificConfigs.push({
output: {
format: 'esm',
dir: path.join(baseConfig.output.dir, 'esm/prod'),
plugins: [makeProductionReplacePlugin(), makePackageNodeEsm()],
},
});
} else {
variantSpecificConfigs.push({
output: {
format: 'esm',
dir: path.join(baseConfig.output.dir, 'esm'),
plugins: [makePackageNodeEsm()],
},
});
}
}

return variantSpecificConfigs.map(variant => deepMerge(baseConfig, variant));
Expand Down
18 changes: 18 additions & 0 deletions dev-packages/rollup-utils/plugins/npmPlugins.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,24 @@ export function makeDebugBuildStatementReplacePlugin() {
});
}

export function makeProductionReplacePlugin() {
const pattern = /\/\* rollup-include-development-only \*\/[\s\S]*?\/\* rollup-include-development-only-end \*\/\s*/g;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not very confident about using regex here. I'm guessing AST based operation is either unavailable or more expensive?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you okay me exploring rollup-plugin-define or Vite (which uses rolldown under the hood which should be fully compatible with rollup) that has the define feature built-in: https://vite.dev/config/#using-environment-variables-in-config

Once we do that, we can set NODE_ENV to development or production based on the build, and use regular if blocks rather than surround the code with magic comments. These if blocks will have a static condition at build time which will then be optimized (stripped out or unwrapped) via terser or whatever minified/optimizer we are using.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When building the SDK npm module, we preserve the individual files (ie. preserveModules: true) and we don't do any kind of minification or optimization.

This is why I went this route vs replace/define plugins.


function stripDevBlocks(code) {
if (!code) return null;
if (!code.includes('rollup-include-development-only')) return null;
const replaced = code.replace(pattern, '');
return { code: replaced, map: null };
}

return {
name: 'remove-dev-mode-blocks',
renderChunk(code) {
return stripDevBlocks(code);
},
};
}

/**
* Creates a plugin to replace build flags of rrweb with either a constant (if passed true/false) or with a safe statement that:
* a) evaluates to `true`
Expand Down
11 changes: 10 additions & 1 deletion dev-packages/rollup-utils/utils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,16 @@ export function mergePlugins(pluginsA, pluginsB) {
// here.
// Additionally, the excludeReplay plugin must run before TS/Sucrase so that we can eliminate the replay code
// before anything is type-checked (TS-only) and transpiled.
const order = ['excludeReplay', 'typescript', 'sucrase', '...', 'terser', 'license', 'output-base64-worker-script'];
const order = [
'remove-dev-mode-blocks',
'excludeReplay',
'typescript',
'sucrase',
'...',
'terser',
'license',
'output-base64-worker-script',
];
const sortKeyA = order.includes(a.name) ? a.name : '...';
const sortKeyB = order.includes(b.name) ? b.name : '...';

Expand Down
14 changes: 9 additions & 5 deletions packages/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,23 @@
"files": [
"/build/npm"
],
"main": "build/npm/cjs/index.js",
"module": "build/npm/esm/index.js",
"main": "build/npm/cjs/prod/index.js",
"module": "build/npm/esm/prod/index.js",
"types": "build/npm/types/index.d.ts",
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"types": "./build/npm/types/index.d.ts",
"default": "./build/npm/esm/index.js"
"development": "./build/npm/esm/dev/index.js",
"production": "./build/npm/esm/prod/index.js",
"default": "./build/npm/esm/prod/index.js"
},
"require": {
"types": "./build/npm/types/index.d.ts",
"default": "./build/npm/cjs/index.js"
"development": "./build/npm/cjs/dev/index.js",
"production": "./build/npm/cjs/prod/index.js",
"default": "./build/npm/cjs/prod/index.js"
}
}
},
Expand Down Expand Up @@ -67,7 +71,7 @@
"clean": "rimraf build coverage .rpt2_cache sentry-browser-*.tgz",
"fix": "eslint . --format stylish --fix",
"lint": "eslint . --format stylish",
"lint:es-compatibility": "es-check es2020 ./build/{bundles,npm/cjs}/*.js && es-check es2020 ./build/npm/esm/*.js --module",
"lint:es-compatibility": "es-check es2020 ./build/{bundles,npm/cjs/prod}/*.js && es-check es2020 ./build/npm/esm/prod/*.js --module",
"size:check": "cat build/bundles/bundle.min.js | gzip -9 | wc -c | awk '{$1=$1/1024; print \"ES2017: \",$1,\"kB\";}'",
"test": "vitest run",
"test:watch": "vitest --watch",
Expand Down
1 change: 1 addition & 0 deletions packages/browser/rollup.npm.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export default makeNPMConfigVariants(
},
},
}),
{ splitDevProd: true },
);
12 changes: 12 additions & 0 deletions packages/browser/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ type BrowserSpecificOptions = BrowserClientReplayOptions &
* @default false
*/
propagateTraceparent?: boolean;

/**
* If you use Spotlight by Sentry during development, use
* this option to forward captured Sentry events to Spotlight.
*
* Either set it to true, or provide a specific Spotlight Sidecar URL.
*
* More details: https://spotlightjs.com/
*
* IMPORTANT: Only set this option to `true` while developing, not in production!
*/
spotlight?: boolean | string;
};
/**
* Configuration options for the Sentry Browser SDK.
Expand Down
17 changes: 15 additions & 2 deletions packages/browser/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { browserSessionIntegration } from './integrations/browsersession';
import { globalHandlersIntegration } from './integrations/globalhandlers';
import { httpContextIntegration } from './integrations/httpcontext';
import { linkedErrorsIntegration } from './integrations/linkederrors';
import { spotlightBrowserIntegration } from './integrations/spotlight';
import { defaultStackParser } from './stack-parsers';
import { makeFetchTransport } from './transports/fetch';
import { checkAndWarnIfIsEmbeddedBrowserExtension } from './utils/detectBrowserExtension';
Expand Down Expand Up @@ -90,14 +91,26 @@ export function init(options: BrowserOptions = {}): Client | undefined {
const shouldDisableBecauseIsBrowserExtenstion =
!options.skipBrowserExtensionCheck && checkAndWarnIfIsEmbeddedBrowserExtension();

let defaultIntegrations =
options.defaultIntegrations == null ? getDefaultIntegrations(options) : options.defaultIntegrations;

/* rollup-include-development-only */
if (options.spotlight) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How hard would it be to fill this value from the env variables? I'm okay with trying all variants like PUBLIC_SENTRY_SPOTLIGHT or VITE_SENTRY_SPOTLIGHT etc.

Happy to do this in a follow up too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah probably best in a follow up PR. If we can keep it between the magic comments it'll all get striped out!

if (!defaultIntegrations) {
defaultIntegrations = [];
}
const args = typeof options.spotlight === 'string' ? { sidecarUrl: options.spotlight } : undefined;
defaultIntegrations.push(spotlightBrowserIntegration(args));
}
/* rollup-include-development-only-end */

const clientOptions: BrowserClientOptions = {
...options,
enabled: shouldDisableBecauseIsBrowserExtenstion ? false : options.enabled,
stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),
integrations: getIntegrationsToSetup({
integrations: options.integrations,
defaultIntegrations:
options.defaultIntegrations == null ? getDefaultIntegrations(options) : options.defaultIntegrations,
defaultIntegrations,
}),
transport: options.transport || makeFetchTransport,
};
Expand Down
Loading