|
| 1 | +# Adding a New CDN Bundle |
| 2 | + |
| 3 | +This guide explains how to create a new CDN bundle for the browser package that includes a specific combination of features. |
| 4 | + |
| 5 | +## Feature Combinations |
| 6 | + |
| 7 | +Feature combinations use dot notation: |
| 8 | + |
| 9 | +- `logs.metrics` - Bundle with logs and metrics |
| 10 | +- `replay.logs.metrics` - Bundle with replay, logs, and metrics |
| 11 | +- `tracing.replay.logs` - Bundle with tracing, replay, and logs |
| 12 | +- `tracing.replay.feedback.logs.metrics` - Full featured bundle |
| 13 | + |
| 14 | +**Feature order in bundle names:** `tracing` → `replay` → `feedback` → `logs` → `metrics` |
| 15 | + |
| 16 | +## Naming Conventions |
| 17 | + |
| 18 | +Given a feature combination, derive these variants: |
| 19 | + |
| 20 | +| Placeholder | Example (`replay.logs.metrics`) | |
| 21 | +| ------------------------------- | --------------------------------- | |
| 22 | +| `{FEATURE_COMBO}` | `replay.logs.metrics` | |
| 23 | +| `{feature_combo}` | `replay_logs_metrics` | |
| 24 | +| `{featureCombo}` | `replayLogsMetrics` | |
| 25 | +| `{Human Readable Features}` | `Replay, Logs, Metrics` | |
| 26 | +| `{Human Readable Feature List}` | `Replay, Logs, and Metrics` | |
| 27 | + |
| 28 | +## Files to Create |
| 29 | + |
| 30 | +### 1. Entry Point: `packages/browser/src/index.bundle.{FEATURE_COMBO}.ts` |
| 31 | + |
| 32 | +**Base structure:** |
| 33 | + |
| 34 | +```typescript |
| 35 | +// If bundle includes TRACING, add this at the top: |
| 36 | +import { registerSpanErrorInstrumentation } from '@sentry/core'; |
| 37 | +registerSpanErrorInstrumentation(); |
| 38 | + |
| 39 | +// Always export base bundle |
| 40 | +export * from './index.bundle.base'; |
| 41 | +``` |
| 42 | + |
| 43 | +**For LOGS (without tracing):** |
| 44 | + |
| 45 | +```typescript |
| 46 | +export { logger, consoleLoggingIntegration } from '@sentry/core'; |
| 47 | +``` |
| 48 | + |
| 49 | +**For TRACING:** |
| 50 | + |
| 51 | +```typescript |
| 52 | +export { |
| 53 | + getActiveSpan, |
| 54 | + getRootSpan, |
| 55 | + getSpanDescendants, |
| 56 | + setMeasurement, |
| 57 | + startInactiveSpan, |
| 58 | + startNewTrace, |
| 59 | + startSpan, |
| 60 | + startSpanManual, |
| 61 | + withActiveSpan, |
| 62 | +} from '@sentry/core'; |
| 63 | + |
| 64 | +export { |
| 65 | + browserTracingIntegration, |
| 66 | + startBrowserTracingNavigationSpan, |
| 67 | + startBrowserTracingPageLoadSpan, |
| 68 | +} from './tracing/browserTracingIntegration'; |
| 69 | +export { reportPageLoaded } from './tracing/reportPageLoaded'; |
| 70 | +export { setActiveSpanInBrowser } from './tracing/setActiveSpan'; |
| 71 | +``` |
| 72 | + |
| 73 | +**For REPLAY:** |
| 74 | + |
| 75 | +```typescript |
| 76 | +export { replayIntegration, getReplay } from '@sentry-internal/replay'; |
| 77 | +``` |
| 78 | + |
| 79 | +**For FEEDBACK:** |
| 80 | + |
| 81 | +```typescript |
| 82 | +import { feedbackAsyncIntegration } from './feedbackAsync'; |
| 83 | +export { getFeedback, sendFeedback } from '@sentry-internal/feedback'; |
| 84 | +export { feedbackAsyncIntegration as feedbackAsyncIntegration, feedbackAsyncIntegration as feedbackIntegration }; |
| 85 | +``` |
| 86 | + |
| 87 | +**For features NOT included, export shims:** |
| 88 | + |
| 89 | +```typescript |
| 90 | +import { |
| 91 | + browserTracingIntegrationShim, // if NO tracing |
| 92 | + feedbackIntegrationShim, // if NO feedback |
| 93 | + replayIntegrationShim, // if NO replay |
| 94 | + consoleLoggingIntegrationShim, // if NO logs |
| 95 | + loggerShim, // if NO logs |
| 96 | +} from '@sentry-internal/integration-shims'; |
| 97 | + |
| 98 | +// Then export them with proper names: |
| 99 | +export { browserTracingIntegrationShim as browserTracingIntegration }; |
| 100 | +export { feedbackIntegrationShim as feedbackAsyncIntegration, feedbackIntegrationShim as feedbackIntegration }; |
| 101 | +export { replayIntegrationShim as replayIntegration }; |
| 102 | +export { consoleLoggingIntegrationShim as consoleLoggingIntegration, loggerShim as logger }; |
| 103 | +``` |
| 104 | + |
| 105 | +### 2. Test File: `packages/browser/test/index.bundle.{FEATURE_COMBO}.test.ts` |
| 106 | + |
| 107 | +```typescript |
| 108 | +import { logger as coreLogger, metrics as coreMetrics } from '@sentry/core'; |
| 109 | +import { describe, expect, it } from 'vitest'; |
| 110 | +// Import real integrations for features included in the bundle |
| 111 | +import { browserTracingIntegration, feedbackAsyncIntegration, replayIntegration } from '../src'; |
| 112 | +import * as Bundle from '../src/index.bundle.{FEATURE_COMBO}'; |
| 113 | + |
| 114 | +describe('index.bundle.{FEATURE_COMBO}', () => { |
| 115 | + it('has correct exports', () => { |
| 116 | + // Test real exports match core implementations |
| 117 | + expect(Bundle.browserTracingIntegration).toBe(browserTracingIntegration); // if tracing included |
| 118 | + expect(Bundle.feedbackAsyncIntegration).toBe(feedbackAsyncIntegration); // if feedback included |
| 119 | + expect(Bundle.replayIntegration).toBe(replayIntegration); // if replay included |
| 120 | + expect(Bundle.logger).toBe(coreLogger); // if logs included |
| 121 | + expect(Bundle.metrics).toBe(coreMetrics); // always (in base bundle) |
| 122 | + }); |
| 123 | +}); |
| 124 | +``` |
| 125 | + |
| 126 | +## Files to Modify |
| 127 | + |
| 128 | +### 3. `packages/browser/rollup.bundle.config.mjs` |
| 129 | + |
| 130 | +Add bundle config before `builds.push(...)`: |
| 131 | + |
| 132 | +```javascript |
| 133 | +const {featureCombo}BaseBundleConfig = makeBaseBundleConfig({ |
| 134 | + bundleType: 'standalone', |
| 135 | + entrypoints: ['src/index.bundle.{FEATURE_COMBO}.ts'], |
| 136 | + licenseTitle: '@sentry/browser ({Human Readable Feature List})', |
| 137 | + outputFileBase: () => 'bundles/bundle.{FEATURE_COMBO}', |
| 138 | +}); |
| 139 | +``` |
| 140 | + |
| 141 | +Add to `builds.push(...)`: |
| 142 | + |
| 143 | +```javascript |
| 144 | +...makeBundleConfigVariants({featureCombo}BaseBundleConfig), |
| 145 | +``` |
| 146 | + |
| 147 | +### 4. `.size-limit.js` |
| 148 | + |
| 149 | +Add two entries in the "Browser CDN bundles" section: |
| 150 | + |
| 151 | +```javascript |
| 152 | +// Gzipped (add after similar bundles) |
| 153 | +{ |
| 154 | + name: 'CDN Bundle (incl. {Human Readable Features})', |
| 155 | + path: createCDNPath('bundle.{FEATURE_COMBO}.min.js'), |
| 156 | + gzip: true, |
| 157 | + limit: '{SIZE} KB', // Estimate based on features |
| 158 | +}, |
| 159 | + |
| 160 | +// Uncompressed (add in the non-gzipped section) |
| 161 | +{ |
| 162 | + name: 'CDN Bundle (incl. {Human Readable Features}) - uncompressed', |
| 163 | + path: createCDNPath('bundle.{FEATURE_COMBO}.min.js'), |
| 164 | + gzip: false, |
| 165 | + brotli: false, |
| 166 | + limit: '{SIZE} KB', // ~3x the gzipped size |
| 167 | +}, |
| 168 | +``` |
| 169 | + |
| 170 | +### 5. `dev-packages/browser-integration-tests/package.json` |
| 171 | + |
| 172 | +Add test scripts in the `scripts` section: |
| 173 | + |
| 174 | +```json |
| 175 | +"test:bundle:{feature_combo}": "PW_BUNDLE=bundle_{feature_combo} yarn test", |
| 176 | +"test:bundle:{feature_combo}:min": "PW_BUNDLE=bundle_{feature_combo}_min yarn test", |
| 177 | +"test:bundle:{feature_combo}:debug_min": "PW_BUNDLE=bundle_{feature_combo}_debug_min yarn test", |
| 178 | +``` |
| 179 | + |
| 180 | +### 6. `dev-packages/browser-integration-tests/utils/generatePlugin.ts` |
| 181 | + |
| 182 | +Add entries to `BUNDLE_PATHS.browser`: |
| 183 | + |
| 184 | +```javascript |
| 185 | +bundle_{feature_combo}: 'build/bundles/bundle.{FEATURE_COMBO}.js', |
| 186 | +bundle_{feature_combo}_min: 'build/bundles/bundle.{FEATURE_COMBO}.min.js', |
| 187 | +bundle_{feature_combo}_debug_min: 'build/bundles/bundle.{FEATURE_COMBO}.debug.min.js', |
| 188 | +``` |
| 189 | + |
| 190 | +### 7. `.github/workflows/build.yml` |
| 191 | + |
| 192 | +Add to the bundle matrix (in the `job_browser_playwright_tests` job): |
| 193 | + |
| 194 | +```yaml |
| 195 | +- bundle_{feature_combo} |
| 196 | +``` |
| 197 | +
|
| 198 | +## Size Estimation Guide |
| 199 | +
|
| 200 | +Use these approximate sizes when setting limits in `.size-limit.js`: |
| 201 | + |
| 202 | +| Feature | Gzipped Size | |
| 203 | +| ----------- | ------------ | |
| 204 | +| Base bundle | ~28 KB | |
| 205 | +| + Tracing | +15 KB | |
| 206 | +| + Replay | +37 KB | |
| 207 | +| + Feedback | +12 KB | |
| 208 | +| + Logs | +1 KB | |
| 209 | + |
| 210 | +Uncompressed size is approximately 3x the gzipped size. |
| 211 | + |
| 212 | +## Verification Steps |
| 213 | + |
| 214 | +After making changes: |
| 215 | + |
| 216 | +1. Run `yarn lint` to check for linting issues |
| 217 | +2. Run `cd packages/browser && yarn build:dev` to verify TypeScript compilation |
| 218 | +3. Run `cd packages/browser && yarn test` to run the unit tests |
| 219 | + |
| 220 | +## Reference: Existing Bundle Examples |
| 221 | + |
| 222 | +Look at these existing bundles for reference: |
| 223 | + |
| 224 | +- `packages/browser/src/index.bundle.tracing.ts` - Tracing only |
| 225 | +- `packages/browser/src/index.bundle.replay.ts` - Replay only |
| 226 | +- `packages/browser/src/index.bundle.tracing.replay.ts` - Tracing + Replay |
| 227 | +- `packages/browser/src/index.bundle.logs.metrics.ts` - Logs + Metrics |
| 228 | + |
| 229 | +## Error Handling |
| 230 | + |
| 231 | +- **Invalid feature combination**: Validate feature names are valid (tracing, replay, feedback, logs, metrics) |
| 232 | +- **Build failures**: Fix TypeScript errors before proceeding |
| 233 | +- **Lint errors**: Run `yarn fix` to auto-fix common issues |
| 234 | +- **Test failures**: Update test expectations to match the bundle's actual exports |
0 commit comments