├── .editorconfig ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── nodejs.yml │ ├── shipjs-manual-prepare.yml │ ├── shipjs-schedule-prepare.yml │ └── shipjs-trigger.yml ├── .gitignore ├── .husky └── commit-msg ├── CHANGELOG.md ├── LICENSE ├── README.md ├── commitlint.config.js ├── example ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.tsx │ ├── BuggyComponent.tsx │ ├── GoodComponent.tsx │ ├── index.css │ ├── index.tsx │ ├── react-app-env.d.ts │ ├── reportWebVitals.ts │ └── setupTests.ts └── tsconfig.json ├── package-lock.json ├── package.json ├── rollup.config.js ├── scripts └── update-versions.sh ├── ship.config.js ├── src ├── DefaultErrorComponent.tsx ├── HoneybadgerErrorBoundary.tsx ├── index.test.tsx └── index.tsx └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## What are the steps to reproduce this issue? 4 | 1. … 5 | 2. … 6 | 3. … 7 | 8 | ## What happens? 9 | … 10 | 11 | ## What were you expecting to happen? 12 | … 13 | 14 | ## Any logs, error output, etc? 15 | … 16 | 17 | ## Any other comments? 18 | … 19 | 20 | ## What versions are you using? 21 | **Operating System:** … 22 | **Package Version:** … 23 | **Browser Version:** … 24 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Status 2 | 3 | **READY/WIP/HOLD** 4 | 5 | ## Description 6 | A few sentences describing the overall goals of the pull request's commits. 7 | 8 | ## Related PRs 9 | List related PRs against other branches: 10 | 11 | branch | PR 12 | ------ | ------ 13 | other_pr_production | [link]() 14 | other_pr_master | [link]() 15 | 16 | ## Todos 17 | - [ ] Tests 18 | - [ ] Documentation 19 | - [ ] Changelog Entry (unreleased) 20 | 21 | ## Steps to Test or Reproduce 22 | Outline the steps to test or reproduce the PR here. 23 | ```bash 24 | > git pull --prune 25 | > git checkout 26 | > yarn test 27 | ``` 28 | 1. 29 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [12.x, 14.x, 16.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - name: npm install, build, and test 21 | run: | 22 | npm i 23 | npm run build --if-present 24 | npm test 25 | env: 26 | CI: true 27 | -------------------------------------------------------------------------------- /.github/workflows/shipjs-manual-prepare.yml: -------------------------------------------------------------------------------- 1 | name: Ship js Manual Prepare 2 | on: 3 | issue_comment: 4 | types: [created] 5 | jobs: 6 | manual_prepare: 7 | if: | 8 | github.event_name == 'issue_comment' && 9 | (github.event.comment.author_association == 'member' || github.event.comment.author_association == 'owner') && 10 | startsWith(github.event.comment.body, '@shipjs prepare') 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | ref: master 17 | - uses: actions/setup-node@v1 18 | with: 19 | node-version: '16.x' 20 | - run: | 21 | if [ -f "yarn.lock" ]; then 22 | yarn install 23 | else 24 | npm ci 25 | fi 26 | - run: | 27 | git config --global user.email "github-actions[bot]@users.noreply.github.com" 28 | git config --global user.name "github-actions[bot]" 29 | - run: npm run release -- --yes --no-browse 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | SLACK_INCOMING_HOOK: ${{ secrets.SLACK_INCOMING_HOOK }} 33 | 34 | create_done_comment: 35 | if: success() 36 | needs: manual_prepare 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/github-script@v3 40 | with: 41 | github-token: ${{ secrets.GITHUB_TOKEN }} 42 | script: | 43 | github.issues.createComment({ 44 | issue_number: context.issue.number, 45 | owner: context.repo.owner, 46 | repo: context.repo.repo, 47 | body: "@${{github.actor}} `shipjs prepare` done" 48 | }) 49 | 50 | create_fail_comment: 51 | if: cancelled() || failure() 52 | needs: manual_prepare 53 | runs-on: ubuntu-latest 54 | steps: 55 | - uses: actions/github-script@v3 56 | with: 57 | github-token: ${{ secrets.GITHUB_TOKEN }} 58 | script: | 59 | github.issues.createComment({ 60 | issue_number: context.issue.number, 61 | owner: context.repo.owner, 62 | repo: context.repo.repo, 63 | body: "@${{github.actor}} `shipjs prepare` fail" 64 | }) 65 | -------------------------------------------------------------------------------- /.github/workflows/shipjs-schedule-prepare.yml: -------------------------------------------------------------------------------- 1 | name: Ship js Schedule Prepare 2 | on: 3 | schedule: 4 | # * is a special character in YAML so you have to quote this string 5 | - cron: "0 8 * * 2" 6 | jobs: 7 | schedule_prepare: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | with: 12 | fetch-depth: 0 13 | ref: master 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: '16.x' 17 | - run: | 18 | if [ -f "yarn.lock" ]; then 19 | yarn install 20 | else 21 | npm ci 22 | fi 23 | - run: | 24 | git config --global user.email "github-actions[bot]@users.noreply.github.com" 25 | git config --global user.name "github-actions[bot]" 26 | - run: npm run release -- --yes --no-browse 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | SLACK_INCOMING_HOOK: ${{ secrets.SLACK_INCOMING_HOOK }} 30 | -------------------------------------------------------------------------------- /.github/workflows/shipjs-trigger.yml: -------------------------------------------------------------------------------- 1 | name: Ship js trigger 2 | on: 3 | pull_request: 4 | types: 5 | - closed 6 | jobs: 7 | build: 8 | name: Release 9 | runs-on: ubuntu-latest 10 | if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'releases/v') 11 | steps: 12 | - uses: actions/checkout@v2 13 | with: 14 | fetch-depth: 0 15 | ref: master 16 | - uses: actions/setup-node@v1 17 | with: 18 | node-version: '16.x' 19 | registry-url: "https://registry.npmjs.org" 20 | - run: | 21 | if [ -f "yarn.lock" ]; then 22 | yarn install 23 | else 24 | npm ci 25 | fi 26 | - run: npx shipjs trigger 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 30 | SLACK_INCOMING_HOOK: ${{ secrets.SLACK_INCOMING_HOOK }} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # See https://help.github.com/ignore-files/ for more about ignoring files. 3 | 4 | # dependencies 5 | node_modules 6 | 7 | # builds 8 | build 9 | dist 10 | .rpt2_cache 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit "$1" 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [2.1.0](https://github.com/honeybadger-io/honeybadger-react/compare/v2.0.1...v2.1.0) (2022-05-25) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * use node 16 and npm ci in shipjs workflows ([#333](https://github.com/honeybadger-io/honeybadger-react/issues/333)) ([b0eef94](https://github.com/honeybadger-io/honeybadger-react/commit/b0eef94574dca7c29782eac301d6666671cf47e0)), closes [#327](https://github.com/honeybadger-io/honeybadger-react/issues/327) 7 | 8 | 9 | ### Features 10 | 11 | * shipjs integration ([2947f08](https://github.com/honeybadger-io/honeybadger-react/commit/2947f084c62fd557852a9eef9b0a1de1e9ec82a9)), closes [#327](https://github.com/honeybadger-io/honeybadger-react/issues/327) 12 | 13 | 14 | 15 | ## [2.0.1] - 2022-03-16 16 | ### Fixed 17 | - Generate inline sources in source maps (fixes a source maps error with create-react-app v5) (#299) 18 | 19 | ## [2.0.0] - 2021-09-24 20 | ### Changed 21 | - Transform to a typescript project 22 | - [Breaking change] Export core `Honeybadger` from `@honeybadger-io/js` to simplify integration. Read more at [PR](https://github.com/honeybadger-io/honeybadger-react/pull/275). 23 | 24 | ## [1.0.2] - 2021-09-14 25 | ### Fixed 26 | - Fix typescript definitions (#272) 27 | 28 | ## [1.0.1] - 2021-02-18 29 | ### Fixed 30 | - Bump peerDependencies version for React 17 31 | 32 | ## [1.0.0] - 2021-01-19 33 | ### Changed 34 | - Migrate honeybadger-js dependency to @honeybadger-io/js 3.0.0 35 | See https://docs.honeybadger.io/lib/javascript/support/upgrading-to-v3.html 36 | 37 | ## [0.0.8] - 2020-09-24 38 | ### Fixed 39 | - Update honeybadger-js to 2.3.0 40 | 41 | ## [0.0.7] - 2020-04-24 42 | ### Fixed 43 | - Update honeybadger-js to 2.2.1 (security release, see 44 | [here](https://github.com/honeybadger-io/honeybadger-js/blob/master/CHANGELOG.md#220---2020-03-16)) 45 | 46 | ## [0.0.6] - 2020-01-08 47 | ### Fixed 48 | - Fix TypeScript type definitions 49 | 50 | ## [0.0.5] - 2019-12-18 51 | ### Fixed 52 | - Deps & security updates 53 | - Proptypes bug (#39) (#40) 54 | 55 | ## [0.0.4] - 2019-07-01 56 | ### Fixed 57 | - Fix PropType mismatch #38 @andrewsouthard 58 | 59 | ## [0.0.3] - 2019-04-16 60 | ### Changed 61 | - Update honeybadger-js to v1.0. See 62 | https://github.com/honeybadger-io/honeybadger-js/blob/master/CHANGELOG.md 63 | 64 | ## [0.0.2] - 2019-01-21 65 | ### Added 66 | - Add TypeScript definition 67 | 68 | ## [0.0.1] - 2018-11-30 69 | ### Added 70 | - Initial release 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Honeybadger Industries LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Honeybadger React.js Integration 2 | 3 | [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fhoneybadger-io%2Fhoneybadger-react%2Fbadge&style=flat)](https://actions-badge.atrox.dev/honeybadger-io/honeybadger-react/goto) 4 | [![npm version](https://badge.fury.io/js/%40honeybadger-io%2Freact.svg)](https://badge.fury.io/js/%40honeybadger-io%2Freact) 5 | 6 | > **Note:** This repository has been moved to [@honeybadger-io/js](https://github.com/honeybadger-io/honeybadger-js), home to all Honeybadger's JavaScript packages. 7 | 8 | > [React.js integration for Honeybadger.io](https://www.honeybadger.io/for/javascript/?utm_source=github&utm_medium=readme&utm_campaign=react&utm_content=React.js+integration+for+Honeybadger.io) 9 | 10 | ## Documentation and Support 11 | 12 | For comprehensive documentation and support, [check out our documentation site](https://docs.honeybadger.io/lib/javascript/index.html). 13 | 14 | The documentation includes a detailed [React integration guide](https://docs.honeybadger.io/lib/javascript/integration/react.html) 15 | 16 | ## Project Goals 17 | 18 | The goal is to provide an idiomatic, simple integration of Honeybadger's 19 | exception monitoring service with React.js applications. 20 | 21 | ## Project Status 22 | 23 | This version is considered suitable for preview. 24 | 25 | ## Limitations 26 | 27 | Honeybadger-react hooks in to the error handler in React. This means we only 28 | notify Honeybadger of React context for errors that React handles. Some 29 | errors inside React code may propagate to the window onerror handler 30 | instead. 31 | 32 | In those cases, Honeybadger Javascript library's default error notifier 33 | is invoked, which will contain a stack trace but none of the React 34 | variables. 35 | 36 | ## Key Assumptions 37 | 38 | This project is built using create-react-library with rollup and generates 39 | artifacts in commonjs, esm and umd formats. It's possible 40 | your own build environment may be just different enough to require some 41 | adjustments. If you find that our artifacts don't quite meet your needs, 42 | please [file an issue on GitHub](https://github.com/honeybadger-io/honeybadger-react/issues). 43 | 44 | ## Example app 45 | 46 | There's a minimal implementation of a honeybadger-react integration in the ./example 47 | folder. If you want to contribute a patch to honeybadger-react, it can be useful to have 48 | the demo app running. 49 | 50 | To run it, issue these commands from your shell: 51 | 52 | ```bash 53 | cd example 54 | npm install 55 | REACT_APP_HONEYBADGER_API_KEY=b425b636 npm run start 56 | ``` 57 | 58 | This will serve the demo app with hot reload at localhost:3000 59 | 60 | For a detailed explanation on how hot reloading works, check out the [documentation](https://webpack.js.org/concepts/hot-module-replacement/). 61 | 62 | ## Changelog 63 | 64 | See https://github.com/honeybadger-io/honeybadger-react/blob/master/CHANGELOG.md 65 | Changelog is automatically generated with [our release automation process](#release-automation). 66 | 67 | ## Contributing 68 | 69 | 1. Fork it. 70 | 2. Create a topic branch `git checkout -b my_branch` 71 | 3. Commit your changes `git commit -am "Boom"` 72 | 3. Push to your branch `git push origin my_branch` 73 | 4. Send a [pull request](https://github.com/honeybadger-io/honeybadger-react/pulls) 74 | 75 | ## Development 76 | 77 | ``` bash 78 | # install dependencies 79 | npm install 80 | 81 | # build for production 82 | npm run build 83 | 84 | # run unit tests 85 | npm test 86 | 87 | # automatically continuously rebuild the dist/ artifacts with hot reload when developing 88 | npm run start 89 | ``` 90 | 91 | ## Releasing 92 | 93 | 1. With a clean working tree, use `npm version [new version]` to bump the version, 94 | commit the changes, tag the release, and push to GitHub. See `npm help version` 95 | for documentation. 96 | 2. To publish the release, use `npm publish`. See `npm help publish` for 97 | documentation. 98 | 99 | ### Release Automation 100 | 101 | We use [Ship.js](https://github.com/algolia/shipjs) to automate releasing. 102 | 103 | Ship.js creates a PR once per week when unreleased changes are present. You can also trigger a release PR by saying "@shipjs prepare" in any issue or pull request comment on GitHub. 104 | 105 | #### Troubleshooting a failed Ship.js release 106 | 107 | If a ship.js release fails, you need to revert the release commit and delete the release branch (e.g `releases/v1.1.0`) 108 | Then, you can debug the issue by simulating the release process locally (`npm run release -- --dry-run --yes --no-browse`). 109 | 110 | ### License 111 | 112 | *honeybadger-react* is MIT licensed. See the [LICENSE](https://raw.github.com/honeybadger-io/honeybadger-react/master/LICENSE) file in this repository for details. 113 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] }; 2 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-ts", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@honeybadger-io/react": "file:..", 7 | "@testing-library/jest-dom": "^5.14.1", 8 | "@testing-library/react": "^11.2.7", 9 | "@testing-library/user-event": "^12.8.3", 10 | "@types/jest": "^26.0.24", 11 | "@types/node": "^12.20.25", 12 | "@types/react": "^17.0.22", 13 | "@types/react-dom": "^17.0.9", 14 | "react": "^17.0.2", 15 | "react-dom": "^17.0.2", 16 | "react-scripts": "4.0.3", 17 | "typescript": "^4.4.3", 18 | "web-vitals": "^1.1.2" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/honeybadger-io/honeybadger-react/c4789ff9153a8510242887a8110a4912d94fcea6/example/public/favicon.ico -------------------------------------------------------------------------------- /example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /example/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/honeybadger-io/honeybadger-react/c4789ff9153a8510242887a8110a4912d94fcea6/example/public/logo192.png -------------------------------------------------------------------------------- /example/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/honeybadger-io/honeybadger-react/c4789ff9153a8510242887a8110a4912d94fcea6/example/public/logo512.png -------------------------------------------------------------------------------- /example/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /example/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /example/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import BuggyComponent from "./BuggyComponent" 3 | import GoodComponent from "./GoodComponent" 4 | 5 | export default class App extends React.Component<{}, {}> { 6 | 7 | render() { 8 | return ( 9 |
10 | 11 | 12 |
13 | ) 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /example/src/BuggyComponent.tsx: -------------------------------------------------------------------------------- 1 | import {Component} from "react" 2 | 3 | export default class BuggyComponent extends Component<{}, { error: boolean }> { 4 | 5 | constructor(props: {}) { 6 | super(props); 7 | this.bug = this.bug.bind(this) 8 | this.state = { 9 | error: false 10 | } 11 | } 12 | 13 | bug() { 14 | this.setState({ 15 | error: true 16 | }) 17 | } 18 | 19 | render () { 20 | if (this.state.error) { 21 | throw Error('oops.') 22 | } 23 | return ( 24 |
25 | 26 |
27 | ) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /example/src/GoodComponent.tsx: -------------------------------------------------------------------------------- 1 | import {Component} from "react"; 2 | 3 | export default class GoodComponent extends Component<{}, {}> { 4 | 5 | render () { 6 | return ( 7 |
8 | 9 |
10 | ) 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /example/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /example/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import reportWebVitals from './reportWebVitals' 6 | import {Honeybadger, HoneybadgerErrorBoundary} from '@honeybadger-io/react' 7 | 8 | Honeybadger.configure({ 9 | apiKey: (process.env.REACT_APP_HONEYBADGER_API_KEY || (prompt('Enter the API key for your Honeybadger project:')) as string), 10 | environment: 'production' 11 | }) 12 | 13 | ReactDOM.render( 14 | 15 | 16 | , 17 | document.getElementById('root') 18 | ) 19 | 20 | // If you want to start measuring performance in your app, pass a function 21 | // to log results (for example: reportWebVitals(console.log)) 22 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 23 | reportWebVitals(); 24 | -------------------------------------------------------------------------------- /example/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /example/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /example/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@honeybadger-io/react", 3 | "version": "2.1.0", 4 | "description": "React.js integration for honeybadger", 5 | "author": "Jason Truesdell (https://github.com/JasonTrue)", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+ssh://git@github.com/honeybadger-io/honeybadger-react.git" 10 | }, 11 | "main": "dist/honeybadger-react.cjs.js", 12 | "types": "./dist/index.d.ts", 13 | "module": "dist/honeybadger-react.esm.js", 14 | "unpkg": "dist/honeybadger-react.js", 15 | "jsnext:main": "dist/honeybadger-react.esm.js", 16 | "engines": { 17 | "node": ">=8", 18 | "npm": ">=5" 19 | }, 20 | "scripts": { 21 | "test": "cross-env CI=1 react-scripts test --env=jsdom", 22 | "test:watch": "react-scripts test --env=jsdom", 23 | "build": "tsc --emitDeclarationOnly --noEmit false && rollup -c", 24 | "start": "rollup -c -w", 25 | "prepare": "npm run build && husky install", 26 | "predeploy": "cd example && npm install && npm run build", 27 | "deploy": "gh-pages -d example/build", 28 | "preversion": "npm test", 29 | "version": "scripts/update-versions.sh", 30 | "postversion": "git push && git push --tags", 31 | "prepublishOnly": "npm run build && npm test", 32 | "release": "shipjs prepare" 33 | }, 34 | "peerDependencies": { 35 | "prop-types": "^15.5.4", 36 | "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", 37 | "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" 38 | }, 39 | "devDependencies": { 40 | "@commitlint/cli": "^17.0.0", 41 | "@commitlint/config-conventional": "^17.0.0", 42 | "@rollup/plugin-commonjs": "^22.0.0", 43 | "@rollup/plugin-typescript": "^8.3.2", 44 | "@types/jest": "^29.0.0", 45 | "@types/react": "^17.0.44", 46 | "@types/react-test-renderer": "^18.0.0", 47 | "@types/sinon": "^10.0.11", 48 | "cross-env": "^7.0.0", 49 | "gh-pages": "^4.0.0", 50 | "husky": "^8.0.0", 51 | "react": "^18.1.0", 52 | "react-scripts": "^5.0.1", 53 | "react-test-renderer": "^18.1.0", 54 | "rollup": "^2.70.2", 55 | "shipjs": "0.24.4", 56 | "sinon": "^14.0.0", 57 | "typescript": "^4.6.3" 58 | }, 59 | "files": [ 60 | "dist" 61 | ], 62 | "dependencies": { 63 | "@honeybadger-io/js": "^4.0.3" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import rollupTs from '@rollup/plugin-typescript' 2 | import typescript from 'typescript' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | import path from 'path' 5 | 6 | import pkg from './package.json' 7 | 8 | export default { 9 | input: 'src/index.tsx', 10 | output: [ 11 | { 12 | file: pkg.main, 13 | exports: 'named', 14 | format: 'cjs', 15 | sourcemap: true, 16 | sourcemapPathTransform: relativePath => { 17 | // will transform e.g. "src/main.js" -> "main.js" 18 | return path.relative('src', relativePath) 19 | }, 20 | }, 21 | { 22 | file: pkg.module, 23 | format: 'es', 24 | exports: 'named', 25 | sourcemap: true, 26 | sourcemapPathTransform: relativePath => { 27 | // will transform e.g. "src/main.js" -> "main.js" 28 | return path.relative('src', relativePath) 29 | } 30 | }, 31 | { 32 | file: pkg.unpkg, 33 | format: 'iife', 34 | exports: 'named', 35 | name: 'HoneybadgerReact', 36 | sourcemap: true, 37 | sourcemapPathTransform: relativePath => { 38 | return path.relative('src', relativePath) 39 | }, 40 | globals: { 41 | 'react': 'React', 42 | 'prop-types': 'PropTypes', 43 | '@honeybadger-io/js': 'Honeybadger', 44 | 'react/jsx-runtime': 'jsxRuntime' 45 | } 46 | } 47 | ], 48 | external: [ 49 | 'react', 50 | 'prop-types', 51 | 'react/jsx-runtime', 52 | '@honeybadger-io/js' 53 | ], 54 | plugins: [ 55 | rollupTs({ 56 | typescript, 57 | declaration: true, 58 | declarationDir: './dist' 59 | }), 60 | commonjs() 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /scripts/update-versions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | VERSION=$(cat package.json | jq -r '.version') 6 | DATE=$(date +"%Y-%m-%d") 7 | 8 | if grep -q "\[$VERSION\]" "CHANGELOG.md"; then 9 | echo "Error: $VERSION already exists in CHANGELOG.md" 10 | exit 1 11 | fi 12 | 13 | # Update CHANGELOG.md 14 | nl=$'\\\n\\\n' 15 | sed -i '' "s/## \[Unreleased\]/## \[Unreleased\]$nl## \[$VERSION\] - $DATE/" CHANGELOG.md 16 | 17 | # Stage for version commit 18 | git add -A CHANGELOG.md 19 | -------------------------------------------------------------------------------- /ship.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | shouldPrepare: ({ releaseType, commitNumbersPerType }) => { 3 | const { fix = 0 } = commitNumbersPerType; 4 | if (releaseType === 'patch' && fix === 0) { 5 | return false; 6 | } 7 | return true; 8 | }, 9 | publishCommand: ({ defaultCommand, tag }) => 10 | `${defaultCommand} --access public --tag ${tag}`, 11 | }; 12 | -------------------------------------------------------------------------------- /src/DefaultErrorComponent.tsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from "react" 2 | import PropTypes from "prop-types"; 3 | 4 | export interface DefaultErrorComponentProps { 5 | error: Error | null 6 | info: React.ErrorInfo | null 7 | } 8 | 9 | export default class DefaultErrorComponent extends Component { 10 | 11 | static propTypes = { 12 | error: PropTypes.object, 13 | info: PropTypes.object 14 | } 15 | 16 | render() { 17 | return ( 18 |
19 |
20 | An Error Occurred 21 |
22 |
23 | {JSON.stringify(this.props.error, null, 2)} 24 |
25 |
26 | {this.props.info ? JSON.stringify(this.props.info, null, 2) : ''} 27 |
28 |
29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/HoneybadgerErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import React, {Component, ReactNode} from 'react' 2 | import HoneybadgerClient from '@honeybadger-io/js/dist/browser/types/core/client' 3 | import DefaultErrorComponent, {DefaultErrorComponentProps} from "./DefaultErrorComponent" 4 | import PropTypes from "prop-types"; 5 | 6 | interface HoneybadgerErrorBoundaryProps { 7 | honeybadger: HoneybadgerClient 8 | ErrorComponent?: ReactNode 9 | } 10 | 11 | interface HoneybadgerErrorBoundaryState extends DefaultErrorComponentProps { 12 | errorOccurred: boolean 13 | } 14 | 15 | export default class HoneybadgerErrorBoundary extends Component { 16 | 17 | static propTypes = { 18 | honeybadger: PropTypes.object.isRequired, 19 | children: PropTypes.element, 20 | ErrorComponent: PropTypes.oneOfType([PropTypes.element, PropTypes.func]) 21 | } 22 | 23 | constructor(props: HoneybadgerErrorBoundaryProps) { 24 | super(props) 25 | this.state = { 26 | error: null, 27 | info: null, 28 | errorOccurred: false 29 | } 30 | } 31 | 32 | public static getDerivedStateFromError(error: Error): HoneybadgerErrorBoundaryState { 33 | return {error: error, errorOccurred: true, info: null} 34 | } 35 | 36 | componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { 37 | this.setState({errorOccurred: true, error: error, info: errorInfo}) 38 | this.props.honeybadger.notify(error, {context: errorInfo as any}) 39 | } 40 | 41 | private getErrorComponent(): ReactNode { 42 | return this.props.ErrorComponent 43 | ? React.createElement(this.props.ErrorComponent as any, this.state) 44 | : 45 | } 46 | 47 | render() { 48 | return ( 49 | <> 50 | {this.state.errorOccurred 51 | ? this.getErrorComponent() 52 | : this.props.children 53 | } 54 | 55 | ) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/index.test.tsx: -------------------------------------------------------------------------------- 1 | import React, {ReactNode} from 'react' 2 | import TestRenderer from 'react-test-renderer' 3 | import {Honeybadger, HoneybadgerErrorBoundary} from './' 4 | import sinon, {SinonSpy} from 'sinon' 5 | 6 | describe('HoneybadgerReact', () => { 7 | let config = {apiKey: 'FFAACCCC00'} 8 | let honeybadger = Honeybadger.configure(config) 9 | 10 | class Clean extends React.Component { 11 | render() { 12 | return

Welcome! This works

13 | } 14 | } 15 | 16 | class Broken extends React.Component { 17 | render(): ReactNode { 18 | throw Error('Busted, sorry') 19 | } 20 | } 21 | 22 | let requests, xhr 23 | 24 | const sandbox = sinon.createSandbox() 25 | beforeEach(function () { 26 | // Stub HTTP requests. 27 | requests = [] 28 | xhr = sandbox.useFakeXMLHttpRequest() 29 | 30 | xhr.onCreate = function (xhr) { 31 | return requests.push(xhr) 32 | } 33 | }) 34 | 35 | afterEach(function () { 36 | sandbox.restore() 37 | }) 38 | 39 | function afterNotify (done: () => void, run: () => void) { 40 | setTimeout(function () { 41 | run() 42 | done() 43 | }, 50) 44 | } 45 | 46 | it('should render the default component when there are no errors', () => { 47 | const testRenderer = TestRenderer.create() 48 | const testInstance = testRenderer.root 49 | expect(testInstance.findByType(Clean)).toBeDefined() 50 | }) 51 | 52 | it("should invoke Honeybadger's notify when a component errors", (done) => { 53 | sandbox.spy(honeybadger, 'notify') 54 | TestRenderer.create() 55 | afterNotify(done, function () { 56 | sinon.assert.calledOnce(honeybadger.notify as SinonSpy) 57 | }) 58 | }) 59 | 60 | describe('when no custom error component is available', () => { 61 | it('should render a default error message when a component errors', () => { 62 | const testRenderer = TestRenderer.create() 63 | const testInstance = testRenderer.root 64 | expect(testInstance.findByProps({className: 'error'})).toBeDefined() 65 | }) 66 | }) 67 | 68 | describe('when a custom error component is available', () => { 69 | it('should render a custom error message when a component errors', (done) => { 70 | sandbox.spy(honeybadger, 'notify') 71 | 72 | const MyError = jest.fn(() => 'custom error view') 73 | TestRenderer.create() 74 | expect(MyError).toBeCalledWith({ 75 | error: expect.any(Error), 76 | info: { componentStack: expect.any(String) }, 77 | errorOccurred: expect.any(Boolean) 78 | }, {}) 79 | // Still want to ensure notify is only called once. The MyError component will be created twice by React. 80 | afterNotify(done, function () { 81 | sinon.assert.calledOnce(honeybadger.notify as SinonSpy) 82 | }) 83 | }) 84 | }) 85 | }) 86 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import Honeybadger from "@honeybadger-io/js" 2 | import HoneybadgerErrorBoundary from "./HoneybadgerErrorBoundary" 3 | 4 | export { 5 | Honeybadger, 6 | HoneybadgerErrorBoundary 7 | } 8 | 9 | 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react-jsx", 4 | "target": "es2018", 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "declaration": true, 8 | "sourceMap": true, 9 | "inlineSources": true, 10 | "outDir": "./dist", 11 | "allowSyntheticDefaultImports": true, 12 | "esModuleInterop": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "strict": true, 15 | "skipLibCheck": true, 16 | "lib": [ 17 | "dom", 18 | "dom.iterable", 19 | "esnext" 20 | ], 21 | "allowJs": true, 22 | "noFallthroughCasesInSwitch": true, 23 | "resolveJsonModule": true, 24 | "isolatedModules": true, 25 | "noEmit": true 26 | }, 27 | "exclude": [ 28 | "node_modules" 29 | ], 30 | "include": [ 31 | "./src/*.tsx" 32 | ] 33 | } 34 | --------------------------------------------------------------------------------