diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index 278cc3c7e17c..4f72afa34a61 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -22,6 +22,7 @@ import { User, } from '@sentry/types'; import { + arrayify, dateTimestampInSeconds, getGlobalSingleton, isPlainObject, @@ -553,11 +554,7 @@ export class Scope implements ScopeInterface { */ private _applyFingerprint(event: Event): void { // Make sure it's an array first and we actually have something in place - event.fingerprint = event.fingerprint - ? Array.isArray(event.fingerprint) - ? event.fingerprint - : [event.fingerprint] - : []; + event.fingerprint = event.fingerprint ? arrayify(event.fingerprint) : []; // If we have something on the scope, then merge it with event if (this._fingerprint) { diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index 453053616314..84650b80e3c7 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -1,6 +1,6 @@ /* eslint-disable max-lines */ import { getSentryRelease } from '@sentry/node'; -import { dropUndefinedKeys, escapeStringForRegex, logger } from '@sentry/utils'; +import { arrayify, dropUndefinedKeys, escapeStringForRegex, logger } from '@sentry/utils'; import { default as SentryWebpackPlugin } from '@sentry/webpack-plugin'; import * as chalk from 'chalk'; import * as fs from 'fs'; @@ -186,7 +186,7 @@ function isMatchingRule(rule: WebpackModuleRule, projectDir: string): boolean { } // `rule.use` can be an object or an array of objects. For simplicity, force it to be an array. - const useEntries = Array.isArray(rule.use) ? rule.use : [rule.use]; + const useEntries = arrayify(rule.use); // Depending on the version of nextjs we're talking about, the loader which does the transpiling is either // diff --git a/packages/tracing/src/integrations/node/apollo.ts b/packages/tracing/src/integrations/node/apollo.ts index 1450f1431e72..90ebbf889777 100644 --- a/packages/tracing/src/integrations/node/apollo.ts +++ b/packages/tracing/src/integrations/node/apollo.ts @@ -1,6 +1,6 @@ import { Hub } from '@sentry/hub'; import { EventProcessor, Integration } from '@sentry/types'; -import { fill, isThenable, loadModule, logger } from '@sentry/utils'; +import { arrayify, fill, isThenable, loadModule, logger } from '@sentry/utils'; type ApolloResolverGroup = { [key: string]: () => unknown; @@ -44,7 +44,7 @@ export class Apollo implements Integration { */ fill(pkg.ApolloServerBase.prototype, 'constructSchema', function (orig: () => unknown) { return function (this: { config: { resolvers: ApolloModelResolvers[] } }) { - const resolvers = Array.isArray(this.config.resolvers) ? this.config.resolvers : [this.config.resolvers]; + const resolvers = arrayify(this.config.resolvers); this.config.resolvers = resolvers.map(model => { Object.keys(model).forEach(resolverGroupName => { diff --git a/packages/utils/src/misc.ts b/packages/utils/src/misc.ts index 86ab32278ffe..964bc12c13bc 100644 --- a/packages/utils/src/misc.ts +++ b/packages/utils/src/misc.ts @@ -200,3 +200,13 @@ export function checkOrSetAlreadyCaught(exception: unknown): boolean { return false; } + +/** + * Checks whether the given input is already an array, and if it isn't, wraps it in one. + * + * @param maybeArray Input to turn into an array, if necessary + * @returns The input, if already an array, or an array with the input as the only element, if not + */ +export function arrayify(maybeArray: T | T[]): T[] { + return Array.isArray(maybeArray) ? maybeArray : [maybeArray]; +} diff --git a/packages/utils/test/misc.test.ts b/packages/utils/test/misc.test.ts index c7c98991efbf..ded5a7a4d037 100644 --- a/packages/utils/test/misc.test.ts +++ b/packages/utils/test/misc.test.ts @@ -3,6 +3,7 @@ import { Event, Mechanism, StackFrame } from '@sentry/types'; import { addContextToFrame, addExceptionMechanism, + arrayify, checkOrSetAlreadyCaught, getEventDescription, uuid4, @@ -309,3 +310,19 @@ describe('uuid4 generation', () => { } }); }); + +describe('arrayify()', () => { + it('returns arrays untouched', () => { + expect(arrayify([])).toEqual([]); + expect(arrayify(['dogs', 'are', 'great'])).toEqual(['dogs', 'are', 'great']); + }); + + it('wraps non-arrays with an array', () => { + expect(arrayify(1231)).toEqual([1231]); + expect(arrayify('dogs are great')).toEqual(['dogs are great']); + expect(arrayify(true)).toEqual([true]); + expect(arrayify({})).toEqual([{}]); + expect(arrayify(null)).toEqual([null]); + expect(arrayify(undefined)).toEqual([undefined]); + }); +}); diff --git a/packages/vue/src/sdk.ts b/packages/vue/src/sdk.ts index 1de282d8de54..35490f925a8d 100644 --- a/packages/vue/src/sdk.ts +++ b/packages/vue/src/sdk.ts @@ -1,5 +1,5 @@ import { init as browserInit, SDK_VERSION } from '@sentry/browser'; -import { getGlobalObject, logger } from '@sentry/utils'; +import { arrayify, getGlobalObject, logger } from '@sentry/utils'; import { DEFAULT_HOOKS } from './constants'; import { attachErrorHandler } from './errorhandler'; @@ -51,7 +51,7 @@ export function init( } if (options.app) { - const apps = Array.isArray(options.app) ? options.app : [options.app]; + const apps = arrayify(options.app); apps.forEach(app => vueInit(app, options)); } else if (options.Vue) { vueInit(options.Vue, options);