diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..651ab83 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,10 @@ +.github +.next +build +bundles +coverage +dist +lib +node_modules +out +public diff --git a/.eslintrc.yaml b/.eslintrc.yaml index fda49f7..bfcc560 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -10,38 +10,38 @@ extends: - plugin:react-hooks/recommended - plugin:jest/recommended - plugin:jest-dom/recommended -parser: "@babel/eslint-parser" +parser: '@babel/eslint-parser' plugins: - - "@babel" + - '@babel' - jest - react - react-hooks - testing-library overrides: - files: - - "**/__tests__/**/*.[jt]s?(x)" - - "**/?(*.)+(spec|test).[jt]s?(x)" + - '**/__tests__/**/*.[jt]s?(x)' + - '**/?(*.)+(spec|test).[jt]s?(x)' extends: - - "plugin:testing-library/react" + - 'plugin:testing-library/react' - files: - - "**/*.ts?(x)" - parser: "@typescript-eslint/parser" + - '**/*.ts?(x)' + parser: '@typescript-eslint/parser' parserOptions: - tsconfigRootDir: . - project: ['./tsconfig.json'] + tsconfigRootDir: . + project: ['./tsconfig.json'] extends: - eslint:recommended - plugin:@typescript-eslint/eslint-recommended - plugin:@typescript-eslint/recommended - plugin:@typescript-eslint/recommended-requiring-type-checking rules: - "@typescript-eslint/no-unused-vars": + '@typescript-eslint/no-unused-vars': - error - - varsIgnorePattern: "^_" - argsIgnorePattern: "^_" + - varsIgnorePattern: '^_' + argsIgnorePattern: '^_' # For compat with jest: https://typescript-eslint.io/rules/unbound-method/ - "@typescript-eslint/unbound-method": "off" - "jest/unbound-method": "error" + '@typescript-eslint/unbound-method': 'off' + 'jest/unbound-method': 'error' rules: quotes: - error diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2077320..f16b6ce 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,14 +1,15 @@ -name: rollbar-react CI +name: ci on: push: - branches: [ '**' ] + branches: + - main pull_request: - branches: [ main ] + branches: + - main jobs: build: - runs-on: ubuntu-latest strategy: @@ -16,12 +17,47 @@ jobs: node-version: [14.x] steps: - - uses: actions/checkout@v2 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - run: npm install - - run: npm run lint - - run: npm run test + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up node ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Cache + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ matrix.node-version }}- + ${{ runner.os }}-node- + ${{ runner.os }}- + + - name: Install dependencies + run: npm ci + + - name: Install examples/create-react-app dependencies + run: npm ci + working-directory: ./examples/create-react-app + + - name: Install examples/nextjs dependencies + run: npm ci + working-directory: ./examples/nextjs + + - name: Install examples/typescript dependencies + run: npm ci + working-directory: ./examples/typescript + + - name: Lint + uses: wearerequired/lint-action@v2 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + prettier: true + eslint: true + eslint_extensions: js,jsx,ts,tsx + eslint_args: '--max-warnings 0' + + - name: Test + run: npm run test diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 444484d..4f40bdf 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -6,16 +6,16 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - # Setup .npmrc file to publish to GitHub Packages - - uses: actions/setup-node@v2 - with: - node-version: '12.x' - registry-url: 'https://registry.npmjs.org' - # Defaults to the user or organization that owns the workflow file - # scope: '@rollbar' - - run: npm install - - run: npm run build - - run: npm publish --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - uses: actions/checkout@v2 + # Setup .npmrc file to publish to GitHub Packages + - uses: actions/setup-node@v2 + with: + node-version: "12.x" + registry-url: "https://registry.npmjs.org" + # Defaults to the user or organization that owns the workflow file + # scope: '@rollbar' + - run: npm install + - run: npm run build + - run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..651ab83 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,10 @@ +.github +.next +build +bundles +coverage +dist +lib +node_modules +out +public diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..192c739 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,38 @@ +{ + "[github-actions-workflow]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + }, + "[javascriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + }, + "[json5]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + }, + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.formatOnSaveMode": "file", + "editor.tabSize": 2, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "files.trimTrailingWhitespace": true +} diff --git a/README.md b/README.md index 3677847..bc8495c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ --- - React features to enhance using Rollbar.js in React Applications. This SDK provides a wrapper around the base [Rollbar.js] SDK in order to provide an @@ -18,12 +17,12 @@ SDK that matches the intention of how to build React Apps with a declarative API hooks and ErrorBoundaries, as well as simplify using Rollbar within a React SPA. ## Key benefits of using Rollbar React are: + - **Telemetry:** The telemetry timeline provides a list of “breadcrumbs” events that can help developers understand and fix problems in their client-side javascript. Learn more about telemetry. - **Automatic error grouping:** Rollbar aggregates Occurrences caused by the same error into Items that represent application issues. Learn more about reducing log noise. - **Advanced search:** Filter items by many different properties. Learn more about search. - **Customizable notifications:** Rollbar supports several messaging and incident management tools where your team can get notified about errors and important events by real-time alerts. Learn more about Rollbar notifications. - ### In Beta It is currently in a public Beta release right now as we push towards a 1.0 release that will have all of the features @@ -143,21 +142,17 @@ The simplest way to use the `Provider` is to provide a configuration as the `con instance of Rollbar for you and provide that to its child tree: ```javascript -import React from 'react'; -import { Provider } from '@rollbar/react'; +import React from 'react' +import { Provider } from '@rollbar/react' // same configuration you would create for the Rollbar.js SDK const rollbarConfig = { accessToken: 'POST_CLIENT_ITEM_ACCESS_TOKEN', environment: 'production', -}; +} export function App(props) { - return ( - - … - - ); + return } ``` @@ -167,24 +162,20 @@ Sometimes you may need to instantiate an instance of Rollbar before adding it to `instance` prop to pass it to the `Provider` like this: ```javascript -import React from 'react'; -import Rollbar from 'rollbar'; -import { Provider } from '@rollbar/react'; +import React from 'react' +import Rollbar from 'rollbar' +import { Provider } from '@rollbar/react' // same configuration you would create for the Rollbar.js SDK const rollbarConfig = { accessToken: 'POST_CLIENT_ITEM_ACCESS_TOKEN', environment: 'production', -}; +} -const rollbar = new Rollbar(rollbarConfig); +const rollbar = new Rollbar(rollbarConfig) export function App(props) { - return ( - - … - - ); + return } ``` @@ -199,18 +190,14 @@ To use the Rollbar React SDK with the [React Native SDK], pass the instance that `instance` prop, like this: ```javascript -import React from 'react'; -import { Client } from 'rollbar-react-native'; -import { Provider } from '@rollbar/react'; +import React from 'react' +import { Client } from 'rollbar-react-native' +import { Provider } from '@rollbar/react' -const rollbarClient = new Client('POST_CLIENT_ITEM_ACCESS_TOKEN'); +const rollbarClient = new Client('POST_CLIENT_ITEM_ACCESS_TOKEN') export function App(props) { - return ( - - … - - ); + return } ``` @@ -231,23 +218,21 @@ You can add an `ErrorBoundary` component to the top of your tree right after the and it will just work: ```javascript -import React from 'react'; -import { Provider, ErrorBoundary } from '@rollbar/react'; +import React from 'react' +import { Provider, ErrorBoundary } from '@rollbar/react' // same configuration you would create for the Rollbar.js SDK const rollbarConfig = { accessToken: 'POST_CLIENT_ITEM_ACCESS_TOKEN', environment: 'production', -}; +} export function App(props) { return ( - - … - + - ); + ) } ``` @@ -260,23 +245,31 @@ These `prop`s take either a value or a function that will be invoked with the `e API's `componentDidCatch` method (i.e. signature is `(error, info)`). ```javascript -import React from 'react'; -import { Provider, ErrorBoundary, LEVEL_WARN } from '@rollbar/react'; +import React from 'react' +import { Provider, ErrorBoundary, LEVEL_WARN } from '@rollbar/react' // same configuration you would create for the Rollbar.js SDK const rollbarConfig = { accessToken: 'POST_CLIENT_ITEM_ACCESS_TOKEN', environment: 'production', -}; +} export function App(props) { return ( - info.componentStack.includes('Experimental') ? { experiment: true } : {} }> + + info.componentStack.includes('Experimental') + ? { experiment: true } + : {} + } + > … - ); + ) } ``` @@ -288,21 +281,23 @@ UI caused during the render cycle of React. It can accept a value that is a React Component ```javascript -import React from 'react'; -import { Provider, ErrorBoundary, LEVEL_WARN } from '@rollbar/react'; +import React from 'react' +import { Provider, ErrorBoundary, LEVEL_WARN } from '@rollbar/react' // same configuration you would create for the Rollbar.js SDK const rollbarConfig = { accessToken: 'POST_CLIENT_ITEM_ACCESS_TOKEN', environment: 'production', -}; +} -const ErrorDisplay = ({ error, resetError }) => ( // <-- props passed to fallbackUI component +const ErrorDisplay = ( + { error, resetError } // <-- props passed to fallbackUI component +) => (

A following error has occured:

{error.toString()}

-); +) export function App(props) { return ( @@ -311,7 +306,7 @@ export function App(props) { … - ); + ) } ``` @@ -331,15 +326,11 @@ To use the `RollbarContext` you must provide the `context` prop, a `String` that [Rollbar.js] to the value while mounted. ```javascript -import React from 'react'; -import { RollbarContext } from '@rollbar/react'; +import React from 'react' +import { RollbarContext } from '@rollbar/react' function HomePage() { - return ( - - … - - ) + return } ``` @@ -352,10 +343,10 @@ to achieve the same result. Here is an example of using `RollbarContext` with [React Router] if you have a top level set of routes: ```javascript -import React from 'react'; -import { Router, Switch, Route } from 'react-router-dom'; -import { RollbarContext } from '@rollbar/react'; -import { About, ContactDetails, ContactsList } from './pages'; +import React from 'react' +import { Router, Switch, Route } from 'react-router-dom' +import { RollbarContext } from '@rollbar/react' +import { About, ContactDetails, ContactsList } from './pages' const Routes = () => ( @@ -379,22 +370,20 @@ const Routes = () => ( ) -export default Routes; +export default Routes ``` Here's another example of using `RollbarContext` within a component that manages its own route: ```javascript -import React from 'react'; -import { Route } from 'react-router-dom'; -import { RollbarContext } from '@rollbar/react'; +import React from 'react' +import { Route } from 'react-router-dom' +import { RollbarContext } from '@rollbar/react' export default function About(props) { return ( - - … - + ) } @@ -422,21 +411,21 @@ By default, if no options (see below) are provided, all history updates will upd `location.pathname` as the value. ```javascript -import Rollbar from 'rollbar'; -import { createBrowserHistory } from 'history'; -import { Provider } from '@rollbar/react'; +import Rollbar from 'rollbar' +import { createBrowserHistory } from 'history' +import { Provider } from '@rollbar/react' // same configuration you would create for the Rollbar.js SDK const rollbarConfig = { accessToken: 'POST_CLIENT_ITEM_ACCESS_TOKEN', environment: 'production', -}; +} -const rollbar = new Rollbar(rollbarConfig); +const rollbar = new Rollbar(rollbarConfig) -const history = createBrowserHistory(); +const history = createBrowserHistory() -history.listen(historyContext(rollbar)); +history.listen(historyContext(rollbar)) ``` #### Controlling `historyContext` behavior with options @@ -458,27 +447,28 @@ The signature is `filter(location, action): Boolean` where `location` is [histor Here's an example of using both: ```javascript -import Rollbar from 'rollbar'; -import { createBrowserHistory } from 'history'; -import { Provider } from '@rollbar/react'; +import Rollbar from 'rollbar' +import { createBrowserHistory } from 'history' +import { Provider } from '@rollbar/react' // same configuration you would create for the Rollbar.js SDK const rollbarConfig = { accessToken: 'POST_CLIENT_ITEM_ACCESS_TOKEN', environment: 'production', -}; +} -const rollbar = new Rollbar(rollbarConfig); +const rollbar = new Rollbar(rollbarConfig) -const ROUTE_PARAMS_RE = /\/\d+/g; +const ROUTE_PARAMS_RE = /\/\d+/g const historyListener = historyContext(rollbar, { // optional: default uses location.pathname - formatter: (location, action) => location.pathname.replace(ROUTE_PARAMS_RE, ''), + formatter: (location, action) => + location.pathname.replace(ROUTE_PARAMS_RE, ''), // optional: true return sets Rollbar context filter: (location, action) => !location.pathname.includes('admin'), -}); -const unlisten = history.listen(historyListener); +}) +const unlisten = history.listen(historyListener) ``` ## Hooks @@ -498,26 +488,22 @@ your [Functional Component], use the `useRollbar` hook which will return the ins Here is a basic example: ```javascript -import { useRollbar } from '@rollbar/react'; +import { useRollbar } from '@rollbar/react' function ContactDetails({ contactId }) { - const rollbar = useRollbar(); // <-- must have parent Provider - const [contact, setContact] = useState(); + const rollbar = useRollbar() // <-- must have parent Provider + const [contact, setContact] = useState() useEffect(async () => { try { - const { data } = await getContactFromApi(contactId); - setContact(data.contact); + const { data } = await getContactFromApi(contactId) + setContact(data.contact) } catch (error) { - rollbar.error('Error fetching contact', error, { contactId }); + rollbar.error('Error fetching contact', error, { contactId }) } - }, [contactId]); + }, [contactId]) - return ( -
- … -
- ); + return
} ``` @@ -530,40 +516,32 @@ Here's an example of using it in several components: ```javascript // src/pages/HomePage.js -import { useRollbarContext } from '@rollbar/react'; +import { useRollbarContext } from '@rollbar/react' function HomePage(props) { - useRollbarContext('home#index'); + useRollbarContext('home#index') - return ( -
- … -
- ); + return
} // src/pages/UsersPage.js -import { useRollbarContext } from '@rollbar/react'; -import UserTable from '../components/users/UserTable'; +import { useRollbarContext } from '@rollbar/react' +import UserTable from '../components/users/UserTable' function UsersPage(props) { - useRollbarContext('users#list'); + useRollbarContext('users#list') - return ( - - ); + return } // src/pages/UserDetailsPage.js -import { useRollbarContext } from '@rollbar/react'; -import UserDetails from '../components/users/UserDetails'; +import { useRollbarContext } from '@rollbar/react' +import UserDetails from '../components/users/UserDetails' function UserDetailsPage(props) { - useRollbarContext('users#details'); + useRollbarContext('users#details') - return ( - - ); + return } ``` @@ -583,27 +561,27 @@ and for any future events or messages logged to [Rollbar] will include that pers Here is a simple example of using it once the current user has been determined: ```javascript -import { useState } from 'react'; -import { useRollbarPerson } from '@rollbar/react'; -import LoggedInHome from './LoggedInHome'; -import LoggedOutHome from './LoggedOutHome'; +import { useState } from 'react' +import { useRollbarPerson } from '@rollbar/react' +import LoggedInHome from './LoggedInHome' +import LoggedOutHome from './LoggedOutHome' function Home() { - const [currentUser, setCurrentUser] = useState(); - useRollbarPerson(currentUser); + const [currentUser, setCurrentUser] = useState() + useRollbarPerson(currentUser) useEffect(() => { - (async () => { - const user = await Auth.getCurrentUser(); - setCurrentUser(user); + ;(async () => { + const user = await Auth.getCurrentUser() + setCurrentUser(user) })() - }); + }) if (currentUser != null) { - return ; + return } - return ; + return } ``` @@ -624,46 +602,41 @@ Here is an example of using `useRollbarCaptureEvent` in the render cycle of a [F event related to the data that will be rendered in the component ```javascript -import { useEffect, useState } from 'react'; -import { useRollbar, useRollbarCaptureEvent, LEVEL_INFO } from '@rollbar/react'; +import { useEffect, useState } from 'react' +import { useRollbar, useRollbarCaptureEvent, LEVEL_INFO } from '@rollbar/react' function BookDetails({ bookId }) { - const rollbar = useRollbar(); // <-- must have ancestor Provider, same with useRollbarCaptureEvent - const [book, setBook] = useState(); + const rollbar = useRollbar() // <-- must have ancestor Provider, same with useRollbarCaptureEvent + const [book, setBook] = useState() useEffect(async () => { try { - const { data } = await getBook(bookId); - setBook(data.book); + const { data } = await getBook(bookId) + setBook(data.book) } catch (error) { - rollbar.error('Error fetching book', error, { bookId }); // <-- use rollbar to log errors as normal + rollbar.error('Error fetching book', error, { bookId }) // <-- use rollbar to log errors as normal } - }, [bookId]); + }, [bookId]) - useRollbarCaptureEvent(book, LEVEL_INFO); // <-- only fires when book changes + useRollbarCaptureEvent(book, LEVEL_INFO) // <-- only fires when book changes - return ( -
- … -
- ) + return
} ``` - -[Rollbar]: https://rollbar.com -[Rollbar Docs]: https://docs.rollbar.com -[Rollbar.js]: https://github.com/rollbar/rollbar.js -[Rollbar.js Setup Instructions]: https://github.com/rollbar/rollbar.js/#setup-instructions -[React Native SDK]: https://github.com/rollbar/rollbar-react-native -[Telemetry]: https://docs.rollbar.com/docs/rollbarjs-telemetry -[`Provider`]: #provider-component -[`ErrorBoundary`]: #errorboundary-component -[`RollbarContext`]: #rollbarcontext-component -[Functional Components]: https://reactjs.org/docs/components-and-props.html#function-and-class-components -[React Context]: https://reactjs.org/docs/context.html -[Error Boundaries]: https://reactjs.org/docs/error-boundaries.html -[React Hooks API]: https://reactjs.org/docs/hooks-intro.html +[rollbar]: https://rollbar.com +[rollbar docs]: https://docs.rollbar.com +[rollbar.js]: https://github.com/rollbar/rollbar.js +[rollbar.js setup instructions]: https://github.com/rollbar/rollbar.js/#setup-instructions +[react native sdk]: https://github.com/rollbar/rollbar-react-native +[telemetry]: https://docs.rollbar.com/docs/rollbarjs-telemetry +[`provider`]: #provider-component +[`errorboundary`]: #errorboundary-component +[`rollbarcontext`]: #rollbarcontext-component +[functional components]: https://reactjs.org/docs/components-and-props.html#function-and-class-components +[react context]: https://reactjs.org/docs/context.html +[error boundaries]: https://reactjs.org/docs/error-boundaries.html +[react hooks api]: https://reactjs.org/docs/hooks-intro.html [history]: https://www.npmjs.com/package/history [history.location]: https://github.com/ReactTraining/history/blob/master/docs/api-reference.md#location [history.action]: https://github.com/ReactTraining/history/blob/master/docs/api-reference.md#action diff --git a/babel.config.js b/babel.config.js index 311b920..b654684 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,18 +1,15 @@ module.exports = { - presets: [ - '@babel/preset-env', - '@babel/preset-react' - ], + presets: ['@babel/preset-env', '@babel/preset-react'], plugins: [ '@babel/plugin-proposal-class-properties', - '@babel/plugin-syntax-class-properties' + '@babel/plugin-syntax-class-properties', ], env: { test: { plugins: [ '@babel/plugin-transform-modules-commonjs', - 'babel-plugin-istanbul' - ] - } - } + 'babel-plugin-istanbul', + ], + }, + }, } diff --git a/examples/create-react-app/package.json b/examples/create-react-app/package.json index fe33a1b..4473755 100644 --- a/examples/create-react-app/package.json +++ b/examples/create-react-app/package.json @@ -21,6 +21,7 @@ "eject": "react-scripts eject" }, "eslintConfig": { + "root": true, "extends": [ "react-app", "react-app/jest" diff --git a/examples/create-react-app/src/App.js b/examples/create-react-app/src/App.js index 7d08425..f0dc5f1 100644 --- a/examples/create-react-app/src/App.js +++ b/examples/create-react-app/src/App.js @@ -4,17 +4,14 @@ import { Provider as RollbarProvider, ErrorBoundary, RollbarContext, - useRollbar + useRollbar, } from '@rollbar/react' -import './App.css'; +import './App.css' const rollbarConfig = { accessToken: process.env.REACT_APP_PUBLIC_ROLLBAR_TOKEN, - hostSafeList: [ - 'localhost:3000', - 'localhost:4000', - ], + hostSafeList: ['localhost:3000', 'localhost:4000'], captureUncaught: true, captureUnhandledRejections: true, payload: { @@ -26,7 +23,7 @@ const rollbarConfig = { }, }, }, -}; +} function App() { return ( @@ -34,7 +31,9 @@ function App() {

Oops, there was an error.

} + fallbackUI={() => ( +

Oops, there was an error.

+ )} extra={{ more: 'data' }} callback={() => console.log('an exception was sent to rollbar')} > @@ -51,10 +50,10 @@ function App() {
- ); + ) } -export default App; +export default App function Router() { return ( @@ -62,50 +61,55 @@ function Router() { } /> } /> - ); + ) } function RouteA() { - const rollbar = useRollbar(); + const rollbar = useRollbar() - const [message, setMessage] = useState('example'); + const [message, setMessage] = useState('example') - const fullMessage = useMemo(() => `Hello, ${message}!`, [message]); + const fullMessage = useMemo(() => `Hello, ${message}!`, [message]) return (

A

Message: {fullMessage}

- setMessage(event.target.value)} /> + setMessage(event.target.value)} + />
- +
- ); + ) } function RouteB() { - const [errorState, setErrorState] = useState({ error: false }); + const [errorState, setErrorState] = useState({ error: false }) const updateErrorState = () => { // Use an error state and throw inside render, // because React won't send errors within event handlers // to the error boundary component. setErrorState({ - error: true - }); - }; + error: true, + }) + } if (errorState.error) { // This uncaught error will be handled by the ErrorBoundary. - throw new Error('uncaught test error'); + throw new Error('uncaught test error') } return (

B

-
- ); + ) } diff --git a/examples/create-react-app/src/App.test.js b/examples/create-react-app/src/App.test.js index 1f03afe..4741580 100644 --- a/examples/create-react-app/src/App.test.js +++ b/examples/create-react-app/src/App.test.js @@ -1,8 +1,8 @@ -import { render, screen } from '@testing-library/react'; -import App from './App'; +import { render, screen } from '@testing-library/react' +import App from './App' test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); + render() + const linkElement = screen.getByText(/learn react/i) + expect(linkElement).toBeInTheDocument() +}) diff --git a/examples/create-react-app/src/index.js b/examples/create-react-app/src/index.js index 44b5f8b..1c0b889 100644 --- a/examples/create-react-app/src/index.js +++ b/examples/create-react-app/src/index.js @@ -1,9 +1,9 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { BrowserRouter } from "react-router-dom"; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; +import React from 'react' +import ReactDOM from 'react-dom' +import { BrowserRouter } from 'react-router-dom' +import './index.css' +import App from './App' +import reportWebVitals from './reportWebVitals' ReactDOM.render( @@ -12,9 +12,9 @@ ReactDOM.render( , document.getElementById('root') -); +) // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); +reportWebVitals() diff --git a/examples/create-react-app/src/logo.svg b/examples/create-react-app/src/logo.svg index 9dfc1c0..7169476 100644 --- a/examples/create-react-app/src/logo.svg +++ b/examples/create-react-app/src/logo.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/examples/create-react-app/src/reportWebVitals.js b/examples/create-react-app/src/reportWebVitals.js index 5253d3a..dc6ff07 100644 --- a/examples/create-react-app/src/reportWebVitals.js +++ b/examples/create-react-app/src/reportWebVitals.js @@ -1,13 +1,13 @@ -const reportWebVitals = onPerfEntry => { +const reportWebVitals = (onPerfEntry) => { if (onPerfEntry && onPerfEntry instanceof Function) { import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); + getCLS(onPerfEntry) + getFID(onPerfEntry) + getFCP(onPerfEntry) + getLCP(onPerfEntry) + getTTFB(onPerfEntry) + }) } -}; +} -export default reportWebVitals; +export default reportWebVitals diff --git a/examples/create-react-app/src/setupTests.js b/examples/create-react-app/src/setupTests.js index 8f2609b..52aaef1 100644 --- a/examples/create-react-app/src/setupTests.js +++ b/examples/create-react-app/src/setupTests.js @@ -2,4 +2,4 @@ // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; +import '@testing-library/jest-dom' diff --git a/examples/index.js b/examples/index.js index 2a29787..0808f28 100644 --- a/examples/index.js +++ b/examples/index.js @@ -1,50 +1,69 @@ -import React, { useEffect, useState } from 'react'; -import Rollbar from 'rollbar'; -import { Router, Switch, Route } from 'react-router-dom'; -import { Client } from 'rollbar-react-native'; -import { Provider, Context, ErrorBoundary, useRollbar, useRollbarCaptureEvent, LEVEL_INFO, useRollbarPerson, useContext, RollbarContext, historyContext } from '../src'; - -function ErrorDisplay({error, resetError}) { // <-- props passed to fallbackUI component +/* eslint-disable no-unused-vars, react/jsx-no-undef, react/prop-types*/ + +import React, { useEffect, useState } from 'react' +import Rollbar from 'rollbar' +import { Router, Switch, Route } from 'react-router-dom' +import { Client } from 'rollbar-react-native' +import { + Provider, + Context, + ErrorBoundary, + useRollbar, + useRollbarCaptureEvent, + LEVEL_INFO, + useRollbarPerson, + useContext, + RollbarContext, + historyContext, +} from '../src' + +function ErrorDisplay({ error, resetError }) { + // <-- props passed to fallbackUI component if (error instanceof AggregateError) { - return ; + return } return (

Something went wrong.

{error &&

{error.toString()}

}
- ); + ) } const rollbarConfig = { accessToken: 'POST_CLIENT_ITEM_ACCESS_TOKEN', environment: 'production', -}; +} -const rollbar = new Client('POST_CLIENT_ITEM_ACCESS_TOKEN'); +const rollbar_client = new Client('POST_CLIENT_ITEM_ACCESS_TOKEN') -const ROUTE_PARAMS_RE = /\/\d+/g; +const ROUTE_PARAMS_RE = /\/\d+/g -const historyListener = historyContext(rollbar, { +const historyListener = historyContext(rollbar_client, { // optional: default uses location.pathname - formatter: (location, action) => location.pathname.replace(ROUTE_PARAMS_RE, ''), + formatter: (location, action) => + location.pathname.replace(ROUTE_PARAMS_RE, ''), // optional: true return sets Rollbar context filter: (location, action) => !location.pathname.includes('admin'), -}); -const unlisten = history.listen(historyListener); +}) +const unlisten = history.listen(historyListener) +const rollbar = new Rollbar(rollbarConfig) -const rollbar = new Rollbar(rollbarConfig); - -function level(error, info) { // <-- same signature as componentDidCatch(error, info) - return error instanceof TypeError ? 'error' : 'warn'; +function level(error, info) { + // <-- same signature as componentDidCatch(error, info) + return error instanceof TypeError ? 'error' : 'warn' } function errorMessage(error, { componentStack }) { - const sourceComponents = componentStack.split('\n').map(c => c.trim().replace('in ', '')) - return `component ${sourceComponents[0]} had an error`; + const sourceComponents = componentStack + .split('\n') + .map((c) => c.trim().replace('in ', '')) + return `component ${sourceComponents[0]} had an error` } function extraData(error, info) { - return info.componentStack.includes('Experiment') ? { experiment: true } : null; + return info.componentStack.includes('Experiment') + ? { experiment: true } + : null } const stack = `\n in Card\n in App\n in ErrorBoundary\n in ErrorProvider` @@ -74,68 +93,65 @@ const Routes = () => ( function Contacts(props) { return ( - - … - + ) } +/* eslint-disable react-hooks/exhaustive-deps, no-undef */ + function Home() { - const [currentUser, setCurrentUser] = useState(); + const [currentUser, setCurrentUser] = useState() + + useRollbarPerson(currentUser) useEffect(async () => { - const user = await Auth.getCurrentUser(); - if (user) { - useRollbarPerson(user); - } - setCurrentUser(user); - }); + const user = await Auth.getCurrentUser() + setCurrentUser(user) + }) if (currentUser != null) { - return ; + return } - return ; + return } function ContactDetails({ contactId }) { - const rollbar = useRollbar(); // <-- must have parent Provider - const [contact, setContact] = useState(); + const rollbar = useRollbar() // <-- must have parent Provider + const [contact, setContact] = useState() useEffect(async () => { try { - const { data } = await getContact(contactId); - setContact(data.contact); + const { data } = await getContact(contactId) + setContact(data.contact) } catch (error) { - rollbar.error('Error fetching contact', error, { contactId }); + rollbar.error('Error fetching contact', error, { contactId }) } - }, [contactId]); + }, [contactId]) - useRollbarCaptureEvent(contact, LEVEL_INFO); // <-- only fires when contact changes + useRollbarCaptureEvent(contact, LEVEL_INFO) // <-- only fires when contact changes - return ( -
- … -
- ) + return
} +/* eslint-enable react-hooks/exhaustive-deps, no-undef */ + function App(props) { return ( - - - +
-

I'm Here

+

I'm Here

- `${error.message}`} extraData={{ person: {}, form: { name: 'myform' } }}> -
- -
+ `${error.message}`} + extraData={{ person: {}, form: { name: 'myform' } }} + > +
@@ -146,33 +162,27 @@ function App(props) { } function HomePage(props) { - useContext('home#index'); + useContext('home#index') - return ( -
- … -
- ); + return
} function UsersPage(props) { - useContext('users#list'); + useContext('users#list') - return ( - - ); + return } function MyInput(props) { - const rollbar = useRollbar(); + const rollbar = useRollbar() useEffect(() => { try { - + throw new Error('Some error while fetching data') } catch (err) { - rollbar.error('error fetching data', err); + rollbar.error('error fetching data', err) } - }); + }) } /** diff --git a/examples/nextjs/.eslintrc.json b/examples/nextjs/.eslintrc.json deleted file mode 100644 index bffb357..0000000 --- a/examples/nextjs/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/examples/nextjs/babel.config.js b/examples/nextjs/babel.config.js new file mode 100644 index 0000000..4ba52ba --- /dev/null +++ b/examples/nextjs/babel.config.js @@ -0,0 +1 @@ +module.exports = {} diff --git a/examples/nextjs/eslint.config.js b/examples/nextjs/eslint.config.js new file mode 100644 index 0000000..632c2b9 --- /dev/null +++ b/examples/nextjs/eslint.config.js @@ -0,0 +1,6 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + root: true, + extends: 'next/core-web-vitals', + ignorePatterns: ['node_modules'], +} diff --git a/examples/nextjs/package-lock.json b/examples/nextjs/package-lock.json index a8ed179..3813b69 100644 --- a/examples/nextjs/package-lock.json +++ b/examples/nextjs/package-lock.json @@ -15,6 +15,7 @@ "rollbar": "^2.24.0" }, "devDependencies": { + "@types/eslint": "^8.56.3", "@types/node": "17.0.21", "@types/react": "17.0.39", "eslint": "8.10.0", @@ -409,6 +410,28 @@ "integrity": "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==", "dev": true }, + "node_modules/@types/eslint": { + "version": "8.56.3", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.3.tgz", + "integrity": "sha512-PvSf1wfv2wJpVIFUMSb+i4PvqNYkB9Rkp9ZDO3oaWzq4SKhsQk4mrMBr3ZH06I0hKrVGLBacmgl8JM4WVjb9dg==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -3259,6 +3282,28 @@ "integrity": "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==", "dev": true }, + "@types/eslint": { + "version": "8.56.3", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.3.tgz", + "integrity": "sha512-PvSf1wfv2wJpVIFUMSb+i4PvqNYkB9Rkp9ZDO3oaWzq4SKhsQk4mrMBr3ZH06I0hKrVGLBacmgl8JM4WVjb9dg==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index 51da7a3..8ce69d1 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -16,6 +16,7 @@ "rollbar": "^2.24.0" }, "devDependencies": { + "@types/eslint": "^8.56.3", "@types/node": "17.0.21", "@types/react": "17.0.39", "eslint": "8.10.0", diff --git a/examples/nextjs/pages/_app.tsx b/examples/nextjs/pages/_app.tsx index af80a6c..d04c726 100644 --- a/examples/nextjs/pages/_app.tsx +++ b/examples/nextjs/pages/_app.tsx @@ -1,13 +1,11 @@ +import React from 'react' import '../styles/globals.css' import type { AppProps } from 'next/app' -import { Provider, ErrorBoundary } from '@rollbar/react'; +import { Provider, ErrorBoundary } from '@rollbar/react' const rollbarConfig = { accessToken: process.env.NEXT_PUBLIC_ROLLBAR_TOKEN, - hostSafeList: [ - 'localhost:3000', - 'localhost:4000', - ], + hostSafeList: ['localhost:3000', 'localhost:4000'], captureUncaught: true, captureUnhandledRejections: true, payload: { @@ -16,9 +14,9 @@ const rollbarConfig = { javascript: { code_version: '1.0.0', source_map_enabled: true, - } - } - } + }, + }, + }, } function MyApp({ Component, pageProps }: AppProps) { @@ -27,14 +25,16 @@ function MyApp({ Component, pageProps }: AppProps) {

Oops, there was an error.

} + fallbackUI={() => ( +

Oops, there was an error.

+ )} extra={{ more: 'data' }} callback={() => console.log('an exception was sent to rollbar')} >
- ); + ) } export default MyApp diff --git a/examples/nextjs/pages/index.tsx b/examples/nextjs/pages/index.tsx index 3e0b246..2335f31 100644 --- a/examples/nextjs/pages/index.tsx +++ b/examples/nextjs/pages/index.tsx @@ -1,22 +1,23 @@ +import React from 'react' import type { NextPage } from 'next' import Head from 'next/head' import Image from 'next/image' -import { useCallback, useState } from 'react'; +import { useCallback, useState } from 'react' import styles from '../styles/Home.module.css' -import { useRollbar, RollbarContext } from '@rollbar/react'; +import { useRollbar, RollbarContext } from '@rollbar/react' const Home: NextPage = () => { - const rollbar = useRollbar(); + const rollbar = useRollbar() const sendMessage = useCallback(() => { - rollbar.info('manual message'); + rollbar.info('manual message') }, [rollbar]) - const [errorState, setErrorState] = useState(false); + const [errorState, setErrorState] = useState(false) if (errorState) { - throw new Error('uncaught error (nextjs)'); + throw new Error('uncaught error (nextjs)') } return ( @@ -32,12 +33,11 @@ const Home: NextPage = () => {

Welcome to Next.js!

- +

Get started by sending a message{' '} - {' '} - or throwing an uncaught exception{' '} - + or throwing an uncaught + exception

@@ -49,7 +49,12 @@ const Home: NextPage = () => { > Powered by{' '} - Vercel Logo + Vercel Logo diff --git a/examples/nextjs/public/vercel.svg b/examples/nextjs/public/vercel.svg index fbf0e25..7daff19 100644 --- a/examples/nextjs/public/vercel.svg +++ b/examples/nextjs/public/vercel.svg @@ -1,4 +1,4 @@ - - \ No newline at end of file + diff --git a/examples/typescript/.eslintrc b/examples/typescript/.eslintrc index 36f9ade..04709d9 100644 --- a/examples/typescript/.eslintrc +++ b/examples/typescript/.eslintrc @@ -5,11 +5,7 @@ }, "root": true, "parser": "@typescript-eslint/parser", - "plugins": [ - "react", - "react-hooks", - "@typescript-eslint" - ], + "plugins": ["react", "react-hooks", "@typescript-eslint"], "extends": [ "eslint:recommended", "plugin:react/recommended", @@ -17,7 +13,7 @@ "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended-requiring-type-checking", - "prettier", + "prettier" ], "parserOptions": { "tsconfigRootDir": ".", diff --git a/examples/typescript/README.md b/examples/typescript/README.md index f1d0e72..866a625 100644 --- a/examples/typescript/README.md +++ b/examples/typescript/README.md @@ -3,12 +3,13 @@ This project demonstrates @rollbar/react in a basic Typescript app. ## Rollbar usage in this example -* Provider component -* ErrorBoundary -* Usage from class components -* Hooks - * useRollbar - * useRollbarContext + +- Provider component +- ErrorBoundary +- Usage from class components +- Hooks + - useRollbar + - useRollbarContext ## Rollbar configuration @@ -16,6 +17,7 @@ To send live reports to Rollbar, replace `POST_CLIENT_ITEM_TOKEN` in App.tsx with your valid client token before building your webpack bundle. ## Documentation + For complete documentation see https://docs.rollbar.com/docs/react and https://docs.rollbar.com/docs/react-ts. diff --git a/examples/typescript/package.json b/examples/typescript/package.json index b561232..f51748b 100644 --- a/examples/typescript/package.json +++ b/examples/typescript/package.json @@ -27,6 +27,7 @@ "lint": "eslint src" }, "eslintConfig": { + "root": true, "extends": [ "react-app", "react-app/jest" diff --git a/examples/typescript/src/App.tsx b/examples/typescript/src/App.tsx index bd2a457..0def860 100644 --- a/examples/typescript/src/App.tsx +++ b/examples/typescript/src/App.tsx @@ -1,11 +1,11 @@ -import React, { ReactElement } from 'react'; -import './App.css'; -import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'; -import Rollbar from 'rollbar'; -import { Provider, ErrorBoundary } from '@rollbar/react'; -import ExampleErrors from './ExampleErrors'; -import ExampleClass from './ExampleClass'; -import { FallbackUI } from './FallbackUI'; +import React, { ReactElement } from 'react' +import './App.css' +import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom' +import Rollbar from 'rollbar' +import { Provider, ErrorBoundary } from '@rollbar/react' +import ExampleErrors from './ExampleErrors' +import ExampleClass from './ExampleClass' +import { FallbackUI } from './FallbackUI' function App(): ReactElement { const rollbarConfig: Rollbar.Configuration = { @@ -18,10 +18,10 @@ function App(): ReactElement { javascript: { code_version: '1.0.0', source_map_enabled: true, - } - } - } - }; + }, + }, + }, + } // To provide your own Rollbar.js instance, pass `instance` to Provider // instead of `config`. One might do this if loading from a non-npm source. @@ -29,12 +29,10 @@ function App(): ReactElement { // return ( - +
-

- Rollbar React Example -

+

Rollbar React Example

@@ -49,15 +47,15 @@ function App(): ReactElement { - } /> - } /> + } /> + } />
- ); + ) } -export default App; +export default App diff --git a/examples/typescript/src/ExampleClass.tsx b/examples/typescript/src/ExampleClass.tsx index 4323d9b..3faf224 100644 --- a/examples/typescript/src/ExampleClass.tsx +++ b/examples/typescript/src/ExampleClass.tsx @@ -1,6 +1,6 @@ -import React, { ReactNode } from 'react'; -import Rollbar from 'rollbar'; -import { Context, getRollbarFromContext } from '@rollbar/react'; +import React, { ReactNode } from 'react' +import Rollbar from 'rollbar' +import { Context, getRollbarFromContext } from '@rollbar/react' // Example of using the Rollbar object from a class component. @@ -8,13 +8,13 @@ export default class ExampleClass extends React.Component { // Context here is a React Context and contextType is a special value used by React. // This technique can only be used when consuming one provider. // For multiple providers, see https://docs.rollbar.com/docs/react - static contextType = Context; - rollbar: Rollbar | undefined; + static contextType = Context + rollbar: Rollbar | undefined componentDidMount(): void { - this.rollbar = getRollbarFromContext(this.context); + this.rollbar = getRollbarFromContext(this.context) - this.rollbar.info('message from class component'); + this.rollbar.info('message from class component') } render(): ReactNode { diff --git a/examples/typescript/src/ExampleErrors.tsx b/examples/typescript/src/ExampleErrors.tsx index 92f8acd..84fb0a5 100644 --- a/examples/typescript/src/ExampleErrors.tsx +++ b/examples/typescript/src/ExampleErrors.tsx @@ -1,55 +1,61 @@ -import React, { ReactElement } from 'react'; -import PropTypes from 'prop-types'; -import Rollbar from 'rollbar'; -import { useState } from 'react'; -import { useRollbar, useRollbarContext } from '@rollbar/react'; +import React, { ReactElement } from 'react' +import PropTypes from 'prop-types' +import Rollbar from 'rollbar' +import { useState } from 'react' +import { useRollbar, useRollbarContext } from '@rollbar/react' interface Props { name: string } function ExampleErrors(props: Props): ReactElement { - const rollbar: Rollbar = useRollbar(); - useRollbarContext(`/${props.name}`); + const rollbar: Rollbar = useRollbar() + useRollbarContext(`/${props.name}`) const sendRollbarMessage = () => { - rollbar.info(`test react-app message: ${props.name}`); + rollbar.info(`test react-app message: ${props.name}`) } const sendRollbarError = () => { try { - throw new Error('caught test error'); + throw new Error('caught test error') } catch (e) { - rollbar.error(e as Rollbar.LogArgument); + rollbar.error(e as Rollbar.LogArgument) } } - const [errorState, setErrorState] = useState({ error: false }); + const [errorState, setErrorState] = useState({ error: false }) const updateErrorState = () => { // Use an error state and throw inside render, // because React won't send errors within event handlers // to the error boundary component. - setErrorState({ error: true }); + setErrorState({ error: true }) } if (errorState.error) { // This uncaught error will be handled by the ErrorBoundary. - throw new Error('uncaught test error'); + throw new Error('uncaught test error') } return ( <>

Rollbar Example for React Child Component: {props.name}

- - - + + + ) } ExampleErrors.propTypes = { - name: PropTypes.string + name: PropTypes.string, } -export default ExampleErrors; +export default ExampleErrors diff --git a/examples/typescript/src/FallbackUI.tsx b/examples/typescript/src/FallbackUI.tsx index aede402..77c7e2c 100644 --- a/examples/typescript/src/FallbackUI.tsx +++ b/examples/typescript/src/FallbackUI.tsx @@ -1,10 +1,10 @@ -import React, { ReactElement } from 'react'; +import React, { ReactElement } from 'react' export interface FallbackProps { error?: Error } -export const FallbackUI = ({ error }:FallbackProps): ReactElement => { - const message = error?.message || 'unknown error'; +export const FallbackUI = ({ error }: FallbackProps): ReactElement => { + const message = error?.message || 'unknown error' return
Oops, there was an error: {message}
} diff --git a/examples/typescript/src/index.tsx b/examples/typescript/src/index.tsx index 6832e78..606a3cf 100644 --- a/examples/typescript/src/index.tsx +++ b/examples/typescript/src/index.tsx @@ -1,11 +1,11 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import './index.css'; -import App from './App'; +import React from 'react' +import ReactDOM from 'react-dom' +import './index.css' +import App from './App' ReactDOM.render( , document.getElementById('root') -); +) diff --git a/examples/typescript/src/setupTests.ts b/examples/typescript/src/setupTests.ts index 8f2609b..52aaef1 100644 --- a/examples/typescript/src/setupTests.ts +++ b/examples/typescript/src/setupTests.ts @@ -2,4 +2,4 @@ // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; +import '@testing-library/jest-dom' diff --git a/examples/typescript/tsconfig.json b/examples/typescript/tsconfig.json index a273b0c..9d379a3 100644 --- a/examples/typescript/tsconfig.json +++ b/examples/typescript/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -20,7 +16,5 @@ "noEmit": true, "jsx": "react-jsx" }, - "include": [ - "src" - ] + "include": ["src"] } diff --git a/index.d.ts b/index.d.ts index 692e721..0016f77 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,90 +1,96 @@ -import { Component, Context as ReactContext, ErrorInfo, ReactNode, ComponentType } from 'react'; -import Rollbar, { Callback, Configuration } from 'rollbar'; +import { + Component, + Context as ReactContext, + ErrorInfo, + ReactNode, + ComponentType, +} from 'react' +import Rollbar, { Callback, Configuration } from 'rollbar' -export const LEVEL_DEBUG = 'debug'; -export const LEVEL_INFO = 'info'; -export const LEVEL_WARN = 'warn'; -export const LEVEL_ERROR = 'error'; -export const LEVEL_CRITICAL = 'critical'; +export const LEVEL_DEBUG = 'debug' +export const LEVEL_INFO = 'info' +export const LEVEL_WARN = 'warn' +export const LEVEL_ERROR = 'error' +export const LEVEL_CRITICAL = 'critical' export type LEVEL = | typeof LEVEL_DEBUG | typeof LEVEL_INFO | typeof LEVEL_WARN | typeof LEVEL_ERROR - | typeof LEVEL_CRITICAL; + | typeof LEVEL_CRITICAL -type Extra = Record; +type Extra = Record export interface ErrorBoundaryProps { - children: ReactNode; - fallbackUI?: ComponentType<{ error: Error | null, resetError: () => void }>; - errorMessage?: string | (() => string); - extra?: - | Extra - | ((error: Error, errorInfo: ErrorInfo) => Extra); - level?: LEVEL | (() => LEVEL); - callback?: Callback; + children: ReactNode + fallbackUI?: ComponentType<{ error: Error | null; resetError: () => void }> + errorMessage?: string | (() => string) + extra?: Extra | ((error: Error, errorInfo: ErrorInfo) => Extra) + level?: LEVEL | (() => LEVEL) + callback?: Callback } interface ErrorBoundaryState { - hasError: boolean; - error: Error | null; + hasError: boolean + error: Error | null } export class ErrorBoundary extends Component< ErrorBoundaryProps, ErrorBoundaryState > { - resetError: () => void; + resetError: () => void } export class RollbarContext extends Component<{ - children: ReactNode; - context?: string; + children: ReactNode + context?: string }> {} export interface ProviderProps { - Rollbar?: new (options: Configuration) => Rollbar; - children: ReactNode; - config?: Configuration | (() => Configuration); - instance?: Rollbar; + Rollbar?: new (options: Configuration) => Rollbar + children: ReactNode + config?: Configuration | (() => Configuration) + instance?: Rollbar } interface ProviderState { - rollbar: Rollbar; - options: Configuration; + rollbar: Rollbar + options: Configuration } export class Provider extends Component {} -declare const RollbarInstance: unique symbol; -declare const BaseOptions: unique symbol; -declare const RollbarCtor: unique symbol; +declare const RollbarInstance: unique symbol +declare const BaseOptions: unique symbol +declare const RollbarCtor: unique symbol interface ContextInterface { - [RollbarInstance]: Rollbar; - [BaseOptions]: Configuration; - [RollbarCtor]: new (options: Configuration) => Rollbar; + [RollbarInstance]: Rollbar + [BaseOptions]: Configuration + [RollbarCtor]: new (options: Configuration) => Rollbar } -export const Context: ReactContext; +export const Context: ReactContext -export function getRollbarFromContext(context: ReactContext): Rollbar; -export function useRollbar(): Rollbar; -export function useRollbarConfiguration(config: Rollbar.Configuration): void; -export function useRollbarContext(ctx?: string, isLayout?: boolean): void; -export function useRollbarPerson(person: object): void; -export function useRollbarCaptureEvent(metadata: object, level?: LEVEL): void; -export function isValidLevel(level: LEVEL): boolean; +export function getRollbarFromContext( + context: ReactContext +): Rollbar +export function useRollbar(): Rollbar +export function useRollbarConfiguration(config: Rollbar.Configuration): void +export function useRollbarContext(ctx?: string, isLayout?: boolean): void +export function useRollbarPerson(person: object): void +export function useRollbarCaptureEvent(metadata: object, level?: LEVEL): void +export function isValidLevel(level: LEVEL): boolean export function historyContext( rollbar: Rollbar, args: { - formatter: (location: string, action: string) => string; - filter: (location: string, action: string) => boolean; + formatter: (location: string, action: string) => string + filter: (location: string, action: string) => boolean } ): ( v4Location: { - action: string; - filter: (location: string, action: string) => boolean; + action: string + filter: (location: string, action: string) => boolean }, v4action: string -) => void; +) => void diff --git a/jest.config.js b/jest.config.js index 848a7da..7d04564 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,16 +1,12 @@ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ module.exports = { - roots: [ - 'src/tests' - ], - setupFilesAfterEnv: [ - './src/tests/jest-setup.ts' - ], + roots: ['src/tests'], + setupFilesAfterEnv: ['./src/tests/jest-setup.ts'], preset: 'ts-jest/presets/js-with-babel', testEnvironment: 'jsdom', globals: { 'ts-jest': { - tsconfig: './tsconfig.json' - } - } -}; + tsconfig: './tsconfig.json', + }, + }, +} diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..73ceb66 --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,9 @@ +/** @type {import('prettier').Config} */ +module.exports = { + trailingComma: 'es5', + bracketSpacing: true, + tabWidth: 2, + semi: false, + singleQuote: true, + arrowParens: 'always', +} diff --git a/rollup.config.js b/rollup.config.js index 2c39bf8..45853e0 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,8 +1,8 @@ -import commonjs from '@rollup/plugin-commonjs'; -import resolve from '@rollup/plugin-node-resolve'; -import peerDepsExternal from 'rollup-plugin-peer-deps-external'; -import babel from '@rollup/plugin-babel'; -import pkg from './package.json'; +import commonjs from '@rollup/plugin-commonjs' +import resolve from '@rollup/plugin-node-resolve' +import peerDepsExternal from 'rollup-plugin-peer-deps-external' +import babel from '@rollup/plugin-babel' +import pkg from './package.json' const COMMON_PLUGINS = [ resolve(), @@ -24,12 +24,9 @@ export default [ react: 'React', 'prop-types': 'PropTypes', rollbar: 'Rollbar', - } + }, }, - plugins: [ - ...COMMON_PLUGINS, - commonjs(), - ], + plugins: [...COMMON_PLUGINS, commonjs()], }, { @@ -61,9 +58,6 @@ export default [ exports: 'named', }, ], - plugins: [ - ...COMMON_PLUGINS, - ], + plugins: [...COMMON_PLUGINS], }, -]; - +] diff --git a/src/constants.js b/src/constants.js index 710d443..8eddde6 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,8 +1,8 @@ -export const LEVEL_DEBUG = 'debug'; -export const LEVEL_INFO = 'info'; -export const LEVEL_WARN = 'warn'; -export const LEVEL_ERROR = 'error'; -export const LEVEL_CRITICAL = 'critical'; +export const LEVEL_DEBUG = 'debug' +export const LEVEL_INFO = 'info' +export const LEVEL_WARN = 'warn' +export const LEVEL_ERROR = 'error' +export const LEVEL_CRITICAL = 'critical' export default { [LEVEL_DEBUG]: 1, diff --git a/src/error-boundary.js b/src/error-boundary.js index 4527afe..475045b 100644 --- a/src/error-boundary.js +++ b/src/error-boundary.js @@ -1,14 +1,14 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import invariant from 'tiny-invariant'; -import { LEVEL_ERROR } from './constants'; -import { Context, getRollbarFromContext } from './provider'; -import * as utils from './utils'; +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import invariant from 'tiny-invariant' +import { LEVEL_ERROR } from './constants' +import { Context, getRollbarFromContext } from './provider' +import * as utils from './utils' -const INITIAL_ERROR_STATE = { hasError: false, error: null }; +const INITIAL_ERROR_STATE = { hasError: false, error: null } export class ErrorBoundary extends Component { - static contextType = Context; + static contextType = Context static propTypes = { fallbackUI: PropTypes.elementType, @@ -24,45 +24,48 @@ export class ErrorBoundary extends Component { } constructor(props) { - super(props); - invariant(utils.isValidLevel(props.level), `${props.level} is not a valid level setting for Rollbar`); - this.state = { ...INITIAL_ERROR_STATE }; + super(props) + invariant( + utils.isValidLevel(props.level), + `${props.level} is not a valid level setting for Rollbar` + ) + this.state = { ...INITIAL_ERROR_STATE } } static getDerivedStateFromError(error) { - return { hasError: true, error }; + return { hasError: true, error } } componentDidCatch(error, info) { - const { errorMessage, extra, level: targetLevel, callback } = this.props; - const custom = utils.value(extra, {}, error, info); - const data = { ...info, ...custom }; - const level = utils.value(targetLevel, LEVEL_ERROR, error, info); - const rollbar = getRollbarFromContext(this.context); + const { errorMessage, extra, level: targetLevel, callback } = this.props + const custom = utils.value(extra, {}, error, info) + const data = { ...info, ...custom } + const level = utils.value(targetLevel, LEVEL_ERROR, error, info) + const rollbar = getRollbarFromContext(this.context) if (!errorMessage) { - rollbar[level](error, data, callback); + rollbar[level](error, data, callback) } else { - let logMessage = utils.value(errorMessage, '', error, info); - rollbar[level](logMessage, error, data, callback); + let logMessage = utils.value(errorMessage, '', error, info) + rollbar[level](logMessage, error, data, callback) } } resetError = () => { - this.setState(INITIAL_ERROR_STATE); + this.setState(INITIAL_ERROR_STATE) } render() { - const { hasError, error } = this.state; - const { fallbackUI: FallbackUI, children } = this.props; + const { hasError, error } = this.state + const { fallbackUI: FallbackUI, children } = this.props if (!hasError) { - return children; + return children } if (!FallbackUI) { - return null; + return null } - return ; + return } } diff --git a/src/history-context.js b/src/history-context.js index 5d330da..2aaa7bf 100644 --- a/src/history-context.js +++ b/src/history-context.js @@ -1,24 +1,36 @@ -import Rollbar from 'rollbar'; -import invariant from 'tiny-invariant'; +import Rollbar from 'rollbar' +import invariant from 'tiny-invariant' export function historyContext(rollbar, { formatter, filter } = {}) { - invariant(rollbar instanceof Rollbar, 'historyContext must have an instance of Rollbar'); - invariant(formatter == null || typeof formatter === 'function', `formatter option must be a function, received ${typeof formatter} instead`); - invariant(filter == null || typeof filter === 'function', `filter option must be a function, received ${typeof filter} instead`); + invariant( + rollbar instanceof Rollbar, + 'historyContext must have an instance of Rollbar' + ) + invariant( + formatter == null || typeof formatter === 'function', + `formatter option must be a function, received ${typeof formatter} instead` + ) + invariant( + filter == null || typeof filter === 'function', + `filter option must be a function, received ${typeof filter} instead` + ) // v4 of history.listen callback signature is (location, action) // v5 of history.listen callback signature is ({ location, action }) // this implementation translates it to work for both return (v4location, v4action) => { - let { action, location } = v4location; + let { action, location } = v4location if (v4action) { - action = v4action; - location = v4location; + action = v4action + location = v4location } if (filter && !filter(location, action)) { - return; + return } - const context = formatter ? formatter(location, action) : location.pathname; - invariant(typeof context === 'string', 'formatter must return a string value to set the context'); - rollbar.configure({ payload: { context }}); + const context = formatter ? formatter(location, action) : location.pathname + invariant( + typeof context === 'string', + 'formatter must return a string value to set the context' + ) + rollbar.configure({ payload: { context } }) } } diff --git a/src/hooks/index.js b/src/hooks/index.js index 26a27de..72940ed 100644 --- a/src/hooks/index.js +++ b/src/hooks/index.js @@ -1,5 +1,5 @@ -export { useRollbar } from './use-rollbar'; -export { useRollbarConfiguration } from './use-rollbar-config'; -export { useRollbarContext } from './use-rollbar-context'; -export { useRollbarPerson } from './use-rollbar-person'; -export { useRollbarCaptureEvent } from './use-rollbar-capture-event'; +export { useRollbar } from './use-rollbar' +export { useRollbarConfiguration } from './use-rollbar-config' +export { useRollbarContext } from './use-rollbar-context' +export { useRollbarPerson } from './use-rollbar-person' +export { useRollbarCaptureEvent } from './use-rollbar-capture-event' diff --git a/src/hooks/use-rollbar-capture-event.js b/src/hooks/use-rollbar-capture-event.js index 0002d5a..fc6388f 100644 --- a/src/hooks/use-rollbar-capture-event.js +++ b/src/hooks/use-rollbar-capture-event.js @@ -1,13 +1,16 @@ -import { useEffect } from 'react'; -import invariant from 'tiny-invariant'; -import { LEVEL_INFO } from '../constants'; -import { useRollbar } from './use-rollbar'; -import { isValidLevel } from '../utils'; +import { useEffect } from 'react' +import invariant from 'tiny-invariant' +import { LEVEL_INFO } from '../constants' +import { useRollbar } from './use-rollbar' +import { isValidLevel } from '../utils' export function useRollbarCaptureEvent(metadata, level = LEVEL_INFO) { - invariant(isValidLevel(level), `${level} is not a valid level setting for Rollbar`); - const rollbar = useRollbar(); + invariant( + isValidLevel(level), + `${level} is not a valid level setting for Rollbar` + ) + const rollbar = useRollbar() useEffect(() => { - rollbar.captureEvent(metadata, level); - }, [metadata, level, rollbar]); + rollbar.captureEvent(metadata, level) + }, [metadata, level, rollbar]) } diff --git a/src/hooks/use-rollbar-config.js b/src/hooks/use-rollbar-config.js index 2b6d480..9c10213 100644 --- a/src/hooks/use-rollbar-config.js +++ b/src/hooks/use-rollbar-config.js @@ -1,6 +1,6 @@ -import { useRollbar } from './use-rollbar'; +import { useRollbar } from './use-rollbar' export function useRollbarConfiguration(config) { - const rollbar = useRollbar(); - rollbar.configure(config); + const rollbar = useRollbar() + rollbar.configure(config) } diff --git a/src/hooks/use-rollbar-context.js b/src/hooks/use-rollbar-context.js index 0a8bf90..9eeeb5b 100644 --- a/src/hooks/use-rollbar-context.js +++ b/src/hooks/use-rollbar-context.js @@ -1,6 +1,6 @@ -import invariant from 'tiny-invariant'; -import { useEffect, useLayoutEffect } from 'react'; -import { useRollbar } from './use-rollbar'; +import invariant from 'tiny-invariant' +import { useEffect, useLayoutEffect } from 'react' +import { useRollbar } from './use-rollbar' // Simple version does its job // export function useRollbarContext(context) { @@ -9,13 +9,13 @@ import { useRollbar } from './use-rollbar'; // Complex version will set the context when part of the tree and reset back to original context when removed export function useRollbarContext(ctx = '', isLayout = false) { - invariant(typeof ctx === 'string', '`ctx` must be a string'); - const rollbar = useRollbar(); - (isLayout ? useLayoutEffect : useEffect)(() => { - const origCtx = rollbar.options.payload.context; - rollbar.configure({ payload: { context: ctx }}); + invariant(typeof ctx === 'string', '`ctx` must be a string') + const rollbar = useRollbar() + ;(isLayout ? useLayoutEffect : useEffect)(() => { + const origCtx = rollbar.options.payload.context + rollbar.configure({ payload: { context: ctx } }) return () => { - rollbar.configure({ payload: { context: origCtx }}); - }; - }, [ctx]); + rollbar.configure({ payload: { context: origCtx } }) + } + }, [ctx]) } diff --git a/src/hooks/use-rollbar-logs.js b/src/hooks/use-rollbar-logs.js index bc364f0..ee63e5e 100644 --- a/src/hooks/use-rollbar-logs.js +++ b/src/hooks/use-rollbar-logs.js @@ -2,42 +2,51 @@ // NOT EXPORTED AS PART OF PUBLIC API YET // NO TEST COVERAGE -import { useEffect, useLayoutEffect } from 'react'; -import invariant from 'tiny-invariant'; -import { LEVEL_DEBUG, LEVEL_INFO, LEVEL_WARN, LEVEL_ERROR, LEVEL_CRITICAL } from '../constants'; -import { useRollbar } from './use-rollbar'; -import { isValidLevel } from '../utils'; - -const LOG = 'log'; +import { useEffect, useLayoutEffect } from 'react' +import invariant from 'tiny-invariant' +import { + LEVEL_DEBUG, + LEVEL_INFO, + LEVEL_WARN, + LEVEL_ERROR, + LEVEL_CRITICAL, +} from '../constants' +import { useRollbar } from './use-rollbar' +import { isValidLevel } from '../utils' + +const LOG = 'log' function useRollbarNotify(type, isLayout, ...args) { - invariant(type === LOG || isValidLevel(type), `cannot notify rollbar using method '${type}'`); - const rollbar = useRollbar(); - (isLayout ? useLayoutEffect : useEffect)(() => { - rollbar[type](...args); + invariant( + type === LOG || isValidLevel(type), + `cannot notify rollbar using method '${type}'` + ) + const rollbar = useRollbar() + ;(isLayout ? useLayoutEffect : useEffect)(() => { + rollbar[type](...args) }, args) } export function useRollbarLog(isLayout, ...args) { - useRollbarNotify(LOG, isLayout, ...args); + useRollbarNotify(LOG, isLayout, ...args) } export function useRollbarDebug(isLayout, ...args) { - useRollbarNotify(LEVEL_DEBUG, isLayout, ...args); + useRollbarNotify(LEVEL_DEBUG, isLayout, ...args) } export function useRollbarInfo(isLayout, ...args) { - useRollbarNotify(LEVEL_INFO, isLayout, ...args); + useRollbarNotify(LEVEL_INFO, isLayout, ...args) } export function useRollbarWarn(isLayout, ...args) { - useRollbarNotify(LEVEL_WARN, isLayout, ...args); + useRollbarNotify(LEVEL_WARN, isLayout, ...args) } export function useRollbarError(isLayout, ...args) { - useRollbarNotify(LEVEL_ERROR, isLayout, ...args); + useRollbarNotify(LEVEL_ERROR, isLayout, ...args) } export function useRollbarCritical(isLayout, ...args) { - useRollbarNotify(LEVEL_CRITICAL, isLayout, ...args); + useRollbarNotify(LEVEL_CRITICAL, isLayout, ...args) } diff --git a/src/hooks/use-rollbar-person.js b/src/hooks/use-rollbar-person.js index f6bd24b..43ae2de 100644 --- a/src/hooks/use-rollbar-person.js +++ b/src/hooks/use-rollbar-person.js @@ -1,5 +1,5 @@ -import { useRollbarConfiguration } from './use-rollbar-config'; +import { useRollbarConfiguration } from './use-rollbar-config' export function useRollbarPerson(person) { - useRollbarConfiguration({ payload: { person }}); + useRollbarConfiguration({ payload: { person } }) } diff --git a/src/hooks/use-rollbar.js b/src/hooks/use-rollbar.js index 666b52f..1055d61 100644 --- a/src/hooks/use-rollbar.js +++ b/src/hooks/use-rollbar.js @@ -1,7 +1,7 @@ -import { useContext } from 'react'; -import { Context, getRollbarFromContext } from '../provider'; +import { useContext } from 'react' +import { Context, getRollbarFromContext } from '../provider' export function useRollbar() { - const context = useContext(Context); - return getRollbarFromContext(context); + const context = useContext(Context) + return getRollbarFromContext(context) } diff --git a/src/hooks/use-scoped-rollbar-config.js b/src/hooks/use-scoped-rollbar-config.js index 80e7f0d..147bea0 100644 --- a/src/hooks/use-scoped-rollbar-config.js +++ b/src/hooks/use-scoped-rollbar-config.js @@ -2,14 +2,18 @@ // NOT EXPORTED AS PART OF PUBLIC API YET // NO TEST COVERAGE -import { useContext } from 'react'; -import { Context, getRollbarFromContext, getRollbarConstructorFromContext } from '../provider'; +import { useContext } from 'react' +import { + Context, + getRollbarFromContext, + getRollbarConstructorFromContext, +} from '../provider' export function useScopedConfiguration(config) { - const ctx = useContext(Context); - const base = getRollbarFromContext(ctx); - const ctor = getRollbarConstructorFromContext(ctx); - const rollbar = new ctor(base.options); - rollbar.configure(config); - return rollbar; + const ctx = useContext(Context) + const base = getRollbarFromContext(ctx) + const ctor = getRollbarConstructorFromContext(ctx) + const rollbar = new ctor(base.options) + rollbar.configure(config) + return rollbar } diff --git a/src/hooks/utils.js b/src/hooks/utils.js index 274a7c9..f6079c0 100644 --- a/src/hooks/utils.js +++ b/src/hooks/utils.js @@ -1,8 +1,8 @@ -import { useRef } from 'react'; +import { useRef } from 'react' // EXPERIMENTAL (NOT IN USE): wrap the instance of rollbar to prevent modification export function useWrappedRollbar(rollbar) { - const rb = useRef(rollbar); + const rb = useRef(rollbar) return { log: (...args) => rb.current.log(...args), debug: (...args) => rb.current.debug(...args), diff --git a/src/index.js b/src/index.js index e9c9d00..f254e6e 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,13 @@ -export { LEVEL_DEBUG, LEVEL_INFO, LEVEL_WARN, LEVEL_ERROR, LEVEL_CRITICAL } from './constants'; -export { historyContext } from './history-context'; -export { Provider, Context, getRollbarFromContext } from './provider'; -export { ErrorBoundary } from './error-boundary'; -export { RollbarContext } from './rollbar-context'; -export { isValidLevel } from './utils'; -export * from './hooks'; +export { + LEVEL_DEBUG, + LEVEL_INFO, + LEVEL_WARN, + LEVEL_ERROR, + LEVEL_CRITICAL, +} from './constants' +export { historyContext } from './history-context' +export { Provider, Context, getRollbarFromContext } from './provider' +export { ErrorBoundary } from './error-boundary' +export { RollbarContext } from './rollbar-context' +export { isValidLevel } from './utils' +export * from './hooks' diff --git a/src/provider.js b/src/provider.js index 4b99f2c..5bc00f4 100644 --- a/src/provider.js +++ b/src/provider.js @@ -1,24 +1,24 @@ -import React, { Component, createContext } from 'react'; -import PropTypes from 'prop-types'; -import Rollbar from 'rollbar'; -import invariant from 'tiny-invariant'; -import { isRollbarInstance } from './utils'; +import React, { Component, createContext } from 'react' +import PropTypes from 'prop-types' +import Rollbar from 'rollbar' +import invariant from 'tiny-invariant' +import { isRollbarInstance } from './utils' -export const Context = createContext(); -Context.displayName = 'Rollbar'; +export const Context = createContext() +Context.displayName = 'Rollbar' -export const RollbarInstance = Symbol('RollbarInstance'); -export const BaseOptions = Symbol('BaseOptions'); -export const RollbarCtor = Symbol('RollbarCtor'); +export const RollbarInstance = Symbol('RollbarInstance') +export const BaseOptions = Symbol('BaseOptions') +export const RollbarCtor = Symbol('RollbarCtor') export function getRollbarFromContext(context) { - const { [RollbarInstance]: rollbar } = context; - return rollbar; + const { [RollbarInstance]: rollbar } = context + return rollbar } export function getRollbarConstructorFromContext(context) { - const { [RollbarCtor]: ctor } = context; - return ctor; + const { [RollbarCtor]: ctor } = context + return ctor } export class Provider extends Component { @@ -26,51 +26,62 @@ export class Provider extends Component { Rollbar: PropTypes.func, config: (props, propName, componentName) => { if (!props.config && !props.instance) { - return new Error(`One of the required props 'config' or 'instance' must be set for ${componentName}.`) + return new Error( + `One of the required props 'config' or 'instance' must be set for ${componentName}.` + ) } if (props.config) { - const configType = typeof props.config; - if (configType === 'function' || configType === 'object' && !Array.isArray(configType)) { - return; + const configType = typeof props.config + if ( + configType === 'function' || + (configType === 'object' && !Array.isArray(configType)) + ) { + return } - return new Error(`${propName} must be either an Object or a Function`); + return new Error(`${propName} must be either an Object or a Function`) } }, instance: (props, propName, componentName) => { if (!props.config && !props.instance) { - return new Error(`One of the required props 'config' or 'instance' must be set for ${componentName}.`) + return new Error( + `One of the required props 'config' or 'instance' must be set for ${componentName}.` + ) } if (props.instance && !isRollbarInstance(props.instance)) { - return new Error(`${propName} must be a configured instance of Rollbar`); + return new Error(`${propName} must be a configured instance of Rollbar`) } }, - children: PropTypes.node + children: PropTypes.node, } constructor(props) { - super(props); - const { config, Rollbar: ctor = Rollbar, instance } = this.props; + super(props) + const { config, Rollbar: ctor = Rollbar, instance } = this.props invariant( !instance || isRollbarInstance(instance), '`instance` must be a configured instance of Rollbar' - ); - const options = typeof config === 'function' ? config() : config; - const rollbar = instance || new ctor(options); + ) + const options = typeof config === 'function' ? config() : config + const rollbar = instance || new ctor(options) // TODO: use isUncaught to filter if this is 2nd Provider added // unless customer wants that - this.state = { rollbar, options }; + this.state = { rollbar, options } } - - // componentDidUpdate() render() { - const { children, Rollbar: ctor = Rollbar } = this.props; - const { rollbar, options } = this.state; + const { children, Rollbar: ctor = Rollbar } = this.props + const { rollbar, options } = this.state return ( - + {children} ) diff --git a/src/rollbar-configuration.js b/src/rollbar-configuration.js index 07bfa70..736dbb7 100644 --- a/src/rollbar-configuration.js +++ b/src/rollbar-configuration.js @@ -3,9 +3,9 @@ // PURPOSE provide a wrapping around Rollbar configuration for a subtree -import { Component } from 'react'; -import PropTypes from 'prop-types'; -import { Context, getRollbarFromContext } from './provider'; +import { Component } from 'react' +import PropTypes from 'prop-types' +import { Context, getRollbarFromContext } from './provider' export class RollbarConfiguration extends Component { static propTypes = { @@ -13,29 +13,29 @@ export class RollbarConfiguration extends Component { children: PropTypes.node, } - static contextType = Context; + static contextType = Context constructor(props) { - super(props); - this.state = { parentConfig: null }; + super(props) + this.state = { parentConfig: null } } componentDidMount() { - const rollbar = getRollbarFromContext(this.context); - const { options } = this.props; + const rollbar = getRollbarFromContext(this.context) + const { options } = this.props // TODO: need to clone this somehow to prevent downstream changes from manipulating it - const parentConfig = (o => o)(rollbar.options); - this.setState({ parentConfig }); - rollbar.configure(options); + const parentConfig = ((o) => o)(rollbar.options) + this.setState({ parentConfig }) + rollbar.configure(options) } componentWillUnmount() { - const rollbar = getRollbarFromContext(this.context); - const { parentConfig } = this.state; - rollbar.configure(parentConfig); + const rollbar = getRollbarFromContext(this.context) + const { parentConfig } = this.state + rollbar.configure(parentConfig) } render() { - return this.props.children; + return this.props.children } } diff --git a/src/rollbar-context.js b/src/rollbar-context.js index 5ddf66c..536be1b 100644 --- a/src/rollbar-context.js +++ b/src/rollbar-context.js @@ -1,6 +1,6 @@ -import { Component } from 'react'; -import PropTypes from 'prop-types'; -import { Context, getRollbarFromContext } from './provider'; +import { Component } from 'react' +import PropTypes from 'prop-types' +import { Context, getRollbarFromContext } from './provider' export class RollbarContext extends Component { static propTypes = { @@ -13,50 +13,50 @@ export class RollbarContext extends Component { onRender: false, } - static contextType = Context; + static contextType = Context - firstRender = true; + firstRender = true constructor(props) { - super(props); - this.state = { previousContext: null }; + super(props) + this.state = { previousContext: null } } changeContext = (storePrevious = true) => { - const rollbar = getRollbarFromContext(this.context); - const { context } = this.props; + const rollbar = getRollbarFromContext(this.context) + const { context } = this.props if (storePrevious) { - this.setState({ previousContext: rollbar.options.payload.context }); + this.setState({ previousContext: rollbar.options.payload.context }) } - rollbar.configure({ payload: { context }}); + rollbar.configure({ payload: { context } }) } componentDidMount() { - const { onRender } = this.props; + const { onRender } = this.props if (!onRender) { - this.changeContext(true); + this.changeContext(true) } } componentDidUpdate() { - const { onRender } = this.props; + const { onRender } = this.props if (!onRender) { - this.changeContext(false); + this.changeContext(false) } } componentWillUnmount() { - const rollbar = getRollbarFromContext(this.context); - const { previousContext } = this.state; - rollbar.configure({ payload: { context: previousContext }}); + const rollbar = getRollbarFromContext(this.context) + const { previousContext } = this.state + rollbar.configure({ payload: { context: previousContext } }) } render() { - const { onRender } = this.props; + const { onRender } = this.props if (onRender && this.firstRender) { - this.changeContext(true); + this.changeContext(true) } - this.firstRender = false; - return this.props.children; + this.firstRender = false + return this.props.children } } diff --git a/src/tests/components/error-boundary.test.tsx b/src/tests/components/error-boundary.test.tsx index 179effc..9738930 100644 --- a/src/tests/components/error-boundary.test.tsx +++ b/src/tests/components/error-boundary.test.tsx @@ -1,44 +1,39 @@ -import React from 'react'; -import { screen } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Rollbar = require('rollbar'); -import { - ErrorBoundary, ErrorBoundaryProps, useRollbar -} from '../rollbar-react'; -import { renderWithProviderProps } from '../utils/provider-util'; +import React from 'react' +import { screen } from '@testing-library/react' +import '@testing-library/jest-dom' +import Rollbar = require('rollbar') +import { ErrorBoundary, ErrorBoundaryProps, useRollbar } from '../rollbar-react' +import { renderWithProviderProps } from '../utils/provider-util' describe('ErrorBoundary', () => { - const accessToken = 'POST_CLIENT_ITEM_TOKEN'; + const accessToken = 'POST_CLIENT_ITEM_TOKEN' const config: Rollbar.Configuration = { accessToken: accessToken, - captureUncaught: true - }; + captureUncaught: true, + } - let rollbar: Rollbar; - const error: Error = new Error('Test'); + let rollbar: Rollbar + const error: Error = new Error('Test') const ThrowError = () => { - throw error; - }; - const fallbackMessage = 'Test fallback'; - const errorMessage = 'Test error'; - const extra = { foo: 'bar' }; - const level = 'warn'; + throw error + } + const fallbackMessage = 'Test fallback' + const errorMessage = 'Test error' + const extra = { foo: 'bar' } + const level = 'warn' const callback: Rollbar.Callback = (_err, _resp) => 'foo' const Fallback = () =>
{fallbackMessage}
const TestComponent = (props: Omit) => { - rollbar = useRollbar(); - rollbar.warn = jest - .fn() - .mockReturnValue({}) - .mockName('Rollbar.warn'); + rollbar = useRollbar() + rollbar.warn = jest.fn().mockReturnValue({}).mockName('Rollbar.warn') return ( - ); - }; + ) + } it('should display the fallback UI and send rollbar on error', () => { renderWithProviderProps( @@ -50,24 +45,27 @@ describe('ErrorBoundary', () => { callback={callback} />, {}, - {config: config} - ); + { config: config } + ) - expect(screen.getByText(fallbackMessage)).toBeInTheDocument(); + expect(screen.getByText(fallbackMessage)).toBeInTheDocument() expect(rollbar.warn).toHaveBeenLastCalledWith( - errorMessage, error, expect.objectContaining(extra), callback - ); - }); + errorMessage, + error, + expect.objectContaining(extra), + callback + ) + }) describe('with extra prop as a fn', () => { it('should send extra value to rollbar on error', () => { const extraFn: ErrorBoundaryProps['extra'] = (error, errorInfo) => { expect(error).toBeInstanceOf(Error) expect(errorInfo).toHaveProperty('componentStack') - return extra; - }; - + return extra + } + renderWithProviderProps( { callback={callback} />, {}, - {config: config} - ); - + { config: config } + ) + expect(rollbar.warn).toHaveBeenLastCalledWith( - errorMessage, error, expect.objectContaining(extra), callback - ); - }); - }); -}); + errorMessage, + error, + expect.objectContaining(extra), + callback + ) + }) + }) +}) diff --git a/src/tests/components/provider.test.tsx b/src/tests/components/provider.test.tsx index aaff2ce..6be57d9 100644 --- a/src/tests/components/provider.test.tsx +++ b/src/tests/components/provider.test.tsx @@ -1,39 +1,42 @@ -import React, { Context as ReactContext, ReactNode } from 'react'; -import { waitFor, screen } from '@testing-library/react'; -import Rollbar = require('rollbar'); +import React, { Context as ReactContext, ReactNode } from 'react' +import { waitFor, screen } from '@testing-library/react' +import Rollbar = require('rollbar') import { - Context, ContextInterface, getRollbarFromContext, useRollbar -} from '../rollbar-react'; -import { renderWithProviderProps } from '../utils/provider-util'; + Context, + ContextInterface, + getRollbarFromContext, + useRollbar, +} from '../rollbar-react' +import { renderWithProviderProps } from '../utils/provider-util' describe('Provider', () => { - const accessToken = 'POST_CLIENT_ITEM_TOKEN'; + const accessToken = 'POST_CLIENT_ITEM_TOKEN' const config: Rollbar.Configuration = { accessToken: accessToken, - captureUncaught: true - }; + captureUncaught: true, + } - const screenText = 'Hello'; + const screenText = 'Hello' const TestComponent = () => { - const rollbar = useRollbar(); + const rollbar = useRollbar() return (
{screenText}
{rollbar?.options?.accessToken}
- ); + ) } class TestClassConponent extends React.Component { - static contextType = Context; - declare context: ReactContext; - rollbar: Rollbar | undefined; + static contextType = Context + declare context: ReactContext + rollbar: Rollbar | undefined render(): ReactNode { - this.rollbar = getRollbarFromContext(this.context); + this.rollbar = getRollbarFromContext(this.context) return (
@@ -44,53 +47,49 @@ describe('Provider', () => { } } - const instance: Rollbar = new Rollbar(config); + const instance: Rollbar = new Rollbar(config) it('should provide a Rollbar instance, given a config', async () => { - renderWithProviderProps( - , {}, { config: config } - ); + renderWithProviderProps(, {}, { config: config }) await waitFor(() => { - screen.getByText(screenText); - }); + screen.getByText(screenText) + }) - expect(screen.getByText(accessToken)).toBeInTheDocument(); - }); + expect(screen.getByText(accessToken)).toBeInTheDocument() + }) it('should provide a Rollbar instance, given a constructor', async () => { renderWithProviderProps( - , {}, { Rollbar: Rollbar, config: config } - ); + , + {}, + { Rollbar: Rollbar, config: config } + ) await waitFor(() => { - screen.getByText(screenText); - }); + screen.getByText(screenText) + }) - expect(screen.getByText(accessToken)).toBeInTheDocument(); - }); + expect(screen.getByText(accessToken)).toBeInTheDocument() + }) it('should provide a Rollbar instance, given the instance', async () => { - renderWithProviderProps( - , {}, { instance: instance } - ); + renderWithProviderProps(, {}, { instance: instance }) await waitFor(() => { - screen.getByText(screenText); - }); + screen.getByText(screenText) + }) - expect(screen.getByText(accessToken)).toBeInTheDocument(); - }); + expect(screen.getByText(accessToken)).toBeInTheDocument() + }) it('should provide a Rollbar instance to class components', async () => { - renderWithProviderProps( - , {}, { config: config } - ); + renderWithProviderProps(, {}, { config: config }) await waitFor(() => { - screen.getByText(screenText); - }); + screen.getByText(screenText) + }) - expect(screen.getByText(accessToken)).toBeInTheDocument(); - }); -}); + expect(screen.getByText(accessToken)).toBeInTheDocument() + }) +}) diff --git a/src/tests/jest-setup.ts b/src/tests/jest-setup.ts index 7de749a..5e85b99 100644 --- a/src/tests/jest-setup.ts +++ b/src/tests/jest-setup.ts @@ -1,9 +1,9 @@ -import '@testing-library/jest-dom'; -import 'regenerator-runtime'; +import '@testing-library/jest-dom' +import 'regenerator-runtime' // Let propType errors cause test failure. -const originalConsoleError = console.error; +const originalConsoleError = console.error console.error = (message: string) => { - if (/(Failed prop type)/.test(message)) throw new Error(message); - originalConsoleError(message); -}; + if (/(Failed prop type)/.test(message)) throw new Error(message) + originalConsoleError(message) +} diff --git a/src/tests/utils/provider-util.tsx b/src/tests/utils/provider-util.tsx index ff58442..ebf2ffe 100644 --- a/src/tests/utils/provider-util.tsx +++ b/src/tests/utils/provider-util.tsx @@ -1,17 +1,14 @@ -import React, { ReactElement } from 'react'; -import { render, RenderOptions } from '@testing-library/react'; -import { Provider } from '../rollbar-react'; +import React, { ReactElement } from 'react' +import { render, RenderOptions } from '@testing-library/react' +import { Provider } from '../rollbar-react' export const renderWithProviderProps = ( ui: ReactElement, options: RenderOptions, providerProps: any ) => { - return render( - ui, - { - wrapper: props => , - ...options - } - ); + return render(ui, { + wrapper: (props) => , + ...options, + }) } diff --git a/src/utils.js b/src/utils.js index 354359a..f340517 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,23 +1,26 @@ -import * as constants from './constants'; +import * as constants from './constants' -const VALID_LEVELS = constants.default; +const VALID_LEVELS = constants.default export function value(val, defaultTo, ...args) { if (typeof val === 'function') { - return val(...args); + return val(...args) } - return val; + return val } export function wrapValue(val, defaultAs) { - return (defaultTo, ...args) => value(val, defaultAs === undefined ? defaultTo : defaultAs, ...args); + return (defaultTo, ...args) => + value(val, defaultAs === undefined ? defaultTo : defaultAs, ...args) } export function isValidLevel(level) { - return VALID_LEVELS[level] >= VALID_LEVELS[constants.LEVEL_DEBUG] - && VALID_LEVELS[level] <= VALID_LEVELS[constants.LEVEL_CRITICAL]; + return ( + VALID_LEVELS[level] >= VALID_LEVELS[constants.LEVEL_DEBUG] && + VALID_LEVELS[level] <= VALID_LEVELS[constants.LEVEL_CRITICAL] + ) } export function isRollbarInstance(instance) { - return !!instance?.options?.accessToken; + return !!instance?.options?.accessToken }