Skip to content

Commit

Permalink
Remove getGlobalObject usage from utils
Browse files Browse the repository at this point in the history
  • Loading branch information
timfish committed Sep 27, 2022
1 parent 12c19d0 commit 99a0687
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 80 deletions.
15 changes: 9 additions & 6 deletions packages/utils/src/browser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { getGlobalObject } from './global';
import { GLOBAL_OBJ } from './global';
import { isString } from './is';

/**
* TODO: Move me to @sentry/browser when @sentry/utils no longer contains any browser code
*/
export const WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window;

/**
* Given a child DOM element, returns a query-selector statement describing that
* and its ancestors
Expand Down Expand Up @@ -115,9 +120,8 @@ function _htmlElementAsString(el: unknown, keyAttrs?: string[]): string {
* A safe form of location.href
*/
export function getLocationHref(): string {
const global = getGlobalObject<Window>();
try {
return global.document.location.href;
return WINDOW.document.location.href;
} catch (oO) {
return '';
}
Expand All @@ -141,9 +145,8 @@ export function getLocationHref(): string {
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function getDomElement<E = any>(selector: string): E | null {
const global = getGlobalObject<Window>();
if (global.document && global.document.querySelector) {
return global.document.querySelector(selector) as unknown as E;
if (WINDOW.document && WINDOW.document.querySelector) {
return WINDOW.document.querySelector(selector) as unknown as E;
}
return null;
}
28 changes: 18 additions & 10 deletions packages/utils/src/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

import { Integration } from '@sentry/types';

/** Internal */
interface SentryGlobal {
/** Internal global with common properties and Sentry extensions */
export interface InternalGlobal {
navigator?: { userAgent?: string };
console: Console;
Sentry?: {
Integrations?: Integration[];
};
Expand All @@ -21,10 +23,15 @@ interface SentryGlobal {
globalEventProcessors: any;
hub: any;
logger: any;
extensions?: {
/** Extension methods for the hub, which are bound to the current Hub instance */
// eslint-disable-next-line @typescript-eslint/ban-types
[key: string]: Function;
};
};
}

// The code below for 'isGlobalObj' and 'GLOBAL' was copied from core-js before modification
// The code below for 'isGlobalObj' and 'GLOBAL_OBJ' was copied from core-js before modification
// https://github.com/zloirock/core-js/blob/1b944df55282cdc99c90db5f49eb0b6eda2cc0a3/packages/core-js/internals/global.js
// core-js has the following licence:
//
Expand Down Expand Up @@ -53,7 +60,8 @@ function isGlobalObj(obj: { Math?: Math }): any | undefined {
return obj && obj.Math == Math ? obj : undefined;
}

const GLOBAL =
/** Get's the global object for the current JavaScript runtime */
export const GLOBAL_OBJ: InternalGlobal =
(typeof globalThis == 'object' && isGlobalObj(globalThis)) ||
// eslint-disable-next-line no-restricted-globals
(typeof window == 'object' && isGlobalObj(window)) ||
Expand All @@ -69,8 +77,8 @@ const GLOBAL =
*
* @returns Global scope object
*/
export function getGlobalObject<T>(): T & SentryGlobal {
return GLOBAL as T & SentryGlobal;
export function getGlobalObject<T>(): T & InternalGlobal {
return GLOBAL_OBJ as T & InternalGlobal;
}

/**
Expand All @@ -81,12 +89,12 @@ export function getGlobalObject<T>(): T & SentryGlobal {
*
* @param name name of the global singleton on __SENTRY__
* @param creator creator Factory function to create the singleton if it doesn't already exist on `__SENTRY__`
* @param obj (Optional) The global object on which to look for `__SENTRY__`, if not `getGlobalObject`'s return value
* @param obj (Optional) The global object on which to look for `__SENTRY__`, if not `GLOBAL_OBJ`'s return value
* @returns the singleton
*/
export function getGlobalSingleton<T>(name: keyof SentryGlobal['__SENTRY__'], creator: () => T, obj?: unknown): T {
const global = (obj || GLOBAL) as SentryGlobal;
const __SENTRY__ = (global.__SENTRY__ = global.__SENTRY__ || {});
export function getGlobalSingleton<T>(name: keyof InternalGlobal['__SENTRY__'], creator: () => T, obj?: unknown): T {
const gbl = (obj || GLOBAL_OBJ) as InternalGlobal;
const __SENTRY__ = (gbl.__SENTRY__ = gbl.__SENTRY__ || {});
const singleton = __SENTRY__[name] || (__SENTRY__[name] = creator());
return singleton;
}
50 changes: 24 additions & 26 deletions packages/utils/src/instrument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
/* eslint-disable @typescript-eslint/ban-types */
import { WrappedFunction } from '@sentry/types';

import { getGlobalObject } from './global';
import { WINDOW } from './browser';
import { isInstanceOf, isString } from './is';
import { CONSOLE_LEVELS, logger } from './logger';
import { fill } from './object';
import { getFunctionName } from './stacktrace';
import { supportsHistory, supportsNativeFetch } from './supports';

const global = getGlobalObject<Window>();

export type InstrumentHandlerType =
| 'console'
| 'dom'
Expand Down Expand Up @@ -105,22 +103,22 @@ function triggerHandlers(type: InstrumentHandlerType, data: any): void {

/** JSDoc */
function instrumentConsole(): void {
if (!('console' in global)) {
if (!('console' in WINDOW)) {
return;
}

CONSOLE_LEVELS.forEach(function (level: string): void {
if (!(level in global.console)) {
if (!(level in WINDOW.console)) {
return;
}

fill(global.console, level, function (originalConsoleMethod: () => any): Function {
fill(WINDOW.console, level, function (originalConsoleMethod: () => any): Function {
return function (...args: any[]): void {
triggerHandlers('console', { args, level });

// this fails for some browsers. :(
if (originalConsoleMethod) {
originalConsoleMethod.apply(global.console, args);
originalConsoleMethod.apply(WINDOW.console, args);
}
};
});
Expand All @@ -133,7 +131,7 @@ function instrumentFetch(): void {
return;
}

fill(global, 'fetch', function (originalFetch: () => void): () => void {
fill(WINDOW, 'fetch', function (originalFetch: () => void): () => void {
return function (...args: any[]): void {
const handlerData = {
args,
Expand All @@ -149,7 +147,7 @@ function instrumentFetch(): void {
});

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return originalFetch.apply(global, args).then(
return originalFetch.apply(WINDOW, args).then(
(response: Response) => {
triggerHandlers('fetch', {
...handlerData,
Expand Down Expand Up @@ -190,7 +188,7 @@ interface SentryWrappedXMLHttpRequest extends XMLHttpRequest {
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/** Extract `method` from fetch call arguments */
function getFetchMethod(fetchArgs: any[] = []): string {
if ('Request' in global && isInstanceOf(fetchArgs[0], Request) && fetchArgs[0].method) {
if ('Request' in WINDOW && isInstanceOf(fetchArgs[0], Request) && fetchArgs[0].method) {
return String(fetchArgs[0].method).toUpperCase();
}
if (fetchArgs[1] && fetchArgs[1].method) {
Expand All @@ -204,7 +202,7 @@ function getFetchUrl(fetchArgs: any[] = []): string {
if (typeof fetchArgs[0] === 'string') {
return fetchArgs[0];
}
if ('Request' in global && isInstanceOf(fetchArgs[0], Request)) {
if ('Request' in WINDOW && isInstanceOf(fetchArgs[0], Request)) {
return fetchArgs[0].url;
}
return String(fetchArgs[0]);
Expand All @@ -213,7 +211,7 @@ function getFetchUrl(fetchArgs: any[] = []): string {

/** JSDoc */
function instrumentXHR(): void {
if (!('XMLHttpRequest' in global)) {
if (!('XMLHttpRequest' in WINDOW)) {
return;
}

Expand Down Expand Up @@ -295,9 +293,9 @@ function instrumentHistory(): void {
return;
}

const oldOnPopState = global.onpopstate;
global.onpopstate = function (this: WindowEventHandlers, ...args: any[]): any {
const to = global.location.href;
const oldOnPopState = WINDOW.onpopstate;
WINDOW.onpopstate = function (this: WindowEventHandlers, ...args: any[]): any {
const to = WINDOW.location.href;
// keep track of the current URL state, as we always receive only the updated state
const from = lastHref;
lastHref = to;
Expand Down Expand Up @@ -336,8 +334,8 @@ function instrumentHistory(): void {
};
}

fill(global.history, 'pushState', historyReplacementFunction);
fill(global.history, 'replaceState', historyReplacementFunction);
fill(WINDOW.history, 'pushState', historyReplacementFunction);
fill(WINDOW.history, 'replaceState', historyReplacementFunction);
}

const debounceDuration = 1000;
Expand Down Expand Up @@ -452,7 +450,7 @@ function makeDOMEventHandler(handler: Function, globalListener: boolean = false)

// Start a new debounce timer that will prevent us from capturing multiple events that should be grouped together.
clearTimeout(debounceTimerID);
debounceTimerID = global.setTimeout(() => {
debounceTimerID = WINDOW.setTimeout(() => {
debounceTimerID = undefined;
}, debounceDuration);
};
Expand Down Expand Up @@ -481,7 +479,7 @@ type InstrumentedElement = Element & {

/** JSDoc */
function instrumentDOM(): void {
if (!('document' in global)) {
if (!('document' in WINDOW)) {
return;
}

Expand All @@ -490,8 +488,8 @@ function instrumentDOM(): void {
// we instrument `addEventListener` so that we don't end up attaching this handler twice.
const triggerDOMHandler = triggerHandlers.bind(null, 'dom');
const globalDOMEventHandler = makeDOMEventHandler(triggerDOMHandler, true);
global.document.addEventListener('click', globalDOMEventHandler, false);
global.document.addEventListener('keypress', globalDOMEventHandler, false);
WINDOW.document.addEventListener('click', globalDOMEventHandler, false);
WINDOW.document.addEventListener('keypress', globalDOMEventHandler, false);

// After hooking into click and keypress events bubbled up to `document`, we also hook into user-handled
// clicks & keypresses, by adding an event listener of our own to any element to which they add a listener. That
Expand All @@ -500,7 +498,7 @@ function instrumentDOM(): void {
// guaranteed to fire at least once.)
['EventTarget', 'Node'].forEach((target: string) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const proto = (global as any)[target] && (global as any)[target].prototype;
const proto = (WINDOW as any)[target] && (WINDOW as any)[target].prototype;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, no-prototype-builtins
if (!proto || !proto.hasOwnProperty || !proto.hasOwnProperty('addEventListener')) {
return;
Expand Down Expand Up @@ -582,9 +580,9 @@ function instrumentDOM(): void {
let _oldOnErrorHandler: OnErrorEventHandler = null;
/** JSDoc */
function instrumentError(): void {
_oldOnErrorHandler = global.onerror;
_oldOnErrorHandler = WINDOW.onerror;

global.onerror = function (msg: any, url: any, line: any, column: any, error: any): boolean {
WINDOW.onerror = function (msg: any, url: any, line: any, column: any, error: any): boolean {
triggerHandlers('error', {
column,
error,
Expand All @@ -605,9 +603,9 @@ function instrumentError(): void {
let _oldOnUnhandledRejectionHandler: ((e: any) => void) | null = null;
/** JSDoc */
function instrumentUnhandledRejection(): void {
_oldOnUnhandledRejectionHandler = global.onunhandledrejection;
_oldOnUnhandledRejectionHandler = WINDOW.onunhandledrejection;

global.onunhandledrejection = function (e: any): boolean {
WINDOW.onunhandledrejection = function (e: any): boolean {
triggerHandlers('unhandledrejection', e);

if (_oldOnUnhandledRejectionHandler) {
Expand Down
15 changes: 5 additions & 10 deletions packages/utils/src/logger.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { WrappedFunction } from '@sentry/types';

import { getGlobalObject, getGlobalSingleton } from './global';

// TODO: Implement different loggers for different environments
const global = getGlobalObject<Window | NodeJS.Global>();
import { getGlobalSingleton, GLOBAL_OBJ } from './global';

/** Prefix for logging strings */
const PREFIX = 'Sentry Logger ';
Expand All @@ -27,21 +24,19 @@ interface Logger extends LoggerConsoleMethods {
* @returns The results of the callback
*/
export function consoleSandbox<T>(callback: () => T): T {
const global = getGlobalObject<Window>();

if (!('console' in global)) {
if (!('console' in GLOBAL_OBJ)) {
return callback();
}

const originalConsole = global.console as Console & Record<string, unknown>;
const originalConsole = GLOBAL_OBJ.console as Console & Record<string, unknown>;
const wrappedLevels: Partial<LoggerConsoleMethods> = {};

// Restore all wrapped console methods
CONSOLE_LEVELS.forEach(level => {
// TODO(v7): Remove this check as it's only needed for Node 6
const originalWrappedFunc =
originalConsole[level] && (originalConsole[level] as WrappedFunction).__sentry_original__;
if (level in global.console && originalWrappedFunc) {
if (level in originalConsole && originalWrappedFunc) {
wrappedLevels[level] = originalConsole[level] as LoggerConsoleMethods[typeof level];
originalConsole[level] = originalWrappedFunc as Console[typeof level];
}
Expand Down Expand Up @@ -74,7 +69,7 @@ function makeLogger(): Logger {
logger[name] = (...args: any[]) => {
if (enabled) {
consoleSandbox(() => {
global.console[name](`${PREFIX}[${name}]:`, ...args);
GLOBAL_OBJ.console[name](`${PREFIX}[${name}]:`, ...args);
});
}
};
Expand Down
21 changes: 10 additions & 11 deletions packages/utils/src/misc.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Event, Exception, Mechanism, StackFrame } from '@sentry/types';

import { getGlobalObject } from './global';
import { GLOBAL_OBJ } from './global';
import { addNonEnumerableProperty } from './object';
import { snipLine } from './string';

/**
* Extended Window interface that allows for Crypto API usage in IE browsers
*/
interface MsCryptoWindow extends Window {
msCrypto?: Crypto;
interface CryptoInternal {
getRandomValues(array: Uint8Array): Uint8Array;
randomUUID?(): string;
}

/** Many browser now support native uuid v4 generation */
interface CryptoWithRandomUUID extends Crypto {
randomUUID?(): string;
/** An interface for common properties on global */
interface CryptoGlobal {
msCrypto?: CryptoInternal;
crypto?: CryptoInternal;
}

/**
Expand All @@ -23,8 +22,8 @@ interface CryptoWithRandomUUID extends Crypto {
* @returns string Generated UUID4.
*/
export function uuid4(): string {
const global = getGlobalObject() as MsCryptoWindow;
const crypto = (global.crypto || global.msCrypto) as CryptoWithRandomUUID;
const gbl = GLOBAL_OBJ as typeof GLOBAL_OBJ & CryptoGlobal;
const crypto = gbl.crypto || gbl.msCrypto;

if (crypto && crypto.randomUUID) {
return crypto.randomUUID().replace(/-/g, '');
Expand Down
Loading

0 comments on commit 99a0687

Please sign in to comment.