From 2b386b55aa04e467e6a4c8137a6b513c4f0ee6a0 Mon Sep 17 00:00:00 2001 From: AnastasiiaSvietlova <119674359+AnastasiiaSvietlova@users.noreply.github.com> Date: Mon, 28 Oct 2024 11:07:16 +0100 Subject: [PATCH] Convert @bugsnag/plugin-browser-request to TypeScript (#2236) * refactor: :recycle: convert plugin-window-onerror to typescript * update rollup configuration * Convert plugin-browser-request to TypeScript * changed tsconfig * fix: :adhesive_bandage: update main entry from ts to js * fix: :adhesive_bandage: add missing external dependencies to rollup config * chore: :label: update types and remove ts-expect-error comments * remove unnecessary rollup config * chore: :label: fix types * chore: :fire: remove IE8 workaround code * fix: :adhesive_bandage: fix RN init command --------- Co-authored-by: Ben Wilson --- packages/plugin-browser-request/package.json | 18 +++- .../rollup.config.npm.mjs | 6 ++ .../{request.js => src/request.ts} | 6 +- .../test/request.test.ts | 2 +- packages/plugin-browser-request/tsconfig.json | 7 ++ .../test/onerror.test.ts | 2 +- .../package.json | 2 +- .../rollup.config.npm.mjs | 3 +- .../src/fix-bluebird-stacktrace.ts | 29 ++++++ .../src/unhandled-rejection.ts | 92 ++++++++----------- .../tsconfig.json | 1 + tsconfig.json | 1 - 12 files changed, 104 insertions(+), 65 deletions(-) create mode 100644 packages/plugin-browser-request/rollup.config.npm.mjs rename packages/plugin-browser-request/{request.js => src/request.ts} (57%) create mode 100644 packages/plugin-browser-request/tsconfig.json create mode 100644 packages/plugin-window-unhandled-rejection/src/fix-bluebird-stacktrace.ts diff --git a/packages/plugin-browser-request/package.json b/packages/plugin-browser-request/package.json index d6f07dcabd..02b4e4f296 100644 --- a/packages/plugin-browser-request/package.json +++ b/packages/plugin-browser-request/package.json @@ -1,7 +1,15 @@ { "name": "@bugsnag/plugin-browser-request", "version": "8.1.1", - "main": "request.js", + "main": "dist/request.js", + "types": "dist/types/request.d.ts", + "exports": { + ".": { + "types": "./dist/types/request.d.ts", + "default": "./dist/request.js", + "import": "./dist/request.mjs" + } + }, "description": "@bugsnag/js plugin to set request info in browsers", "homepage": "https://www.bugsnag.com/", "repository": { @@ -12,9 +20,8 @@ "access": "public" }, "files": [ - "*.js" + "dist" ], - "scripts": {}, "author": "Bugsnag", "license": "MIT", "devDependencies": { @@ -22,5 +29,10 @@ }, "peerDependencies": { "@bugsnag/core": "^8.0.0" + }, + "scripts": { + "build": "npm run build:npm", + "build:npm": "rollup --config rollup.config.npm.mjs", + "clean": "rm -rf dist/*" } } diff --git a/packages/plugin-browser-request/rollup.config.npm.mjs b/packages/plugin-browser-request/rollup.config.npm.mjs new file mode 100644 index 0000000000..00f813ac9a --- /dev/null +++ b/packages/plugin-browser-request/rollup.config.npm.mjs @@ -0,0 +1,6 @@ +import createRollupConfig from "../../.rollup/index.mjs"; + +export default createRollupConfig({ + input: "src/request.ts", + external: ['@bugsnag/core/lib/es-utils/assign'] +}); diff --git a/packages/plugin-browser-request/request.js b/packages/plugin-browser-request/src/request.ts similarity index 57% rename from packages/plugin-browser-request/request.js rename to packages/plugin-browser-request/src/request.ts index 83e18d771b..68866c7921 100644 --- a/packages/plugin-browser-request/request.js +++ b/packages/plugin-browser-request/src/request.ts @@ -1,13 +1,15 @@ -const assign = require('@bugsnag/core/lib/es-utils/assign') +import { Plugin } from '@bugsnag/core' +import assign from '@bugsnag/core/lib/es-utils/assign' /* * Sets the event request: { url } to be the current href */ -module.exports = (win = window) => ({ +export default (win = window): Plugin => ({ load: (client) => { client.addOnError(event => { if (event.request && event.request.url) return event.request = assign({}, event.request, { url: win.location.href }) + // @ts-expect-error second parameter is private API }, true) } }) diff --git a/packages/plugin-browser-request/test/request.test.ts b/packages/plugin-browser-request/test/request.test.ts index e0e225baa2..3610374e14 100644 --- a/packages/plugin-browser-request/test/request.test.ts +++ b/packages/plugin-browser-request/test/request.test.ts @@ -1,4 +1,4 @@ -import plugin from '../' +import plugin from '../src/request' import Client, { EventDeliveryPayload } from '@bugsnag/core/client' diff --git a/packages/plugin-browser-request/tsconfig.json b/packages/plugin-browser-request/tsconfig.json new file mode 100644 index 0000000000..9478c51300 --- /dev/null +++ b/packages/plugin-browser-request/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": { + "target": "ES2020" + } +} diff --git a/packages/plugin-window-onerror/test/onerror.test.ts b/packages/plugin-window-onerror/test/onerror.test.ts index 1bd7eeee27..b37b621d66 100644 --- a/packages/plugin-window-onerror/test/onerror.test.ts +++ b/packages/plugin-window-onerror/test/onerror.test.ts @@ -1,6 +1,6 @@ /* eslint-disable jest/no-commented-out-tests */ -import plugin from '../' +import plugin from '../src/onerror' import Client, { EventDeliveryPayload } from '@bugsnag/core/client' diff --git a/packages/plugin-window-unhandled-rejection/package.json b/packages/plugin-window-unhandled-rejection/package.json index e6335bf601..1eb44213c0 100644 --- a/packages/plugin-window-unhandled-rejection/package.json +++ b/packages/plugin-window-unhandled-rejection/package.json @@ -20,7 +20,7 @@ "access": "public" }, "files": [ - "*.js" + "dist" ], "author": "Bugsnag", "license": "MIT", diff --git a/packages/plugin-window-unhandled-rejection/rollup.config.npm.mjs b/packages/plugin-window-unhandled-rejection/rollup.config.npm.mjs index ce422d72a3..dd1a7d102d 100644 --- a/packages/plugin-window-unhandled-rejection/rollup.config.npm.mjs +++ b/packages/plugin-window-unhandled-rejection/rollup.config.npm.mjs @@ -1,5 +1,6 @@ import createRollupConfig from '../../.rollup/index.mjs' export default createRollupConfig({ - input: 'src/unhandled-rejection.ts' + input: 'src/unhandled-rejection.ts', + external: ['@bugsnag/core/lib/iserror', '@bugsnag/core/lib/es-utils/map'] }) diff --git a/packages/plugin-window-unhandled-rejection/src/fix-bluebird-stacktrace.ts b/packages/plugin-window-unhandled-rejection/src/fix-bluebird-stacktrace.ts new file mode 100644 index 0000000000..1e4740c82a --- /dev/null +++ b/packages/plugin-window-unhandled-rejection/src/fix-bluebird-stacktrace.ts @@ -0,0 +1,29 @@ +import { Stackframe } from 'packages/core/types' + +// The stack parser on bluebird stacks in FF get a suprious first frame: +// +// Error: derp +// b@http://localhost:5000/bluebird.html:22:24 +// a@http://localhost:5000/bluebird.html:18:9 +// @http://localhost:5000/bluebird.html:14:9 +// +// results in +// […] +// 0: Object { file: "Error: derp", method: undefined, lineNumber: undefined, … } +// 1: Object { file: "http://localhost:5000/bluebird.html", method: "b", lineNumber: 22, … } +// 2: Object { file: "http://localhost:5000/bluebird.html", method: "a", lineNumber: 18, … } +// 3: Object { file: "http://localhost:5000/bluebird.html", lineNumber: 14, columnNumber: 9, … } +// +// so the following reduce/accumulator function removes such frames +// +// Bluebird pads method names with spaces so trim that too… + +// https://github.com/petkaantonov/bluebird/blob/b7f21399816d02f979fe434585334ce901dcaf44/src/debuggability.js#L568-L571 +const fixBluebirdStacktrace = (error: PromiseRejectionEvent['reason']) => (frame: Stackframe) => { + if (frame.file === error.toString()) return + if (frame.method) { + frame.method = frame.method.replace(/^\s+/, '') + } +} + +export default fixBluebirdStacktrace diff --git a/packages/plugin-window-unhandled-rejection/src/unhandled-rejection.ts b/packages/plugin-window-unhandled-rejection/src/unhandled-rejection.ts index abba8cefcd..5434f4b584 100644 --- a/packages/plugin-window-unhandled-rejection/src/unhandled-rejection.ts +++ b/packages/plugin-window-unhandled-rejection/src/unhandled-rejection.ts @@ -1,82 +1,89 @@ -import { Plugin, Stackframe } from '@bugsnag/core' +import { Client, Logger, Plugin } from '@bugsnag/core' import map from '@bugsnag/core/lib/es-utils/map' import isError from '@bugsnag/core/lib/iserror' +import fixBluebirdStacktrace from './fix-bluebird-stacktrace' type Listener = (evt: PromiseRejectionEvent) => void let _listener: Listener | null +interface InternalClient extends Client { + _config: { + autoDetectErrors: boolean + enabledErrorTypes: { + unhandledRejections: boolean + } + reportUnhandledPromiseRejectionsAsHandled: boolean + } + _logger: Logger +} + +interface BluebirdPromiseRejectionEvent { + detail?: { + reason: PromiseRejectionEvent['reason'] + promise: PromiseRejectionEvent['promise'] + } +} + /* * Automatically notifies Bugsnag when window.onunhandledrejection is called */ export default (win = window): Plugin => { const plugin: Plugin = { load: (client) => { - // @ts-expect-error _config is private API - if (!client._config.autoDetectErrors || !client._config.enabledErrorTypes.unhandledRejections) return - const listener = (evt: PromiseRejectionEvent) => { - let error = evt.reason + const internalClient = client as InternalClient + + if (!internalClient._config.autoDetectErrors || !internalClient._config.enabledErrorTypes.unhandledRejections) return + const listener: Listener = (ev) => { + const bluebirdEvent = ev as BluebirdPromiseRejectionEvent + + let error = ev.reason let isBluebird = false // accessing properties on evt.detail can throw errors (see #394) try { - // @ts-expect-error detail does not exist on type PromiseRejectionEvent - if (evt.detail && evt.detail.reason) { - // @ts-expect-error detail does not exist on type PromiseRejectionEvent - error = evt.detail.reason + if (bluebirdEvent.detail && bluebirdEvent.detail.reason) { + error = bluebirdEvent.detail.reason isBluebird = true } } catch (e) {} - // Report unhandled promise rejections as handled if the user has configured it - // @ts-expect-error _config is private API - const unhandled = !client._config.reportUnhandledPromiseRejectionsAsHandled - - const event = client.Event.create(error, false, { + const event = internalClient.Event.create(error, false, { severity: 'error', - unhandled, + // Report unhandled promise rejections as handled when set by config + unhandled: !internalClient._config.reportUnhandledPromiseRejectionsAsHandled, severityReason: { type: 'unhandledPromiseRejection' } - // @ts-expect-error _logger is private API - }, 'unhandledrejection handler', 1, client._logger) + }, 'unhandledrejection handler', 1, internalClient._logger) if (isBluebird) { map(event.errors[0].stacktrace, fixBluebirdStacktrace(error)) } - client._notify(event, (event) => { + internalClient._notify(event, (event) => { if (isError(event.originalError) && !event.originalError.stack) { event.addMetadata('unhandledRejection handler', { [Object.prototype.toString.call(event.originalError)]: { name: event.originalError.name, message: event.originalError.message, - // @ts-expect-error Property 'code' does not exist on type 'Error' + // @ts-expect-error optional error.code property code: event.originalError.code } }) } }) } - if ('addEventListener' in win) { + if (typeof win.addEventListener === 'function') { win.addEventListener('unhandledrejection', listener) - } else { - // @ts-expect-error onunhandledrejection does not exist on type never - win.onunhandledrejection = (reason, promise) => { - // @ts-expect-error detail does not exist on type PromiseRejectionEvent - listener({ detail: { reason, promise } }) - } } _listener = listener } } - // @ts-expect-error cannot find name 'process' if (process.env.NODE_ENV !== 'production') { plugin.destroy = (win = window) => { if (_listener) { - if ('addEventListener' in win) { + if (typeof win.removeEventListener === 'function') { win.removeEventListener('unhandledrejection', _listener) - } else { - (win as Window).onunhandledrejection = null } } _listener = null @@ -85,28 +92,3 @@ export default (win = window): Plugin => { return plugin } - -// The stack parser on bluebird stacks in FF get a suprious first frame: -// -// Error: derp -// b@http://localhost:5000/bluebird.html:22:24 -// a@http://localhost:5000/bluebird.html:18:9 -// @http://localhost:5000/bluebird.html:14:9 -// -// results in -// […] -// 0: Object { file: "Error: derp", method: undefined, lineNumber: undefined, … } -// 1: Object { file: "http://localhost:5000/bluebird.html", method: "b", lineNumber: 22, … } -// 2: Object { file: "http://localhost:5000/bluebird.html", method: "a", lineNumber: 18, … } -// 3: Object { file: "http://localhost:5000/bluebird.html", lineNumber: 14, columnNumber: 9, … } -// -// so the following reduce/accumulator function removes such frames -// -// Bluebird pads method names with spaces so trim that too… -// https://github.com/petkaantonov/bluebird/blob/b7f21399816d02f979fe434585334ce901dcaf44/src/debuggability.js#L568-L571 -const fixBluebirdStacktrace = (error: PromiseRejectionEvent['reason']) => (frame: Stackframe) => { - if (frame.file === error.toString()) return - if (frame.method) { - frame.method = frame.method.replace(/^\s+/, '') - } -} diff --git a/packages/plugin-window-unhandled-rejection/tsconfig.json b/packages/plugin-window-unhandled-rejection/tsconfig.json index c76cdab7cd..7ef24300eb 100644 --- a/packages/plugin-window-unhandled-rejection/tsconfig.json +++ b/packages/plugin-window-unhandled-rejection/tsconfig.json @@ -2,6 +2,7 @@ "extends": "../../tsconfig.json", "include": ["src/**/*.ts"], "compilerOptions": { + "types": ["node"], "target": "ES2020" } } diff --git a/tsconfig.json b/tsconfig.json index b236ba9048..e619f3ad1d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -82,7 +82,6 @@ "packages/plugin-strip-query-string", "packages/plugin-strip-project-root", "packages/plugin-interaction-breadcrumbs", - "packages/plugin-browser-request", "packages/plugin-simple-throttle", "packages/plugin-intercept", "packages/plugin-node-unhandled-rejection",