Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(svelte): Add
withSentryConfig
function to wrap User Svelte Con…
…figuration (#5936) Add a `withSentryConfig` wrapper function to our Svelte SDK which can be used to automatically add Sentry-specific svelte configuration options to our users' configuration in `svelte.config.js`. Going forward, this function will allow us to add more sentry-specific config items, such as additional preprocessors to the config without having to ask our users to adjust their config stuff every time we introduce changes. The change is fully backward-compatible with how we previously instructed users to add the `componentTrackingPreprocessor` explicitly to their set of preprocessors in their config. However, to make it clear that `withSentryConfig` is the way forward, this PR deprecates `componentTrackingPreprocessor` and in v8, we'll remove it from our public exports. Additionally, this patch - adds tests for `withSentryConfig`. - removes the svelte-internal types we previously copied from Svelte source code into our types file
- Loading branch information
Showing
6 changed files
with
207 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { PreprocessorGroup } from 'svelte/types/compiler/preprocess'; | ||
|
||
import { componentTrackingPreprocessor, defaultComponentTrackingOptions } from './preprocessors'; | ||
import { SentryPreprocessorGroup, SentrySvelteConfigOptions, SvelteConfig } from './types'; | ||
|
||
const DEFAULT_SENTRY_OPTIONS: SentrySvelteConfigOptions = { | ||
componentTracking: defaultComponentTrackingOptions, | ||
}; | ||
|
||
/** | ||
* Add Sentry options to the Svelte config to be exported from the user's `svelte.config.js` file. | ||
* | ||
* @param originalConfig The existing config to be exported prior to adding Sentry | ||
* @param sentryOptions The configuration of the Sentry-added options | ||
* | ||
* @return The wrapped and modified config to be exported | ||
*/ | ||
export function withSentryConfig( | ||
originalConfig: SvelteConfig, | ||
sentryOptions?: SentrySvelteConfigOptions, | ||
): SvelteConfig { | ||
const mergedOptions = { | ||
...DEFAULT_SENTRY_OPTIONS, | ||
...sentryOptions, | ||
}; | ||
|
||
const originalPreprocessors = getOriginalPreprocessorArray(originalConfig); | ||
|
||
// Map is insertion-order-preserving. It's important to add preprocessors | ||
// to this map in the right order we want to see them being executed. | ||
// see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map | ||
const sentryPreprocessors = new Map<string, SentryPreprocessorGroup>(); | ||
|
||
const shouldTrackComponents = mergedOptions.componentTracking && mergedOptions.componentTracking.trackComponents; | ||
if (shouldTrackComponents) { | ||
// TODO(v8): Remove eslint rule | ||
// eslint-disable-next-line deprecation/deprecation | ||
const firstPassPreproc: SentryPreprocessorGroup = componentTrackingPreprocessor(mergedOptions.componentTracking); | ||
sentryPreprocessors.set(firstPassPreproc.sentryId || '', firstPassPreproc); | ||
} | ||
|
||
// We prioritize user-added preprocessors, so we don't insert sentry processors if they | ||
// have already been added by users. | ||
originalPreprocessors.forEach((p: SentryPreprocessorGroup) => { | ||
if (p.sentryId) { | ||
sentryPreprocessors.delete(p.sentryId); | ||
} | ||
}); | ||
|
||
const mergedPreprocessors = [...sentryPreprocessors.values(), ...originalPreprocessors]; | ||
|
||
return { | ||
...originalConfig, | ||
preprocess: mergedPreprocessors, | ||
}; | ||
} | ||
|
||
/** | ||
* Standardizes the different ways the user-provided preprocessor option can be specified. | ||
* Users can specify an array of preprocessors, a single one or no preprocessor. | ||
* | ||
* @param originalConfig the user-provided svelte config oject | ||
* @return an array of preprocessors or an empty array if no preprocessors were specified | ||
*/ | ||
function getOriginalPreprocessorArray(originalConfig: SvelteConfig): PreprocessorGroup[] { | ||
if (originalConfig.preprocess) { | ||
if (Array.isArray(originalConfig.preprocess)) { | ||
return originalConfig.preprocess; | ||
} | ||
return [originalConfig.preprocess]; | ||
} | ||
return []; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { withSentryConfig } from '../src/config'; | ||
import { componentTrackingPreprocessor, FIRST_PASS_COMPONENT_TRACKING_PREPROC_ID } from '../src/preprocessors'; | ||
import { SentryPreprocessorGroup, SentrySvelteConfigOptions, SvelteConfig } from '../src/types'; | ||
|
||
describe('withSentryConfig', () => { | ||
it.each([ | ||
[ | ||
'no preprocessors specified', | ||
{ | ||
compilerOptions: { | ||
enableSourcemap: true, | ||
}, | ||
}, | ||
], | ||
[ | ||
'a single preprocessor specified', | ||
{ | ||
compilerOptions: { | ||
enableSourcemap: true, | ||
}, | ||
preprocess: {}, | ||
}, | ||
], | ||
[ | ||
'an array of preprocessors specified', | ||
{ | ||
compilerOptions: { | ||
enableSourcemap: true, | ||
}, | ||
preprocess: [{}, {}, {}], | ||
}, | ||
], | ||
])('adds our preprocessors by default to the provided svelte config with %s', (_, originalConfig: SvelteConfig) => { | ||
const wrappedConfig = withSentryConfig(originalConfig); | ||
const originalPreprocs = originalConfig.preprocess; | ||
const originalNumberOfPreprocs = originalPreprocs | ||
? Array.isArray(originalPreprocs) | ||
? originalPreprocs.length | ||
: 1 | ||
: 0; | ||
|
||
expect(Array.isArray(wrappedConfig.preprocess)).toBe(true); | ||
expect(wrappedConfig).toEqual({ ...originalConfig, preprocess: expect.any(Array) }); | ||
expect(wrappedConfig.preprocess).toHaveLength(originalNumberOfPreprocs + 1); | ||
expect((wrappedConfig.preprocess as SentryPreprocessorGroup[])[0].sentryId).toEqual( | ||
FIRST_PASS_COMPONENT_TRACKING_PREPROC_ID, | ||
); | ||
}); | ||
|
||
it("doesn't add Sentry preprocessors that were already added by the users", () => { | ||
// eslint-disable-next-line deprecation/deprecation | ||
const sentryPreproc = componentTrackingPreprocessor(); | ||
const originalConfig = { | ||
compilerOptions: { | ||
enableSourcemap: true, | ||
}, | ||
preprocess: sentryPreproc, | ||
}; | ||
|
||
const wrappedConfig = withSentryConfig(originalConfig); | ||
|
||
expect(wrappedConfig).toEqual({ ...originalConfig, preprocess: [sentryPreproc] }); | ||
}); | ||
|
||
it('handles multiple wraps correctly by only adding our preprocessors once', () => { | ||
const originalConfig = { | ||
compilerOptions: { | ||
enableSourcemap: true, | ||
}, | ||
}; | ||
|
||
const wrappedConfig = withSentryConfig(withSentryConfig(withSentryConfig(originalConfig))); | ||
|
||
expect(wrappedConfig).toEqual({ ...originalConfig, preprocess: expect.any(Array) }); | ||
expect(wrappedConfig.preprocess).toHaveLength(1); | ||
}); | ||
|
||
it("doesn't add component tracking preprocessors if the feature is deactivated", () => { | ||
const originalConfig = { | ||
compilerOptions: { | ||
enableSourcemap: true, | ||
}, | ||
preprocess: [{}], | ||
}; | ||
|
||
const sentryOptions: SentrySvelteConfigOptions = { componentTracking: { trackComponents: false } }; | ||
const wrappedConfig = withSentryConfig(originalConfig, sentryOptions); | ||
|
||
expect(wrappedConfig).toEqual(originalConfig); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters