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