From 952360e7abf57c82178e79bfd0e8a5a36f6ebc0a Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 12 Oct 2022 20:07:23 +0200 Subject: [PATCH] feat(svelte): Add `withSentryConfig` function to wrap User Svelte Configuration (#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 --- packages/svelte/src/config.ts | 73 +++++++++++++++++ packages/svelte/src/index.ts | 5 ++ packages/svelte/src/preprocessors.ts | 20 ++++- packages/svelte/src/types.ts | 55 +++++-------- packages/svelte/test/config.test.ts | 91 ++++++++++++++++++++++ packages/svelte/test/preprocessors.test.ts | 1 + 6 files changed, 207 insertions(+), 38 deletions(-) create mode 100644 packages/svelte/src/config.ts create mode 100644 packages/svelte/test/config.test.ts diff --git a/packages/svelte/src/config.ts b/packages/svelte/src/config.ts new file mode 100644 index 000000000000..03c6c0dc1f01 --- /dev/null +++ b/packages/svelte/src/config.ts @@ -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(); + + 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 []; +} diff --git a/packages/svelte/src/index.ts b/packages/svelte/src/index.ts index 40f648a46286..ac5aa49a5298 100644 --- a/packages/svelte/src/index.ts +++ b/packages/svelte/src/index.ts @@ -7,5 +7,10 @@ export * from '@sentry/browser'; export { init } from './sdk'; +// TODO(v8): Remove this export +// eslint-disable-next-line deprecation/deprecation export { componentTrackingPreprocessor } from './preprocessors'; + export { trackComponent } from './performance'; + +export { withSentryConfig } from './config'; diff --git a/packages/svelte/src/preprocessors.ts b/packages/svelte/src/preprocessors.ts index 9102636c045f..f97948eef868 100644 --- a/packages/svelte/src/preprocessors.ts +++ b/packages/svelte/src/preprocessors.ts @@ -1,6 +1,7 @@ import MagicString from 'magic-string'; +import { PreprocessorGroup } from 'svelte/types/compiler/preprocess'; -import { ComponentTrackingInitOptions, PreprocessorGroup, TrackComponentOptions } from './types'; +import { ComponentTrackingInitOptions, SentryPreprocessorGroup, TrackComponentOptions } from './types'; export const defaultComponentTrackingOptions: Required = { trackComponents: true, @@ -8,18 +9,22 @@ export const defaultComponentTrackingOptions: Required(); - return { - // This script hook is called whenever a Svelte component's